pytest-httpchain-jsonref 0.2.0__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/PKG-INFO +2 -1
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/pyproject.toml +2 -2
- pytest_httpchain_jsonref-0.2.4/src/pytest_httpchain_jsonref/exceptions.py +5 -0
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/src/pytest_httpchain_jsonref/loader.py +6 -3
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/src/pytest_httpchain_jsonref/plumbing/circular.py +1 -1
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/src/pytest_httpchain_jsonref/plumbing/path.py +5 -0
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/src/pytest_httpchain_jsonref/plumbing/reference.py +17 -4
- pytest_httpchain_jsonref-0.2.0/src/pytest_httpchain_jsonref/exceptions.py +0 -2
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/README.md +0 -0
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/src/pytest_httpchain_jsonref/__init__.py +0 -0
- {pytest_httpchain_jsonref-0.2.0 → pytest_httpchain_jsonref-0.2.4}/src/pytest_httpchain_jsonref/plumbing/__init__.py +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pytest-httpchain-jsonref
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: JSON reference ($ref) support for pytest-httpchain
|
|
5
5
|
Author: Alexander Eresov
|
|
6
6
|
Author-email: Alexander Eresov <aeresov@gmail.com>
|
|
7
7
|
Requires-Dist: deepmerge>=2.0
|
|
8
|
+
Requires-Dist: pytest-httpchain-core
|
|
8
9
|
Requires-Python: >=3.13
|
|
9
10
|
Description-Content-Type: text/markdown
|
|
10
11
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pytest-httpchain-jsonref"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.4"
|
|
4
4
|
description = "JSON reference ($ref) support for pytest-httpchain"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
7
7
|
authors = [{ name = "Alexander Eresov", email = "aeresov@gmail.com" }]
|
|
8
|
-
dependencies = ["deepmerge>=2.0"]
|
|
8
|
+
dependencies = ["deepmerge>=2.0", "pytest-httpchain-core"]
|
|
9
9
|
|
|
10
10
|
[build-system]
|
|
11
11
|
requires = ["uv_build>=0.7.21,<0.8.0"]
|
|
@@ -7,15 +7,18 @@ from pytest_httpchain_jsonref.plumbing.reference import ReferenceResolver
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def load_json(path: Path, max_parent_traversal_depth: int = 3, root_path: Path | None = None) -> dict[str, Any]:
|
|
10
|
-
"""Load JSON from file and resolve all $ref statements with circular reference protection.
|
|
10
|
+
"""Load JSON from file and resolve all $include/$merge/$ref statements with circular reference protection.
|
|
11
|
+
|
|
12
|
+
All three directives ($include, $merge, $ref) work identically. $include and $merge are preferred
|
|
13
|
+
as they avoid conflicts with VS Code's JSON Schema validation (which treats $ref specially).
|
|
11
14
|
|
|
12
15
|
Args:
|
|
13
16
|
path: Path to the JSON file to load
|
|
14
|
-
max_parent_traversal_depth: Maximum number of parent directory traversals allowed in
|
|
17
|
+
max_parent_traversal_depth: Maximum number of parent directory traversals allowed in reference paths
|
|
15
18
|
root_path: Optional root directory for resolving references (e.g., pytest's rootdir)
|
|
16
19
|
|
|
17
20
|
Returns:
|
|
18
|
-
Dictionary with all $ref statements resolved
|
|
21
|
+
Dictionary with all $include/$ref statements resolved
|
|
19
22
|
|
|
20
23
|
Raises:
|
|
21
24
|
ReferenceResolverError: If the file cannot be loaded or parsed, if merge conflicts occur, or if circular references are detected
|
|
@@ -65,7 +65,7 @@ class CircularDependencyTracker:
|
|
|
65
65
|
Returns:
|
|
66
66
|
A new tracker with copies of the current reference sets
|
|
67
67
|
"""
|
|
68
|
-
child =
|
|
68
|
+
child = self.__class__()
|
|
69
69
|
child.external_refs = self.external_refs.copy()
|
|
70
70
|
child.internal_refs = self.internal_refs.copy()
|
|
71
71
|
return child
|
|
@@ -46,6 +46,11 @@ class PathValidator:
|
|
|
46
46
|
# Try resolving from different base paths in order of preference
|
|
47
47
|
paths_to_try = [base_path]
|
|
48
48
|
|
|
49
|
+
# Add CWD so that paths like "tests/common.json" resolve from where the user runs the tool
|
|
50
|
+
cwd = Path.cwd()
|
|
51
|
+
if cwd.resolve() not in (base_path_resolved, root_path_resolved):
|
|
52
|
+
paths_to_try.append(cwd)
|
|
53
|
+
|
|
49
54
|
# Add root_path if it's different from base_path
|
|
50
55
|
if root_path_resolved != base_path_resolved:
|
|
51
56
|
paths_to_try.append(root_path)
|
|
@@ -14,6 +14,9 @@ from pytest_httpchain_jsonref.plumbing.path import PathValidator
|
|
|
14
14
|
|
|
15
15
|
REF_PATTERN = re.compile(r"^(?P<file>[^#]+)?(?:#(?P<pointer>/.*))?$")
|
|
16
16
|
|
|
17
|
+
# Supported reference keys: $include/$merge (preferred, avoids VS Code conflicts) and $ref (legacy)
|
|
18
|
+
REF_KEYS = ("$include", "$merge", "$ref")
|
|
19
|
+
|
|
17
20
|
|
|
18
21
|
class ReferenceResolver:
|
|
19
22
|
"""Resolves JSON references ($ref) in documents."""
|
|
@@ -79,7 +82,7 @@ class ReferenceResolver:
|
|
|
79
82
|
root_data: Any,
|
|
80
83
|
) -> Any:
|
|
81
84
|
match data:
|
|
82
|
-
case dict() if
|
|
85
|
+
case dict() if self._get_ref_key(data):
|
|
83
86
|
return self._resolve_single_ref(data, current_path, root_data)
|
|
84
87
|
case dict():
|
|
85
88
|
return {key: self._resolve_refs(value, current_path, root_data) for key, value in data.items()}
|
|
@@ -88,17 +91,26 @@ class ReferenceResolver:
|
|
|
88
91
|
case _:
|
|
89
92
|
return data
|
|
90
93
|
|
|
94
|
+
def _get_ref_key(self, data: dict[str, Any]) -> str | None:
|
|
95
|
+
"""Get the reference key ($include or $ref) if present in data."""
|
|
96
|
+
for key in REF_KEYS:
|
|
97
|
+
if key in data:
|
|
98
|
+
return key
|
|
99
|
+
return None
|
|
100
|
+
|
|
91
101
|
def _resolve_single_ref(
|
|
92
102
|
self,
|
|
93
103
|
data: dict[str, Any],
|
|
94
104
|
current_path: Path,
|
|
95
105
|
root_data: Any,
|
|
96
106
|
) -> Any:
|
|
97
|
-
|
|
107
|
+
ref_key = self._get_ref_key(data)
|
|
108
|
+
assert ref_key is not None
|
|
109
|
+
ref_value = data[ref_key]
|
|
98
110
|
match = REF_PATTERN.match(ref_value)
|
|
99
111
|
|
|
100
112
|
if not match:
|
|
101
|
-
raise ReferenceResolverError(f"Invalid
|
|
113
|
+
raise ReferenceResolverError(f"Invalid {ref_key} format: {ref_value}")
|
|
102
114
|
|
|
103
115
|
file_path = match.group("file")
|
|
104
116
|
pointer = match.group("pointer") or ""
|
|
@@ -143,6 +155,7 @@ class ReferenceResolver:
|
|
|
143
155
|
|
|
144
156
|
try:
|
|
145
157
|
referenced_data = self._navigate_pointer(root_data, pointer)
|
|
158
|
+
assert self.base_path is not None
|
|
146
159
|
return self._resolve_refs(referenced_data, self.base_path, root_data)
|
|
147
160
|
finally:
|
|
148
161
|
self.tracker.clear_internal_ref(pointer)
|
|
@@ -173,7 +186,7 @@ class ReferenceResolver:
|
|
|
173
186
|
current_path: Path,
|
|
174
187
|
root_data: Any,
|
|
175
188
|
) -> Any:
|
|
176
|
-
siblings = {k: v for k, v in ref_dict.items() if k
|
|
189
|
+
siblings = {k: v for k, v in ref_dict.items() if k not in REF_KEYS}
|
|
177
190
|
|
|
178
191
|
if not siblings:
|
|
179
192
|
return referenced_data
|
|
File without changes
|
|
File without changes
|
|
File without changes
|