Skip to content

Commit 8f404a3

Browse files
committed
resolver / refactor (squash me)
1 parent c90005f commit 8f404a3

File tree

6 files changed

+101
-37
lines changed

6 files changed

+101
-37
lines changed
Binary file not shown.
Binary file not shown.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import urllib.parse
2+
from typing import List, Union
3+
4+
5+
class Pointer:
6+
""" https://tools.ietf.org/html/rfc6901 """
7+
8+
def __init__(self, pointer: str) -> None:
9+
if pointer is None or pointer != "" and not pointer.startswith("/"):
10+
raise ValueError(f'Invalid pointer value {pointer}, it must match: *( "/" reference-token )')
11+
12+
self._pointer = pointer
13+
14+
@property
15+
def value(self) -> str:
16+
return self._pointer
17+
18+
@property
19+
def parent(self) -> Union["Pointer", None]:
20+
tokens = self.tokens(False)
21+
22+
if len(tokens) > 1:
23+
tokens.pop()
24+
return Pointer("/".join(tokens))
25+
else:
26+
assert tokens[-1] == ""
27+
return None
28+
29+
def tokens(self, unescape: bool = True) -> List[str]:
30+
tokens = []
31+
32+
if unescape:
33+
for token in self._pointer.split("/"):
34+
tokens.append(self._unescape(token))
35+
else:
36+
tokens = self._pointer.split("/")
37+
38+
return tokens
39+
40+
@property
41+
def unescapated_value(self) -> str:
42+
return self._unescape(self._pointer)
43+
44+
def _unescape(self, data: str) -> str:
45+
data = urllib.parse.unquote(data)
46+
data = data.replace("~1", "/")
47+
data = data.replace("~0", "~")
48+
return data
Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,51 @@
1-
import urllib
2-
from typing import Union
1+
import urllib.parse
2+
3+
from .pointer import Pointer
34

45

56
class Reference:
7+
""" https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03 """
8+
69
def __init__(self, reference: str):
710
self._ref = reference
11+
self._parsed_ref = urllib.parse.urlparse(reference)
812

913
@property
10-
def remote_relative_path(self) -> Union[str, None]:
11-
if self.is_remote_ref():
12-
return self._ref.split("#")[0]
13-
return None
14+
def path(self) -> str:
15+
return urllib.parse.urldefrag(self._parsed_ref.geturl()).url
1416

1517
@property
16-
def path_parent(self) -> str:
17-
path = self.path
18-
parts = path.split("/")
19-
parts.pop()
20-
return "/".join(parts)
18+
def pointer(self) -> Pointer:
19+
frag = self._parsed_ref.fragment
20+
if self.is_url() and frag != "" and not frag.startswith("/"):
21+
frag = f"/{frag}"
2122

22-
@property
23-
def path(self) -> str:
24-
d = self._ref.split("#")[-1]
25-
d = urllib.parse.unquote(d)
26-
d = d.replace("~1", "/")
27-
return d
23+
return Pointer(frag)
24+
25+
def is_relative(self) -> bool:
26+
""" return True if reference path is a relative path """
27+
return not self.is_absolute()
28+
29+
def is_absolute(self) -> bool:
30+
""" return True is reference path is an absolute path """
31+
return self._parsed_ref.netloc != ""
2832

2933
@property
3034
def value(self) -> str:
3135
return self._ref
3236

33-
def is_relative_reference(self) -> bool:
34-
return self.is_remote_ref() and not self.is_url_reference()
37+
def is_url(self) -> bool:
38+
""" return True if the reference path is pointing to an external url location """
39+
return self.is_remote() and self._parsed_ref.netloc != ""
3540

36-
def is_url_reference(self) -> bool:
37-
return self.is_remote_ref() and (self._ref.startswith("//", 0) or self._ref.startswith("http", 0))
41+
def is_remote(self) -> bool:
42+
""" return True if the reference pointer is pointing to a remote document """
43+
return not self.is_local()
3844

39-
def is_remote_ref(self) -> bool:
40-
return not self.is_local_ref()
45+
def is_local(self) -> bool:
46+
""" return True if the reference pointer is pointing to the current document """
47+
return self._parsed_ref.path == ""
4148

42-
def is_local_ref(self) -> bool:
43-
return self._ref.startswith("#", 0)
49+
def is_full_document(self) -> bool:
50+
""" return True if the reference pointer is pointing to the whole document content """
51+
return self.pointer.parent is None

