pytest-jsonschema-snapshot 0.2.4__py3-none-any.whl → 0.2.5__py3-none-any.whl

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.
@@ -8,5 +8,5 @@ pytest-typed-schema-shot
8
8
 
9
9
  from .core import SchemaShot
10
10
 
11
- __version__ = "0.2.4"
11
+ __version__ = "0.2.5"
12
12
  __all__ = ["SchemaShot"]
@@ -13,10 +13,17 @@ if TYPE_CHECKING:
13
13
  from jsonschema_diff import JsonSchemaDiff
14
14
 
15
15
  import pytest
16
+ from genschema import Converter, PseudoArrayHandler
17
+ from genschema.comparators import (
18
+ DeleteElement,
19
+ FormatComparator,
20
+ RequiredComparator,
21
+ SchemaVersionComparator,
22
+ )
16
23
  from jsonschema import FormatChecker, ValidationError, validate
17
24
 
18
25
  from .stats import GLOBAL_STATS
19
- from .tools import JsonToSchemaConverter, NameMaker
26
+ from .tools import NameMaker
20
27
 
21
28
 
22
29
  class SchemaShot:
@@ -54,6 +61,17 @@ class SchemaShot:
54
61
  self.snapshot_dir: Path = root_dir / snapshot_dir_name
55
62
  self.used_schemas: Set[str] = set()
56
63
 
64
+ self.conv = Converter(
65
+ pseudo_handler=PseudoArrayHandler(),
66
+ base_of="anyOf",
67
+ )
68
+ self.conv.register(FormatComparator())
69
+ self.conv.register(RequiredComparator())
70
+ # self.conv.register(EmptyComparator())
71
+ self.conv.register(SchemaVersionComparator())
72
+ self.conv.register(DeleteElement())
73
+ self.conv.register(DeleteElement("isPseudoArray"))
74
+
57
75
  self.logger = logging.getLogger(__name__)
58
76
  # добавляем вывод в stderr
59
77
  handler = logging.StreamHandler()
@@ -144,11 +162,9 @@ class SchemaShot:
144
162
 
145
163
  real_name = self._process_name(name)
146
164
 
147
- builder = JsonToSchemaConverter(
148
- format_mode=self.format_mode # type: ignore[arg-type]
149
- ) # , examples=self.examples_limit)
150
- builder.add_object(data)
151
- current_schema = builder.to_schema()
165
+ self.conv.clear_data()
166
+ self.conv.add_schema(data)
167
+ current_schema = self.conv.run()
152
168
 
153
169
  real_name, status = self._base_match(data, current_schema, real_name)
154
170
 
@@ -234,12 +250,11 @@ class SchemaShot:
234
250
  schema_updated = False
235
251
 
236
252
  def merge_schemas(old: dict, new: dict) -> dict:
237
- builder = JsonToSchemaConverter(
238
- format_mode=self.format_mode # type: ignore[arg-type]
239
- ) # , examples=self.examples_limit)
240
- builder.add_schema(old)
241
- builder.add_schema(new)
242
- return builder.to_schema()
253
+ self.conv.clear_data()
254
+ self.conv.add_schema(old)
255
+ self.conv.add_schema(new)
256
+ result = self.conv.run()
257
+ return result
243
258
 
244
259
  if existing_schema != current_schema: # есть отличия
245
260
  if (self.update_mode or self.reset_mode) and self.update_actions.get("update"):
@@ -1,4 +1,3 @@
1
- from .genson_addon import JsonToSchemaConverter
2
1
  from .name_maker import NameMaker
3
2
 
