1818 "RewriteOptions" ,
1919 "rewrite_urls_inplace" ,
2020 "iter_url_fields" ,
21+ "iter_ref_fields" ,
2122 "count_references" ,
2223]
2324
2425
25- def count_references (root : Any ) -> Tuple [int , int , int , int ]:
26+ def count_references (root : Any , refs_only : bool = False ) -> Tuple [int , int , int , int ]:
2627 """
2728 Counts the number of references in a given data structure based on their type.
2829
@@ -34,6 +35,8 @@ def count_references(root: Any) -> Tuple[int, int, int, int]:
3435 Args:
3536 root: Any
3637 The root data structure containing references to be analyzed.
38+ refs_only: bool, optional (default=False)
39+ If True, only count references in $ref fields.
3740
3841 Returns:
3942 Tuple[int, int, int, int]: A tuple containing the following counts:
@@ -47,7 +50,8 @@ def count_references(root: Any) -> Tuple[int, int, int, int]:
4750 total_refs_count = 0
4851 local_refs_count = 0
4952
50- for path , _parent , key , value in iter_url_fields (root ):
53+ iterator = iter_ref_fields if refs_only else iter_url_fields
54+ for path , _parent , key , value in iterator (root ):
5155 assert isinstance (key , str )
5256 total_refs_count += 1
5357 if key == "$ref" and is_fragment_only_uri (value ):
@@ -60,14 +64,15 @@ def count_references(root: Any) -> Tuple[int, int, int, int]:
6064 return total_refs_count , local_refs_count , relative_refs_count , absolute_http_refs_count
6165
6266
63- def find_relative_urls (root : Any ) -> List [Tuple [JSONPath , str , str ]]:
67+ def find_relative_urls (root : Any , refs_only : bool = False ) -> List [Tuple [JSONPath , str , str ]]:
6468 """
65- Return a list of (json_path, key, value) for any URL-like field
66- (including $ref) whose value is relative (e.g., 'schemas/a.yaml', '../b', '/c')
69+ Return a list of (json_path, key, value) for any URL-like field (refs_only=False) or $ref field (refs_only=True)
70+ whose value is relative (e.g., 'schemas/a.yaml', '../b', '/c')
6771 and not a pure fragment '#/...' .
6872 """
6973 out : List [Tuple [JSONPath , str , str ]] = []
70- for path , _parent , key , value in iter_url_fields (root ):
74+ iterator = iter_ref_fields if refs_only else iter_url_fields
75+ for path , _parent , key , value in iterator (root ):
7176 assert isinstance (key , str )
7277 if key == "$ref" and is_fragment_only_uri (value ):
7378 continue
@@ -77,13 +82,14 @@ def find_relative_urls(root: Any) -> List[Tuple[JSONPath, str, str]]:
7782 return out
7883
7984
80- def find_absolute_http_urls (root : Any ) -> List [Tuple [JSONPath , str , str ]]:
85+ def find_absolute_http_urls (root : Any , refs_only : bool = False ) -> List [Tuple [JSONPath , str , str ]]:
8186 """
82- Return a list of (json_path, key, value) for any URL-like field
83- (including $ref) whose value is absolute and http
87+ Return a list of (json_path, key, value) for any URL-like field (refs_only=False) or $ref field (refs_only=True)
88+ whose value is absolute and http
8489 """
8590 out : List [Tuple [JSONPath , str , str ]] = []
86- for path , _parent , key , value in iter_url_fields (root ):
91+ iterator = iter_ref_fields if refs_only else iter_url_fields
92+ for path , _parent , key , value in iterator (root ):
8793 assert isinstance (key , str )
8894 if key == "$ref" and is_fragment_only_uri (value ):
8995 continue
@@ -100,24 +106,27 @@ class RewriteOptions:
100106 - original_base_url: If provided together with include_absolute_urls=True, we'll
101107 retarget absolute URLs that begin with original_base_url to base_url.
102108 - include_absolute_urls: If True (and original_base_url given), retarget absolute URLs too.
109+ - refs_only: If True, only rewrite $ref fields.
103110 """
104111
105- __slots__ = ("base_url" , "original_base_url" , "include_absolute_urls" )
112+ __slots__ = ("base_url" , "original_base_url" , "include_absolute_urls" , "refs_only" )
106113
107114 def __init__ (
108115 self ,
109116 base_url : str ,
110117 original_base_url : str | None = None ,
111118 include_absolute_urls : bool = False ,
119+ refs_only : bool = False ,
112120 ) -> None :
113121 self .base_url = base_url
114122 self .original_base_url = original_base_url
115123 self .include_absolute_urls = include_absolute_urls
124+ self .refs_only = refs_only
116125
117126
118127def rewrite_urls_inplace (root : Any , opts : RewriteOptions ) -> int :
119128 """
120- Rewrite $ref and other URL-bearing fields in-place.
129+ Rewrite $ref and other URL-bearing fields (refs_only=False) in-place.
121130 Rules:
122131 - Relative values (except fragment-only) are absolutized against opts.base_url.
123132 - If opts.include_absolute_urls and opts.original_base_url are set,
@@ -126,7 +135,8 @@ def rewrite_urls_inplace(root: Any, opts: RewriteOptions) -> int:
126135 Returns the number of fields changed.
127136 """
128137 changed = 0
129- for _path , parent , key , value in iter_url_fields (root ):
138+ iterator = iter_ref_fields if opts .refs_only else iter_url_fields
139+ for _path , parent , key , value in iterator (root ):
130140 # value is str by iter_url_fields contract
131141 if key == "$ref" and is_fragment_only_uri (value ):
132142 continue # keep pure fragments
@@ -159,6 +169,20 @@ def iter_url_fields(root: Any) -> Iterator[Tuple[JSONPath, MutableMapping[str, A
159169 yield node .path , node .parent , node .segment , node .value
160170
161171
172+ def iter_ref_fields (root : Any ) -> Iterator [Tuple [JSONPath , MutableMapping [str , Any ], str , str ]]:
173+ """
174+ Iterate over all $ref fields
175+ """
176+ for node in traverse (root ):
177+ if (
178+ isinstance (node .parent , MutableMapping )
179+ and isinstance (node .segment , str )
180+ and isinstance (node .value , str )
181+ and node .segment == "$ref"
182+ ):
183+ yield node .path , node .parent , node .segment , node .value
184+
185+
162186# Keys that *by spec* or convention carry URL/URI references (OpenAPI 3.0/3.1).
163187_URL_KEYS_EXPLICIT : frozenset [str ] = frozenset (
164188 {
0 commit comments