pytest-httpchain-jsonref 0.2.1__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.
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pytest-httpchain-jsonref
3
- Version: 0.2.1
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.1"
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"]
@@ -0,0 +1,5 @@
1
+ from pytest_httpchain_core import HttpChainError
2
+
3
+
4
+ class ReferenceResolverError(HttpChainError):
5
+ """Exception for reference resolution errors."""
@@ -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 $ref paths
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."""
@@ -79,7 +82,7 @@ class ReferenceResolver:
79
82
  root_data: Any,
80
83
  ) -> Any:
81
84
  match data:
82
- case dict() if "$ref" in data:
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
- ref_value = data["$ref"]
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 $ref format: {ref_value}")
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 ""
@@ -174,7 +186,7 @@ class ReferenceResolver:
174
186
  current_path: Path,
175
187
  root_data: Any,
176
188
  ) -> Any:
177
- siblings = {k: v for k, v in ref_dict.items() if k != "$ref"}
189
+ siblings = {k: v for k, v in ref_dict.items() if k not in REF_KEYS}
178
190
 
179
191
  if not siblings:
180
192
  return referenced_data
@@ -1,2 +0,0 @@
1
- class ReferenceResolverError(Exception):
2
- """Base exception for reference resolution errors."""