4
- __all__ = ["JsonToSchemaConverter", "NameMaker"]
3
+ __all__ = ["NameMaker"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-jsonschema-snapshot
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Pytest plugin for automatic JSON Schema generation and validation from examples
5
5
  Project-URL: Homepage, https://miskler.github.io/pytest-jsonschema-snapshot/basic/quick_start.html
6
6
  Project-URL: Repository, https://github.com/Miskler/pytest-jsonschema-snapshot
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
23
  Classifier: Topic :: Utilities
24
24
  Requires-Python: >=3.10
25
- Requires-Dist: genson
25
+ Requires-Dist: genschema
26
26
  Requires-Dist: jsonschema
27
27
  Requires-Dist: jsonschema-diff
28
28
  Requires-Dist: pathvalidate
@@ -0,0 +1,12 @@
1
+ pytest_jsonschema_snapshot/__init__.py,sha256=o7yiVh_nbK13tO4NpaE25XW3J9V2YD7fkicByUdS-iM,385
2
+ pytest_jsonschema_snapshot/core.py,sha256=fM8r8wD83litob2sheHb1jh8x8Nam4g8AnjGzk8TZ2o,12347
3
+ pytest_jsonschema_snapshot/plugin.py,sha256=nvAfxtLSX_B5FzaWu7DfsiWRxFjxDvnQNNOhkRrRnbw,8677
4
+ pytest_jsonschema_snapshot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ pytest_jsonschema_snapshot/stats.py,sha256=BfhfMoSkRq6Q8BwhVfrpcFl5TP9OzpgpLwnKf1Kslkw,9593
6
+ pytest_jsonschema_snapshot/tools/__init__.py,sha256=WeD2EVrQpKIoFW1s43QAqsJmartqZ3Irwckt814P1bs,59
7
+ pytest_jsonschema_snapshot/tools/name_maker.py,sha256=tqss8NCGSo2aQX_-RkCJzy3NJx_TDA-xrn8qsblecf0,5799
8
+ pytest_jsonschema_snapshot-0.2.5.dist-info/METADATA,sha256=QR0VvtvzFi6fDHtGUv_sNgSSUBSTw7ZJET_Ajd09IEE,7798
9
+ pytest_jsonschema_snapshot-0.2.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
10
+ pytest_jsonschema_snapshot-0.2.5.dist-info/entry_points.txt,sha256=eJ1x4TMmhcc8YtM7IoCsUJO4-rLeTrGy8tPgkrojjKs,58
11
+ pytest_jsonschema_snapshot-0.2.5.dist-info/licenses/LICENSE,sha256=1HRFdSzlJ4BtHv6U7tZun3iCArjbCnm5NUowE9hZpNs,1071
12
+ pytest_jsonschema_snapshot-0.2.5.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,3 +0,0 @@
1
- from .to_schema_converter import JsonToSchemaConverter
2
-
3
- __all__ = ["JsonToSchemaConverter"]
@@ -1,52 +0,0 @@
1
- import re
2
- from typing import Optional
3
-
4
-
5
- class FormatDetector:
6
- """Class for detecting string formats"""
7
-
8
- # Regular expressions for various formats
9
- EMAIL_PATTERN = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
10
- UUID_PATTERN = re.compile(
11
- r"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
12
- re.I,
13
- )
14
- DATE_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}$")
15
- DATETIME_PATTERN = re.compile(
16
- r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$"
17
- )
18
- URI_PATTERN = re.compile(r"^https?://[^\s/$.?#].[^\s]*$", re.I)
19
- IPV4_PATTERN = re.compile(
20
- r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}"
21
- r"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
22
- )
23
-
24
- @classmethod
25
- def detect_format(cls, value: str) -> Optional[str]:
26
- """
27
- Detects the format of a string.
28
-
29
- Args:
30
- value: The string to analyze
31
-
32
- Returns:
33
- The name of the format or None if the format is not defined
34
- """
35
- if not isinstance(value, str) or not value:
36
- return None
37
-
38
- # Check formats from more specific to less specific
39
- if cls.EMAIL_PATTERN.match(value):
40
- return "email"
41
- elif cls.UUID_PATTERN.match(value):
42
- return "uuid"
43
- elif cls.DATETIME_PATTERN.match(value):
44
- return "date-time"
45
- elif cls.DATE_PATTERN.match(value):
46
- return "date"
47
- elif cls.URI_PATTERN.match(value):
48
- return "uri"
49
- elif cls.IPV4_PATTERN.match(value):
50
- return "ipv4"
51
-
52
- return None
@@ -1,100 +0,0 @@
1
- """Json → Schema with optional format handling.
2
-
3
- `format_mode` options
4
- ---------------------
5
- * ``"on"`` – detect formats and let validators assert them (default).
6
- * ``"off"`` – ignore formats entirely.
7
- * ``"safe"`` – keep the annotations but embed a ``$vocabulary`` block that
8
- **disables** the draft‑2020‑12 *format‑assertion* vocabulary.
9
- This makes every ``format`` purely informational, regardless
10
- of validator settings.
11
- """
12
-
13
- from typing import Any, Dict, Literal
14
-
15
- from genson import SchemaBuilder # type: ignore[import-untyped]
16
-
17
- from .format_detector import FormatDetector
18
-
19
- _FormatMode = Literal["on", "off", "safe"]
20
-
21
-
22
- class JsonToSchemaConverter(SchemaBuilder):
23
- """A thin wrapper around :class:`genson.SchemaBuilder`."""
24
-
25
- # ------------------------------------------------------------------
26
- # Construction
27
- # ------------------------------------------------------------------
28
- def __init__(
29
- self,
30
- schema_uri: str = "https://json-schema.org/draft/2020-12/schema",
31
- *,
32
- format_mode: _FormatMode = "on",
33
- ):
34
- super().__init__(schema_uri) if schema_uri else super().__init__()
35
- if format_mode not in {"on", "off", "safe"}:
36
- raise ValueError("format_mode must be 'on', 'off', or 'safe'.")
37
- self._format_mode: _FormatMode = format_mode
38
- self._format_cache: Dict[str, set[str]] = {}
39
-
40
- # ------------------------------------------------------------------
41
- # Public API (overrides)
42
- # ------------------------------------------------------------------
43
- def add_object(self, obj: Any, path: str = "root") -> None:
44
- super().add_object(obj)
45
- if self._format_mode != "off":
46
- self._collect_formats(obj, path)
47
-
48
- def to_schema(self) -> Dict[str, Any]:
49
- schema = dict(super().to_schema()) # shallow‑copy
50
-
51
- if self._format_mode != "off":
52
- self._inject_formats(schema, "root")
53
-
54
- if self._format_mode == "safe":
55
- schema.setdefault(
56
- "$vocabulary",
57
- {
58
- "https://json-schema.org/draft/2020-12/vocab/core": True,
59
- "https://json-schema.org/draft/2020-12/vocab/applicator": True,
60
- "https://json-schema.org/draft/2020-12/vocab/format-annotation": True,
61
- "https://json-schema.org/draft/2020-12/vocab/format-assertion": False,
62
- },
63
- )
64
-
65
- return schema
66
-
67
- # ------------------------------------------------------------------
68
- # Internals
69
- # ------------------------------------------------------------------
70
- def _collect_formats(self, obj: Any, path: str) -> None:
71
- if isinstance(obj, str):
72
- fmt = FormatDetector.detect_format(obj)
73
- if fmt:
74
- self._format_cache.setdefault(path, set()).add(fmt)
75
- elif isinstance(obj, dict):
76
- for k, v in obj.items():
77
- self._collect_formats(v, f"{path}.{k}")
78
- elif isinstance(obj, (list, tuple)):
79
- for i, item in enumerate(obj):
80
- self._collect_formats(item, f"{path}[{i}]")
81
-
82
- def _inject_formats(self, schema: Dict[str, Any], path: str) -> None:
83
- t = schema.get("type")
84
- if t == "string":
85
- fmts = self._format_cache.get(path)
86
- if fmts and len(fmts) == 1:
87
- schema["format"] = next(iter(fmts))
88
- elif t == "object" and "properties" in schema:
89
- for name, subschema in schema["properties"].items():
90
- self._inject_formats(subschema, f"{path}.{name}")
91
- elif t == "array" and "items" in schema:
92
- items_schema = schema["items"]
93
- if isinstance(items_schema, dict):
94
- self._inject_formats(items_schema, f"{path}[0]")
95
- else:
96
- for idx, subschema in enumerate(items_schema):
97
- self._inject_formats(subschema, f"{path}[{idx}]")
98
- elif "anyOf" in schema:
99
- for subschema in schema["anyOf"]:
100
- self._inject_formats(subschema, path)
@@ -1,15 +0,0 @@
1
- pytest_jsonschema_snapshot/__init__.py,sha256=rUBgKVh7nCLEwfO0XZS-TewnM5eTXpBEI7NaC5p4qLg,385
2
- pytest_jsonschema_snapshot/core.py,sha256=CoL_W-u6o3N7XDwv-MbePJiZaGX0LtGS6BLbj9MHROU,11995
3
- pytest_jsonschema_snapshot/plugin.py,sha256=nvAfxtLSX_B5FzaWu7DfsiWRxFjxDvnQNNOhkRrRnbw,8677
4
- pytest_jsonschema_snapshot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- pytest_jsonschema_snapshot/stats.py,sha256=BfhfMoSkRq6Q8BwhVfrpcFl5TP9OzpgpLwnKf1Kslkw,9593
6
- pytest_jsonschema_snapshot/tools/__init__.py,sha256=WMS6PdgMABBfTRhPGuoUOXB-R2PcqcadwH8pG1C6MFU,132
7
- pytest_jsonschema_snapshot/tools/name_maker.py,sha256=tqss8NCGSo2aQX_-RkCJzy3NJx_TDA-xrn8qsblecf0,5799
8
- pytest_jsonschema_snapshot/tools/genson_addon/__init__.py,sha256=nANkqHTaWTZPwBDztsnQvObHUZLSeHenJS--oWfep8c,92
9
- pytest_jsonschema_snapshot/tools/genson_addon/format_detector.py,sha256=Wc5pB_xstyr4OtjwJ2qqmV62xET63cN7Nb0gxkrYyW0,1636
10
- pytest_jsonschema_snapshot/tools/genson_addon/to_schema_converter.py,sha256=UdQIkZhMrTJNHwI1B1dv3aEwx41B1B_lLyr4KWiUpNY,4168
11
- pytest_jsonschema_snapshot-0.2.4.dist-info/METADATA,sha256=iiGeO3N83BH1griTvads-RUawss1rw_9NIlFEdK6mkM,7795
12
- pytest_jsonschema_snapshot-0.2.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- pytest_jsonschema_snapshot-0.2.4.dist-info/entry_points.txt,sha256=eJ1x4TMmhcc8YtM7IoCsUJO4-rLeTrGy8tPgkrojjKs,58
14
- pytest_jsonschema_snapshot-0.2.4.dist-info/licenses/LICENSE,sha256=1HRFdSzlJ4BtHv6U7tZun3iCArjbCnm5NUowE9hZpNs,1071
15
- pytest_jsonschema_snapshot-0.2.4.dist-info/RECORD,,