pytest-jsonschema-snapshot 0.2.0__tar.gz → 0.2.2__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_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/PKG-INFO +3 -3
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/README.md +2 -2
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/docs/source/basic/quick_start.rst +12 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/__init__.py +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/core.py +54 -13
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/plugin.py +65 -5
- pytest_jsonschema_snapshot-0.2.2/pytest_jsonschema_snapshot/tools/genson_addon/to_schema_converter.py +100 -0
- pytest_jsonschema_snapshot-0.2.2/tests/tools/genson_addon/test_format_safe.py +82 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/TestDataClass.get_data.first.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/TestDataClass.get_data.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/array_formats_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/date_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/datetime_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/email_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/get_data.first.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/get_data.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/ipv4_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/mixed_formats_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/regular_strings_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/some_schema.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/strict_email_validation_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/uri_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/uuid_test.schema.json +1 -1
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/test_multiple_schema_creation.py +4 -2
- pytest_jsonschema_snapshot-0.2.0/pytest_jsonschema_snapshot/tools/genson_addon/to_schema_converter.py +0 -119
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.flake8 +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/ISSUE_TEMPLATE/documentation_issue.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/PULL_REQUEST_TEMPLATE/general.md +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/actions/build-docs/action.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/workflows/release.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/workflows/reusable-test.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/workflows/test.yml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.gitignore +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/LICENSE +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/Makefile +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/assets/logo.png +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/docs/source/_static/logo_day.png +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/docs/source/_static/logo_night.png +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/docs/source/conf.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/docs/source/index.rst +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pyproject.toml +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/py.typed +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/stats.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/tools/__init__.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/tools/genson_addon/__init__.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/tools/genson_addon/format_detector.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/pytest_jsonschema_snapshot/tools/name_maker.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/reinstall_plugin.sh +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/requirements.txt +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/test_stats.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/tools/test_name_maker.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/TestDataClass.get_data.first.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/TestDataClass.get_data.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/array_formats_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/date_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/datetime_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/email_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/get_data.first.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/get_data.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/ipv4_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/mixed_formats_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/multi_schema_one.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/multi_schema_three.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/multi_schema_two.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/regular_strings_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/some_schema.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/strict_email_validation_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/uri_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/__snapshots__/uuid_test.json +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/test_base.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/test_base_name_callable.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/test_format_detection.py +0 -0
- {pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/test_format_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytest-jsonschema-snapshot
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
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
|
|
@@ -117,12 +117,12 @@ pip install pytest-jsonschema-snapshot
|
|
|
117
117
|
)
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
2. On first run, generate schemas with the `--schema-update` flag
|
|
120
|
+
2. On first run, generate schemas with the `--schema-update` or `--schema-reset` (what is the difference? see the documentation) flag
|
|
121
121
|
```bash
|
|
122
122
|
pytest --schema-update --save-original
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
**--save-original**: save the original data on which the validation was performed. Saving occurs when `--schema-update`, if you run the schema update without this attribute, the old original data will be deleted without saving new ones.
|
|
125
|
+
**--save-original**: save the original data on which the validation was performed. Saving occurs when `--schema-update` or `--schema-reset`, if you run the schema update without this attribute, the old original data will be deleted without saving new ones.
|
|
126
126
|
|
|
127
127
|
3. On subsequent runs, tests will validate data against saved schemas
|
|
128
128
|
```bash
|
|
@@ -72,12 +72,12 @@ pip install pytest-jsonschema-snapshot
|
|
|
72
72
|
)
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
2. On first run, generate schemas with the `--schema-update` flag
|
|
75
|
+
2. On first run, generate schemas with the `--schema-update` or `--schema-reset` (what is the difference? see the documentation) flag
|
|
76
76
|
```bash
|
|
77
77
|
pytest --schema-update --save-original
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
**--save-original**: save the original data on which the validation was performed. Saving occurs when `--schema-update`, if you run the schema update without this attribute, the old original data will be deleted without saving new ones.
|
|
80
|
+
**--save-original**: save the original data on which the validation was performed. Saving occurs when `--schema-update` or `--schema-reset`, if you run the schema update without this attribute, the old original data will be deleted without saving new ones.
|
|
81
81
|
|
|
82
82
|
3. On subsequent runs, tests will validate data against saved schemas
|
|
83
83
|
```bash
|
|
@@ -19,6 +19,7 @@ Choose the option that best suits your project:
|
|
|
19
19
|
[pytest]
|
|
20
20
|
jsss_dir = __snapshots__ # where to put the schemas
|
|
21
21
|
jsss_callable_regex = {class_method=.} # rule for the callable part
|
|
22
|
+
jsss_format_mode = on
|
|
22
23
|
|
|
23
24
|
.. code-block:: python
|
|
24
25
|
|
|
@@ -26,6 +27,7 @@ Choose the option that best suits your project:
|
|
|
26
27
|
[tool.pytest.ini_options]
|
|
27
28
|
jsss_dir = "__snapshots__"
|
|
28
29
|
jsss_callable_regex = "{class_method=.}"
|
|
30
|
+
jsss_format_mode = "on"
|
|
29
31
|
|
|
30
32
|
.. code-block:: ini
|
|
31
33
|
|
|
@@ -33,9 +35,11 @@ Choose the option that best suits your project:
|
|
|
33
35
|
[tool:pytest]
|
|
34
36
|
jsss_dir = __snapshots__
|
|
35
37
|
jsss_callable_regex = {class_method=.}
|
|
38
|
+
jsss_format_mode = on
|
|
36
39
|
|
|
37
40
|
* **jsss_dir**: the name of the folder where the library will save the schemas/originals *(always in the same directory as the test that called it)*.
|
|
38
41
|
* **jsss_callable_regex**: the rule for interpreting the callable name.
|
|
42
|
+
* **jsss_format_mode**: "on" (annotate and validate), "safe" (annotate), "off" (disable).
|
|
39
43
|
|
|
40
44
|
Next, you can use the fixture in your tests:
|
|
41
45
|
--------------------------------------------
|
|
@@ -84,8 +88,15 @@ Run
|
|
|
84
88
|
|
|
85
89
|
* **--jsss-debug**: by default, the library hides its part of the call stack when raising. This is convenient for debugging your tests, but if the problem is in PJSSS itself - you can pass.
|
|
86
90
|
* **--save-original**: save the original data on which the validation was performed. Saving occurs when `--schema-update`, if you run the schema update without this attribute, the old original data will be deleted without saving new ones.
|
|
87
|
-
* **--schema-update**: update the schema - without this attribute, the library will only report changes in the schemas and fail the tests, with it, the tests (and the originals, if requested) will be updated.
|
|
88
91
|
|
|
92
|
+
Modes of operation
|
|
93
|
+
* **--schema-update**: when updating **merges** two schemas. Works by principle: "what is valid for the old one, is valid for the new one, and vice versa"
|
|
94
|
+
* **--schema-reset**: when updating **replaces** the old schema with the new one.
|
|
95
|
+
|
|
96
|
+
Disabling update mechanisms
|
|
97
|
+
* **--without-delete**: disables deletion of old schemas
|
|
98
|
+
* **--without-update**: disables updating of existing schemas
|
|
99
|
+
* **--without-add**: disables adding new schemas
|
|
89
100
|
|
|
90
101
|
.. code-block:: console
|
|
91
102
|
|
|
@@ -25,7 +25,10 @@ class SchemaShot:
|
|
|
25
25
|
root_dir: Path,
|
|
26
26
|
differ: "JsonSchemaDiff",
|
|
27
27
|
callable_regex: str = "{class_method=.}",
|
|
28
|
+
format_mode: str = "on",
|
|
28
29
|
update_mode: bool = False,
|
|
30
|
+
reset_mode: bool = False,
|
|
31
|
+
update_actions: dict[str, bool] = {},
|
|
29
32
|
save_original: bool = False,
|
|
30
33
|
debug_mode: bool = False,
|
|
31
34
|
snapshot_dir_name: str = "__snapshots__",
|
|
@@ -41,7 +44,11 @@ class SchemaShot:
|
|
|
41
44
|
self.root_dir: Path = root_dir
|
|
42
45
|
self.differ: "JsonSchemaDiff" = differ
|
|
43
46
|
self.callable_regex: str = callable_regex
|
|
47
|
+
self.format_mode: str = format_mode
|
|
48
|
+
# self.examples_limit: int = examples_limit
|
|
44
49
|
self.update_mode: bool = update_mode
|
|
50
|
+
self.reset_mode: bool = reset_mode
|
|
51
|
+
self.update_actions: dict[str, bool] = update_actions
|
|
45
52
|
self.save_original: bool = save_original
|
|
46
53
|
self.debug_mode: bool = debug_mode
|
|
47
54
|
self.snapshot_dir: Path = root_dir / snapshot_dir_name
|
|
@@ -104,7 +111,9 @@ class SchemaShot:
|
|
|
104
111
|
available_to_create = not json_path.exists() or status is None
|
|
105
112
|
available_to_update = status is True
|
|
106
113
|
|
|
107
|
-
if available_to_create or
|
|
114
|
+
if (available_to_create and self.update_actions.get("add")) or (
|
|
115
|
+
available_to_update and self.update_actions.get("update")
|
|
116
|
+
):
|
|
108
117
|
with open(json_path, "w", encoding="utf-8") as f:
|
|
109
118
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
110
119
|
|
|
@@ -114,7 +123,7 @@ class SchemaShot:
|
|
|
114
123
|
GLOBAL_STATS.add_updated(json_name)
|
|
115
124
|
else:
|
|
116
125
|
raise ValueError(f"Unexpected status: {status}")
|
|
117
|
-
elif json_path.exists():
|
|
126
|
+
elif json_path.exists() and self.update_actions.get("delete"):
|
|
118
127
|
# удаляем
|
|
119
128
|
json_path.unlink()
|
|
120
129
|
GLOBAL_STATS.add_deleted(json_name)
|
|
@@ -135,13 +144,15 @@ class SchemaShot:
|
|
|
135
144
|
|
|
136
145
|
real_name = self._process_name(name)
|
|
137
146
|
|
|
138
|
-
builder = JsonToSchemaConverter(
|
|
147
|
+
builder = JsonToSchemaConverter(
|
|
148
|
+
format_mode=self.format_mode # type: ignore[arg-type]
|
|
149
|
+
) # , examples=self.examples_limit)
|
|
139
150
|
builder.add_object(data)
|
|
140
151
|
current_schema = builder.to_schema()
|
|
141
152
|
|
|
142
153
|
real_name, status = self._base_match(data, current_schema, real_name)
|
|
143
154
|
|
|
144
|
-
if self.update_mode:
|
|
155
|
+
if self.update_mode or self.reset_mode:
|
|
145
156
|
self._save_process_original(real_name=real_name, status=status, data=data)
|
|
146
157
|
|
|
147
158
|
return status
|
|
@@ -199,11 +210,15 @@ class SchemaShot:
|
|
|
199
210
|
|
|
200
211
|
# --- когда схемы ещё нет ---
|
|
201
212
|
if not schema_exists_before:
|
|
202
|
-
if not self.update_mode:
|
|
213
|
+
if not self.update_mode and not self.reset_mode:
|
|
203
214
|
raise pytest.fail.Exception(
|
|
204
215
|
f"Schema `{name}` not found."
|
|
205
216
|
"Run the test with the --schema-update option to create it."
|
|
206
217
|
)
|
|
218
|
+
elif not self.update_actions.get("add"):
|
|
219
|
+
raise pytest.fail.Exception(
|
|
220
|
+
f"Schema `{name}` not found and adding new schemas is disabled."
|
|
221
|
+
)
|
|
207
222
|
|
|
208
223
|
with open(schema_path, "w", encoding="utf-8") as f:
|
|
209
224
|
json.dump(current_schema, f, indent=2, ensure_ascii=False)
|
|
@@ -219,17 +234,43 @@ class SchemaShot:
|
|
|
219
234
|
schema_updated = False
|
|
220
235
|
|
|
221
236
|
if existing_schema != current_schema: # есть отличия
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if self.update_mode:
|
|
225
|
-
GLOBAL_STATS.add_updated(schema_path.name, differences)
|
|
226
|
-
|
|
237
|
+
if (self.update_mode or self.reset_mode) and self.update_actions.get("update"):
|
|
227
238
|
# обновляем файл
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
239
|
+
if self.reset_mode and not self.update_mode:
|
|
240
|
+
differences = self.differ.compare(
|
|
241
|
+
dict(existing_schema), current_schema
|
|
242
|
+
).render()
|
|
243
|
+
GLOBAL_STATS.add_updated(schema_path.name, differences)
|
|
244
|
+
|
|
245
|
+
with open(schema_path, "w", encoding="utf-8") as f:
|
|
246
|
+
json.dump(current_schema, f, indent=2, ensure_ascii=False)
|
|
247
|
+
self.logger.warning(f"Schema `{name}` updated (reset).\n\n{differences}")
|
|
248
|
+
elif self.update_mode and not self.reset_mode:
|
|
249
|
+
builder = JsonToSchemaConverter(
|
|
250
|
+
format_mode=self.format_mode # type: ignore[arg-type]
|
|
251
|
+
) # , examples=self.examples_limit)
|
|
252
|
+
builder.add_schema(existing_schema)
|
|
253
|
+
builder.add_schema(current_schema)
|
|
254
|
+
merged_schema = builder.to_schema()
|
|
255
|
+
|
|
256
|
+
differences = self.differ.compare(
|
|
257
|
+
dict(existing_schema), merged_schema
|
|
258
|
+
).render()
|
|
259
|
+
GLOBAL_STATS.add_updated(schema_path.name, differences)
|
|
260
|
+
|
|
261
|
+
with open(schema_path, "w", encoding="utf-8") as f:
|
|
262
|
+
json.dump(merged_schema, f, indent=2, ensure_ascii=False)
|
|
263
|
+
|
|
264
|
+
self.logger.warning(f"Schema `{name}` updated (update).\n\n{differences}")
|
|
265
|
+
else: # both update_mode and reset_mode are True
|
|
266
|
+
raise ValueError(
|
|
267
|
+
"Both update_mode and reset_mode cannot be True at the same time."
|
|
268
|
+
)
|
|
231
269
|
schema_updated = True
|
|
232
270
|
elif data is not None:
|
|
271
|
+
differences = self.differ.compare(
|
|
272
|
+
dict(existing_schema), current_schema
|
|
273
|
+
).render()
|
|
233
274
|
GLOBAL_STATS.add_uncommitted(schema_path.name, differences)
|
|
234
275
|
|
|
235
276
|
# только валидируем по старой схеме
|
|
@@ -22,7 +22,16 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
|
|
22
22
|
parser.addoption(
|
|
23
23
|
"--schema-update",
|
|
24
24
|
action="store_true",
|
|
25
|
-
help=
|
|
25
|
+
help=(
|
|
26
|
+
"Augmenting mode for updating schemas. "
|
|
27
|
+
"If something is valid for the old schema, then it is valid "
|
|
28
|
+
"for the new one (and vice versa)."
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
parser.addoption(
|
|
32
|
+
"--schema-reset",
|
|
33
|
+
action="store_true",
|
|
34
|
+
help="New schema does not take into account the old one during update.",
|
|
26
35
|
)
|
|
27
36
|
parser.addoption(
|
|
28
37
|
"--save-original",
|
|
@@ -35,6 +44,22 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
|
|
35
44
|
help="Show internal exception stack (stops hiding them)",
|
|
36
45
|
)
|
|
37
46
|
|
|
47
|
+
parser.addoption(
|
|
48
|
+
"--without-delete",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help="Disable deleting unused schemas",
|
|
51
|
+
)
|
|
52
|
+
parser.addoption(
|
|
53
|
+
"--without-update",
|
|
54
|
+
action="store_true",
|
|
55
|
+
help="Disable updating schemas",
|
|
56
|
+
)
|
|
57
|
+
parser.addoption(
|
|
58
|
+
"--without-add",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Disable adding new schemas",
|
|
61
|
+
)
|
|
62
|
+
|
|
38
63
|
parser.addini(
|
|
39
64
|
"jsss_dir",
|
|
40
65
|
default="__snapshots__",
|
|
@@ -45,6 +70,11 @@ def pytest_addoption(parser: pytest.Parser) -> None:
|
|
|
45
70
|
default="{class_method=.}",
|
|
46
71
|
help="Regex for saving callable part of path",
|
|
47
72
|
)
|
|
73
|
+
parser.addini(
|
|
74
|
+
"jsss_format_mode",
|
|
75
|
+
default="on",
|
|
76
|
+
help="Format mode: 'on' (annotate and validate), 'safe' (annotate), 'off' (disable)",
|
|
77
|
+
)
|
|
48
78
|
|
|
49
79
|
|
|
50
80
|
@pytest.fixture(scope="function")
|
|
@@ -56,13 +86,26 @@ def schemashot(request: pytest.FixtureRequest) -> Generator[SchemaShot, None, No
|
|
|
56
86
|
# Получаем путь к тестовому файлу
|
|
57
87
|
test_path = Path(request.node.path if hasattr(request.node, "path") else request.node.fspath)
|
|
58
88
|
root_dir = test_path.parent
|
|
89
|
+
|
|
59
90
|
update_mode = bool(request.config.getoption("--schema-update"))
|
|
91
|
+
reset_mode = bool(request.config.getoption("--schema-reset"))
|
|
92
|
+
if update_mode and reset_mode:
|
|
93
|
+
raise ValueError("Options --schema-update and --schema-reset are mutually exclusive.")
|
|
94
|
+
|
|
60
95
|
save_original = bool(request.config.getoption("--save-original"))
|
|
61
96
|
debug_mode = bool(request.config.getoption("--jsss-debug"))
|
|
62
97
|
|
|
98
|
+
actions = {
|
|
99
|
+
"delete": not request.config.getoption("--without-delete"),
|
|
100
|
+
"update": not request.config.getoption("--without-update"),
|
|
101
|
+
"add": not request.config.getoption("--without-add"),
|
|
102
|
+
}
|
|
103
|
+
|
|
63
104
|
# Получаем настраиваемую директорию для схем
|
|
64
105
|
schema_dir_name = str(request.config.getini("jsss_dir"))
|
|
65
106
|
callable_regex = str(request.config.getini("jsss_callable_regex"))
|
|
107
|
+
format_mode = str(request.config.getini("jsss_format_mode")).lower()
|
|
108
|
+
# examples_limit = int(request.config.getini("jsss_examples_limit"))
|
|
66
109
|
|
|
67
110
|
differ = JsonSchemaDiff(
|
|
68
111
|
ConfigMaker.make(),
|
|
@@ -77,7 +120,11 @@ def schemashot(request: pytest.FixtureRequest) -> Generator[SchemaShot, None, No
|
|
|
77
120
|
root_dir,
|
|
78
121
|
differ,
|
|
79
122
|
callable_regex,
|
|
123
|
+
format_mode,
|
|
124
|
+
# examples_limit,
|
|
80
125
|
update_mode,
|
|
126
|
+
reset_mode,
|
|
127
|
+
actions,
|
|
81
128
|
save_original,
|
|
82
129
|
debug_mode,
|
|
83
130
|
schema_dir_name,
|
|
@@ -108,11 +155,21 @@ def pytest_terminal_summary(terminalreporter: pytest.TerminalReporter, exitstatu
|
|
|
108
155
|
"""
|
|
109
156
|
# Выполняем cleanup перед показом summary
|
|
110
157
|
if _schema_managers:
|
|
111
|
-
|
|
158
|
+
|
|
159
|
+
def get_opt(opt: str) -> bool:
|
|
160
|
+
return bool(terminalreporter.config.getoption(opt))
|
|
161
|
+
|
|
162
|
+
update_mode = get_opt("--schema-update")
|
|
163
|
+
|
|
164
|
+
actions = {
|
|
165
|
+
"delete": not get_opt("--without-delete"),
|
|
166
|
+
"update": not get_opt("--without-update"),
|
|
167
|
+
"add": not get_opt("--without-add"),
|
|
168
|
+
}
|
|
112
169
|
|
|
113
170
|
# Вызываем метод очистки неиспользованных схем для каждого экземпляра
|
|
114
171
|
for _root_dir, manager in _schema_managers.items():
|
|
115
|
-
cleanup_unused_schemas(manager, update_mode, GLOBAL_STATS)
|
|
172
|
+
cleanup_unused_schemas(manager, update_mode, actions, GLOBAL_STATS)
|
|
116
173
|
|
|
117
174
|
# Используем новую функцию для вывода статистики
|
|
118
175
|
update_mode = bool(terminalreporter.config.getoption("--schema-update"))
|
|
@@ -120,7 +177,10 @@ def pytest_terminal_summary(terminalreporter: pytest.TerminalReporter, exitstatu
|
|
|
120
177
|
|
|
121
178
|
|
|
122
179
|
def cleanup_unused_schemas(
|
|
123
|
-
manager: SchemaShot,
|
|
180
|
+
manager: SchemaShot,
|
|
181
|
+
update_mode: bool,
|
|
182
|
+
actions: dict[str, bool],
|
|
183
|
+
stats: Optional[SchemaStats] = None,
|
|
124
184
|
) -> None:
|
|
125
185
|
"""
|
|
126
186
|
Deletes unused schemas in update mode and collects statistics.
|
|
@@ -140,7 +200,7 @@ def cleanup_unused_schemas(
|
|
|
140
200
|
|
|
141
201
|
for schema_file in all_schemas:
|
|
142
202
|
if schema_file.name not in manager.used_schemas:
|
|
143
|
-
if update_mode:
|
|
203
|
+
if update_mode and actions.get("delete"):
|
|
144
204
|
try:
|
|
145
205
|
# Удаляем саму схему
|
|
146
206
|
schema_file.unlink()
|
|
@@ -0,0 +1,100 @@
|
|
|
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)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Pytest suite for ``JsonToSchemaConverter`` using generic ``jsonschema.validate``.
|
|
2
|
+
|
|
3
|
+
We use two JSON samples (``SOURCE`` and ``SOURCE_INVALID``) to check that
|
|
4
|
+
*format* handling behaves as intended under three modes.
|
|
5
|
+
|
|
6
|
+
Expectation matrix
|
|
7
|
+
================== ================= =========================
|
|
8
|
+
format_mode call to validate Expected result
|
|
9
|
+
================== ================= =========================
|
|
10
|
+
"on" validate + FC ValidationError raised
|
|
11
|
+
"safe" validate passes OK
|
|
12
|
+
"off" validate passes OK
|
|
13
|
+
================== ================= =========================
|
|
14
|
+
Where **FC** = ``FormatChecker``.
|
|
15
|
+
|
|
16
|
+
Why this matters?
|
|
17
|
+
-----------------
|
|
18
|
+
*User requested*: «должно проходить на обычном validate». Therefore we
|
|
19
|
+
avoid draft‑specific classes like ``Draft202012Validator`` and rely on
|
|
20
|
+
``jsonschema.validate`` selecting a validator based on the ``$schema``
|
|
21
|
+
keyword embedded by *genson* (currently `http://json-schema.org/schema#`).
|
|
22
|
+
|
|
23
|
+
In python‑jsonschema, *format* assertions only run when a
|
|
24
|
+
``FormatChecker`` is provided, regardless of the *format-assertion*
|
|
25
|
+
vocabulary. Hence, supplying ``FormatChecker`` only in **on** mode gives
|
|
26
|
+
us the desired behaviour.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import jsonschema
|
|
32
|
+
import pytest
|
|
33
|
+
|
|
34
|
+
from pytest_jsonschema_snapshot.tools import JsonToSchemaConverter
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# Fixtures
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
SOURCE = {
|
|
40
|
+
"email": "alice@example.com",
|
|
41
|
+
"website": "https://example.com",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
SOURCE_INVALID = {
|
|
45
|
+
"email": "not-an-email",
|
|
46
|
+
"website": "notaurl",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
PARAMS = [
|
|
50
|
+
("on", False, True), # должен упасть (ValidationError)
|
|
51
|
+
("safe", True, True), # должен пройти
|
|
52
|
+
("off", True, False), # должен пройти
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Tests
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
@pytest.mark.parametrize("mode, should_pass, have_formats", PARAMS)
|
|
60
|
+
def test_format_handling(mode: str, should_pass: bool, have_formats: bool) -> None:
|
|
61
|
+
"""Validate *SOURCE_INVALID* against schema generated from *SOURCE*."""
|
|
62
|
+
|
|
63
|
+
# 1. Generate schema
|
|
64
|
+
conv = JsonToSchemaConverter(format_mode=mode)
|
|
65
|
+
conv.add_object(SOURCE)
|
|
66
|
+
schema = conv.to_schema()
|
|
67
|
+
|
|
68
|
+
# 2. Prepare kwargs for jsonschema.validate
|
|
69
|
+
kwargs: dict[str, object] = {}
|
|
70
|
+
if mode == "on":
|
|
71
|
+
kwargs["format_checker"] = jsonschema.FormatChecker()
|
|
72
|
+
|
|
73
|
+
# 3. Validate and assert outcome
|
|
74
|
+
if should_pass:
|
|
75
|
+
jsonschema.validate(SOURCE_INVALID, schema, **kwargs)
|
|
76
|
+
else:
|
|
77
|
+
with pytest.raises(jsonschema.ValidationError):
|
|
78
|
+
jsonschema.validate(SOURCE_INVALID, schema, **kwargs)
|
|
79
|
+
|
|
80
|
+
# 4. Ensure presence/absence of "format" matches the mode
|
|
81
|
+
has_format = schema["properties"]["email"].get("format") is not None
|
|
82
|
+
assert has_format == have_formats
|
|
@@ -5,8 +5,10 @@ import pytest
|
|
|
5
5
|
|
|
6
6
|
def test_multiple_schema_creation(schemashot, pytestconfig):
|
|
7
7
|
"""Ensure multiple schemas can be created sequentially."""
|
|
8
|
-
if not pytestconfig.getoption("--schema-
|
|
9
|
-
|
|
8
|
+
if not pytestconfig.getoption("--schema-reset") and not pytestconfig.getoption(
|
|
9
|
+
"--schema-update"
|
|
10
|
+
):
|
|
11
|
+
pytest.skip("requires --schema-reset or --schema-update")
|
|
10
12
|
|
|
11
13
|
snapshot_dir = Path(__file__).parent / "__snapshots__"
|
|
12
14
|
names = ["multi_schema_one", "multi_schema_two", "multi_schema_three"]
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Module for advanced JSON Schema generation with format detection support.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from typing import Any, Dict, Optional
|
|
6
|
-
|
|
7
|
-
from genson import SchemaBuilder # type: ignore[import-untyped]
|
|
8
|
-
|
|
9
|
-
from .format_detector import FormatDetector
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class FormatAwareString:
|
|
13
|
-
"""Strategy for strings with format detection"""
|
|
14
|
-
|
|
15
|
-
def __init__(self) -> None:
|
|
16
|
-
self.formats: set = set()
|
|
17
|
-
|
|
18
|
-
def match_schema(self, obj: Any) -> bool:
|
|
19
|
-
"""Checks if the object matches this strategy"""
|
|
20
|
-
return isinstance(obj, str)
|
|
21
|
-
|
|
22
|
-
def match_object(self, obj: Any) -> bool:
|
|
23
|
-
"""Checks if the object matches this strategy"""
|
|
24
|
-
return isinstance(obj, str)
|
|
25
|
-
|
|
26
|
-
def add_object(self, obj: Any) -> None:
|
|
27
|
-
"""Adds an object for analysis"""
|
|
28
|
-
if isinstance(obj, str):
|
|
29
|
-
detected_format = FormatDetector.detect_format(obj)
|
|
30
|
-
if detected_format:
|
|
31
|
-
self.formats.add(detected_format)
|
|
32
|
-
|
|
33
|
-
def to_schema(self) -> Dict[str, Any]:
|
|
34
|
-
"""Generates a schema for the string"""
|
|
35
|
-
schema = {"type": "string"}
|
|
36
|
-
|
|
37
|
-
# If all strings have the same format, add it to the schema
|
|
38
|
-
if len(self.formats) == 1:
|
|
39
|
-
schema["format"] = list(self.formats)[0]
|
|
40
|
-
|
|
41
|
-
return schema
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class JsonToSchemaConverter(SchemaBuilder):
|
|
45
|
-
"""Extended SchemaBuilder with format detection support"""
|
|
46
|
-
|
|
47
|
-
def __init__(self, schema_uri: Optional[str] = None):
|
|
48
|
-
if schema_uri:
|
|
49
|
-
super().__init__(schema_uri)
|
|
50
|
-
else:
|
|
51
|
-
super().__init__()
|
|
52
|
-
self._format_cache: Dict[str, set] = {}
|
|
53
|
-
|
|
54
|
-
def add_object(self, obj: Any, path: str = "root") -> None:
|
|
55
|
-
"""
|
|
56
|
-
Adds an object to the builder with format detection.
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
obj: Object to add
|
|
60
|
-
path: Path to the object (for internal use)
|
|
61
|
-
"""
|
|
62
|
-
# Call the parent method first
|
|
63
|
-
super().add_object(obj)
|
|
64
|
-
|
|
65
|
-
# Then process the formats
|
|
66
|
-
self._process_formats(obj, path)
|
|
67
|
-
|
|
68
|
-
def _process_formats(self, obj: Any, path: str) -> None:
|
|
69
|
-
"""Recursively processes the object for format detection"""
|
|
70
|
-
if isinstance(obj, str):
|
|
71
|
-
# Detect the format of the string
|
|
72
|
-
detected_format = FormatDetector.detect_format(obj)
|
|
73
|
-
if detected_format:
|
|
74
|
-
if path not in self._format_cache:
|
|
75
|
-
self._format_cache[path] = set()
|
|
76
|
-
self._format_cache[path].add(detected_format)
|
|
77
|
-
elif isinstance(obj, dict):
|
|
78
|
-
# Recursively process the dictionary
|
|
79
|
-
for key, value in obj.items():
|
|
80
|
-
self._process_formats(value, f"{path}.{key}")
|
|
81
|
-
elif isinstance(obj, (list, tuple)):
|
|
82
|
-
# Recursively process the list
|
|
83
|
-
for i, item in enumerate(obj):
|
|
84
|
-
self._process_formats(item, f"{path}[{i}]")
|
|
85
|
-
|
|
86
|
-
def to_schema(self) -> Dict:
|
|
87
|
-
"""Generates the schema with format detection"""
|
|
88
|
-
# Get the base schema
|
|
89
|
-
schema = dict(super().to_schema())
|
|
90
|
-
|
|
91
|
-
# Add the formats
|
|
92
|
-
self._add_formats_to_schema(schema, "root")
|
|
93
|
-
|
|
94
|
-
return schema
|
|
95
|
-
|
|
96
|
-
def _add_formats_to_schema(self, schema: Dict[str, Any], path: str) -> None:
|
|
97
|
-
"""Recursively adds formats to the schema"""
|
|
98
|
-
if schema.get("type") == "string":
|
|
99
|
-
# If there is only one format for this path
|
|
100
|
-
if path in self._format_cache and len(self._format_cache[path]) == 1:
|
|
101
|
-
schema["format"] = list(self._format_cache[path])[0]
|
|
102
|
-
|
|
103
|
-
elif schema.get("type") == "object" and "properties" in schema:
|
|
104
|
-
# Recursively process the object properties
|
|
105
|
-
for prop_name, prop_schema in schema["properties"].items():
|
|
106
|
-
self._add_formats_to_schema(prop_schema, f"{path}.{prop_name}")
|
|
107
|
-
|
|
108
|
-
elif schema.get("type") == "array" and "items" in schema:
|
|
109
|
-
# Process the array items
|
|
110
|
-
if isinstance(schema["items"], dict):
|
|
111
|
-
self._add_formats_to_schema(schema["items"], f"{path}[0]")
|
|
112
|
-
elif isinstance(schema["items"], list):
|
|
113
|
-
for i, item_schema in enumerate(schema["items"]):
|
|
114
|
-
self._add_formats_to_schema(item_schema, f"{path}[{i}]")
|
|
115
|
-
|
|
116
|
-
elif "anyOf" in schema:
|
|
117
|
-
# Process the anyOf schemas
|
|
118
|
-
for i, sub_schema in enumerate(schema["anyOf"]):
|
|
119
|
-
self._add_formats_to_schema(sub_schema, path)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/workflows/release.yml
RENAMED
|
File without changes
|
|
File without changes
|
{pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/.github/workflows/test.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/tools/test_name_maker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytest_jsonschema_snapshot-0.2.0 → pytest_jsonschema_snapshot-0.2.2}/tests/usage/test_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|