pytest-httpchain-jsonref 0.2.1__tar.gz → 0.3.0__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.1 → pytest_httpchain_jsonref-0.3.0}/PKG-INFO +2 -1
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/pyproject.toml +2 -2
- pytest_httpchain_jsonref-0.3.0/src/pytest_httpchain_jsonref/exceptions.py +5 -0
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/src/pytest_httpchain_jsonref/loader.py +6 -3
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/src/pytest_httpchain_jsonref/plumbing/path.py +5 -0
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/src/pytest_httpchain_jsonref/plumbing/reference.py +18 -8
- pytest_httpchain_jsonref-0.2.1/src/pytest_httpchain_jsonref/exceptions.py +0 -2
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/README.md +0 -0
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/src/pytest_httpchain_jsonref/__init__.py +0 -0
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/src/pytest_httpchain_jsonref/plumbing/__init__.py +0 -0
- {pytest_httpchain_jsonref-0.2.1 → pytest_httpchain_jsonref-0.3.0}/src/pytest_httpchain_jsonref/plumbing/circular.py +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pytest-httpchain-jsonref
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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.
|
|
3
|
+
version = "0.3.0"
|
|
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
|
|
@@ -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."""
|
|
@@ -54,8 +57,7 @@ class ReferenceResolver:
|
|
|
54
57
|
ReferenceResolverError: If the file cannot be loaded or references cannot be resolved
|
|
55
58
|
"""
|
|
56
59
|
try:
|
|
57
|
-
|
|
58
|
-
data = json.load(f)
|
|
60
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
59
61
|
|
|
60
62
|
# If root_path wasn't provided, find a suitable one by going up the directory tree
|
|
61
63
|
# up to max_parent_traversal_depth levels
|
|
@@ -79,7 +81,7 @@ class ReferenceResolver:
|
|
|
79
81
|
root_data: Any,
|
|
80
82
|
) -> Any:
|
|
81
83
|
match data:
|
|
82
|
-
case dict() if
|
|
84
|
+
case dict() if self._get_ref_key(data):
|
|
83
85
|
return self._resolve_single_ref(data, current_path, root_data)
|
|
84
86
|
case dict():
|
|
85
87
|
return {key: self._resolve_refs(value, current_path, root_data) for key, value in data.items()}
|
|
@@ -88,17 +90,26 @@ class ReferenceResolver:
|
|
|
88
90
|
case _:
|
|
89
91
|
return data
|
|
90
92
|
|
|
93
|
+
def _get_ref_key(self, data: dict[str, Any]) -> str | None:
|
|
94
|
+
"""Get the reference key ($include or $ref) if present in data."""
|
|
95
|
+
for key in REF_KEYS:
|
|
96
|
+
if key in data:
|
|
97
|
+
return key
|
|
98
|
+
return None
|
|
99
|
+
|
|
91
100
|
def _resolve_single_ref(
|
|
92
101
|
self,
|
|
93
102
|
data: dict[str, Any],
|
|
94
103
|
current_path: Path,
|
|
95
104
|
root_data: Any,
|
|
96
105
|
) -> Any:
|
|
97
|
-
|
|
106
|
+
ref_key = self._get_ref_key(data)
|
|
107
|
+
assert ref_key is not None
|
|
108
|
+
ref_value = data[ref_key]
|
|
98
109
|
match = REF_PATTERN.match(ref_value)
|
|
99
110
|
|
|
100
111
|
if not match:
|
|
101
|
-
raise ReferenceResolverError(f"Invalid
|
|
112
|
+
raise ReferenceResolverError(f"Invalid {ref_key} format: {ref_value}")
|
|
102
113
|
|
|
103
114
|
file_path = match.group("file")
|
|
104
115
|
pointer = match.group("pointer") or ""
|
|
@@ -174,7 +185,7 @@ class ReferenceResolver:
|
|
|
174
185
|
current_path: Path,
|
|
175
186
|
root_data: Any,
|
|
176
187
|
) -> Any:
|
|
177
|
-
siblings = {k: v for k, v in ref_dict.items() if k
|
|
188
|
+
siblings = {k: v for k, v in ref_dict.items() if k not in REF_KEYS}
|
|
178
189
|
|
|
179
190
|
if not siblings:
|
|
180
191
|
return referenced_data
|
|
@@ -192,8 +203,7 @@ class ReferenceResolver:
|
|
|
192
203
|
|
|
193
204
|
def _load_json_file(self, path: Path) -> dict[str, Any]:
|
|
194
205
|
"""Load JSON file content."""
|
|
195
|
-
|
|
196
|
-
return json.load(f)
|
|
206
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
197
207
|
|
|
198
208
|
def _create_child_resolver(self) -> Self:
|
|
199
209
|
"""Create a child resolver with inherited state."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|