openapi_python_client/resolver/resolved_schema.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ def _process_remote_paths(self) -> None:
3434
for owner, ref_key, ref_val in self._lookup_schema_references_in(self._root, "paths"):
3535
ref = Reference(ref_val)
3636

37-
if ref.is_local_ref():
37+
if ref.is_local():
3838
continue
3939

40-
remote_path = ref.remote_relative_path
40+
remote_path = ref.pointer.value
4141
path = ref.path
4242

4343
if remote_path not in self._refs:
@@ -62,12 +62,12 @@ def _process_remote_components(
6262
for parent, ref_key, ref_val in self._lookup_schema_references(target):
6363
ref = Reference(ref_val)
6464

65-
if ref.is_local_ref():
65+
if ref.is_local():
6666
# print('Found local reference >> {0}'.format(ref.value))
6767
if depth > 0:
6868
self._transform_to_local_components(owner, ref)
6969
else:
70-
remote_path = ref.remote_relative_path
70+
remote_path = ref.pointer.value
7171
if remote_path not in self._refs:
7272
self._errors.append("Failed to resolve remote reference > {0}".format(remote_path))
7373
else:
@@ -80,20 +80,23 @@ def _transform_to_local_components(self, owner: SchemaData, ref: Reference) -> N
8080

8181
# print('Processing remote component > {0}'.format(ref.value))
8282
remote_component = self._lookup_dict(owner, ref.path)
83-
root_components_dir = self._lookup_dict(self._resolved_remotes_components, ref.path_parent)
84-
component_name = ref.path.split("/")[-1]
83+
pointer_parent = ref.pointer.parent
84+
85+
if pointer_parent is not None:
86+
root_components_dir = self._lookup_dict(self._resolved_remotes_components, pointer_parent.value)
87+
component_name = ref.path.split("/")[-1]
8588

8689
if component_name == "SorTransparentContainer" or component_name == "sorTransparentContainer":
8790
print(ref.value)
8891

8992
if remote_component is None:
9093
print("Weirdy relookup of >> {0}".format(ref.value))
91-
assert ref.is_local_ref() and self._lookup_dict(self._resolved_remotes_components, ref.path)
94+
assert ref.is_local() and self._lookup_dict(self._resolved_remotes_components, ref.path)
9295
return
9396

9497
if "$ref" in remote_component:
9598
subref = Reference(remote_component["$ref"])
96-
if not subref.is_local_ref():
99+
if not subref.is_local():
97100
print("Lookup remote ref >>> {0}".format(subref.value))
98101
return self._process_remote_components(remote_component)
99102

@@ -118,7 +121,10 @@ def _transform_to_local_components(self, owner: SchemaData, ref: Reference) -> N
118121

119122
def _ensure_components_dir_exists(self, ref: Reference) -> None:
120123
cursor = self._resolved_remotes_components
121-
for key in ref.path_parent.split("/"):
124+
pointer_dir = ref.pointer.parent
125+
assert pointer_dir is not None
126+
127+
for key in pointer_dir.value.split("/"): # noqa
122128
if key == "":
123129
continue
124130

openapi_python_client/resolver/schema_resolver.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ def __init__(self, url_or_path: Union[str, Path]):
3030
self._root_url = url_or_path
3131
try:
3232
self._root_url_scheme = urllib.parse.urlparse(url_or_path).scheme
33-
except Exception:
33+
if self._root_url_scheme not in ["http", "https"]:
34+
raise ValueError(f"Unsupported URL scheme '{self._root_url_scheme}', expecting http or https")
35+
except (TypeError, AttributeError):
3436
raise urllib.error.URLError(f"Coult not parse URL > {url_or_path}")
3537

3638
def _isapath(self, url_or_path: Union[str, Path]) -> bool:
@@ -56,15 +58,15 @@ def _resolve_schema_references(
5658
) -> None:
5759

5860
for ref in self._lookup_schema_references(root):
59-
if ref.is_local_ref():
61+
if ref.is_local():
6062
continue
6163

6264
try:
6365
path = ref.value.split("#")[0]
6466
if path in external_schemas:
6567
continue
6668

67-
if ref.is_url_reference():
69+
if ref.is_url():
6870
external_schemas[path] = self._fetch_url_reference(path)
6971
else:
7072
external_schemas[path] = self._fetch_remote_reference(path)

0 commit comments

Comments
 (0)