pytest-jsonschema-snapshot 0.2.2__py3-none-any.whl → 0.2.4__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.2"
11
+ __version__ = "0.2.4"
12
12
  __all__ = ["SchemaShot"]
@@ -233,6 +233,14 @@ class SchemaShot:
233
233
  # --- схема уже была: сравнение и валидация --------------------------------
234
234
  schema_updated = False
235
235
 
236
+ 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()
243
+
236
244
  if existing_schema != current_schema: # есть отличия
237
245
  if (self.update_mode or self.reset_mode) and self.update_actions.get("update"):
238
246
  # обновляем файл
@@ -246,12 +254,7 @@ class SchemaShot:
246
254
  json.dump(current_schema, f, indent=2, ensure_ascii=False)
247
255
  self.logger.warning(f"Schema `{name}` updated (reset).\n\n{differences}")
248
256
  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()
257
+ merged_schema = merge_schemas(existing_schema, current_schema)
255
258
 
256
259
  differences = self.differ.compare(
257
260
  dict(existing_schema), merged_schema
@@ -268,9 +271,9 @@ class SchemaShot:
268
271
  )
269
272
  schema_updated = True
270
273
  elif data is not None:
271
- differences = self.differ.compare(
272
- dict(existing_schema), current_schema
273
- ).render()
274
+ merged_schema = merge_schemas(existing_schema, current_schema)
275
+
276
+ differences = self.differ.compare(dict(existing_schema), merged_schema).render()
274
277
  GLOBAL_STATS.add_uncommitted(schema_path.name, differences)
275
278
 
276
279
  # только валидируем по старой схеме
@@ -293,9 +296,9 @@ class SchemaShot:
293
296
  format_checker=FormatChecker(),
294
297
  )
295
298
  except ValidationError as e:
296
- differences = self.differ.compare(
297
- dict(existing_schema), current_schema
298
- ).render()
299
+ merged_schema = merge_schemas(existing_schema, current_schema)
300
+
301
+ differences = self.differ.compare(dict(existing_schema), merged_schema).render()
299
302
  pytest.fail(f"\n\n{differences}\n\nValidation error in `{name}`: {e.message}")
300
303
 
301
304
  return name, schema_updated
@@ -80,51 +80,61 @@ class SchemaStats:
80
80
 
81
81
  return "\n".join(parts)
82
82
 
83
+ def _iter_schemas(self, names: List[str]) -> Generator[tuple[str, Optional[str]], None, None]:
84
+ """
85
+ Iterates over schema displays: (display, schema_key)
86
+ - display: string to display (may have " + original")
87
+ - schema_key: file name of the schema (<name>.schema.json) to find diffs,
88
+ or None if it's not a schema.
89
+ Preserves the original list order: merging happens at .schema.json
90
+ position; skips .json if paired with schema.
91
+ """
92
+ names = list(names) # order matters
93
+ schema_sfx = ".schema.json"
94
+ json_sfx = ".json"
95
+
96
+ # sets of bases
97
+ # bases_with_schema = {n[: -len(schema_sfx)] for n in names if n.endswith(schema_sfx)}
98
+ bases_with_original = {
99
+ n[: -len(json_sfx)]
100
+ for n in names
101
+ if n.endswith(json_sfx) and not n.endswith(schema_sfx)
102
+ }
103
+
104
+ for n in names:
105
+ if n.endswith(schema_sfx):
106
+ base = n[: -len(schema_sfx)]
107
+ if base in bases_with_original:
108
+ yield f"{n} + original", n # display, schema_key
109
+ else:
110
+ yield n, n
111
+ # if .json, skip if paired
112
+ # if other, yield n, n (but assume all are .json or .schema.json)
113
+
114
+ def _iter_only_originals(self, names: List[str]) -> Generator[str, None, None]:
115
+ """
116
+ Iterates over only unpaired .json files, in the order they appear.
117
+ """
118
+ names = list(names) # order matters
119
+ schema_sfx = ".schema.json"
120
+ json_sfx = ".json"
121
+
122
+ bases_with_schema = {n[: -len(schema_sfx)] for n in names if n.endswith(schema_sfx)}
123
+
124
+ for n in names:
125
+ if n.endswith(json_sfx) and not n.endswith(schema_sfx):
126
+ base = n[: -len(json_sfx)]
127
+ if base not in bases_with_schema:
128
+ yield n
129
+
83
130
  def print_summary(self, terminalreporter: pytest.TerminalReporter, update_mode: bool) -> None:
84
131
  """
85
132
  Prints schema summary to pytest terminal output.
86
133
  Pairs of "<name>.schema.json" + "<name>.json" are merged into one line:
87
134
  "<name>.schema.json + original" (if original is present).
135
+ Unpaired .json are shown in separate "only originals" sections.
88
136
  """
89
137
 
90
- def _iter_merged(names: List[str]) -> Generator[tuple[str, Optional[str]], None, None]:
91
- """
92
- Iterates over (display, schema_key):
93
- - display: string to display (may have " + original")
94
- - schema_key: file name of the schema (<name>.schema.json) to find diffs,
95
- or None if it's not a schema.
96
- Preserves the original list order: merging happens at .schema.json
97
- position; single .json outputs are left as is.
98
- """
99
- names = list(names) # порядок важен
100
- schema_sfx = ".schema.json"
101
- json_sfx = ".json"
102
-
103
- # множество баз, где имеются схемы/оригиналы
104
- bases_with_schema = {n[: -len(schema_sfx)] for n in names if n.endswith(schema_sfx)}
105
- bases_with_original = {
106
- n[: -len(json_sfx)]
107
- for n in names
108
- if n.endswith(json_sfx) and not n.endswith(schema_sfx)
109
- }
110
-
111
- for n in names:
112
- if n.endswith(schema_sfx):
113
- base = n[: -len(schema_sfx)]
114
- if base in bases_with_original:
115
- yield f"{n} + original", n # display, schema_key
116
- else:
117
- yield n, n
118
- elif n.endswith(json_sfx) and not n.endswith(schema_sfx):
119
- base = n[: -len(json_sfx)]
120
- # если есть парная схема — .json не выводим отдельно
121
- if base in bases_with_schema:
122
- continue
123
- yield n, None
124
- else:
125
- # на всякий случай — прочие имена
126
- yield n, n
127
-
128
138
  if not self.has_any_info():
129
139
  return
130
140
 
@@ -132,52 +142,82 @@ class SchemaStats:
132
142
 
133
143
  # Created
134
144
  if self.created:
135
- terminalreporter.write_line(f"Created schemas ({len(self.created)}):", green=True)
136
- for display, _key in _iter_merged(self.created):
137
- terminalreporter.write_line(f" - {display}", green=True)
145
+ schemas = list(self._iter_schemas(self.created))
146
+ only_originals = list(self._iter_only_originals(self.created))
147
+ if schemas:
148
+ terminalreporter.write_line(f"Created schemas ({len(schemas)}):", green=True)
149
+ for display, _key in schemas:
150
+ terminalreporter.write_line(f" - {display}", green=True)
151
+ if only_originals:
152
+ terminalreporter.write_line(
153
+ f"Created only originals ({len(only_originals)}):", green=True
154
+ )
155
+ for display in only_originals:
156
+ terminalreporter.write_line(f" - {display}", green=True)
138
157
 
139
158
  # Updated
140
159
  if self.updated:
141
- terminalreporter.write_line(f"Updated schemas ({len(self.updated)}):", yellow=True)
142
- for display, key in _iter_merged(self.updated):
143
- terminalreporter.write_line(f" - {display}", yellow=True)
144
- # Показываем diff, если он есть под ключом схемы (.schema.json)
145
- if key and key in self.updated_diffs:
146
- terminalreporter.write_line(" Changes:", yellow=True)
147
- for line in self.updated_diffs[key].split("\n"):
148
- if line.strip():
149
- terminalreporter.write_line(f" {line}")
150
- terminalreporter.write_line("") # разделение
151
- elif key:
152
- terminalreporter.write_line(
153
- " (Schema unchanged - no differences detected)", cyan=True
154
- )
160
+ schemas = list(self._iter_schemas(self.updated))
161
+ only_originals = list(self._iter_only_originals(self.updated))
162
+ if schemas:
163
+ terminalreporter.write_line(f"Updated schemas ({len(schemas)}):", yellow=True)
164
+ for display, key in schemas:
165
+ terminalreporter.write_line(f" - {display}", yellow=True)
166
+ # Show diff if available for schema
167
+ if key and key in self.updated_diffs:
168
+ diff = self.updated_diffs[key]
169
+ if diff.strip():
170
+ terminalreporter.write_line(" Changes:", yellow=True)
171
+ for line in diff.split("\n"):
172
+ if line.strip():
173
+ terminalreporter.write_line(f" {line}")
174
+ terminalreporter.write_line("") # separation
175
+ else:
176
+ terminalreporter.write_line(
177
+ " (Schema unchanged - no differences detected)", cyan=True
178
+ )
179
+ if only_originals:
180
+ terminalreporter.write_line(
181
+ f"Updated only originals ({len(only_originals)}):", yellow=True
182
+ )
183
+ for display in only_originals:
184
+ terminalreporter.write_line(f" - {display}", yellow=True)
155
185
 
156
186
  # Uncommitted
157
187
  if self.uncommitted:
158
188
  terminalreporter.write_line(
159
189
  f"Uncommitted minor updates ({len(self.uncommitted)}):", bold=True
160
190
  )
161
- for display, key in _iter_merged(self.uncommitted):
191
+ for display, key in self._iter_schemas(self.uncommitted): # assuming mostly schemas
162
192
  terminalreporter.write_line(f" - {display}", cyan=True)
193
+ # Show diff if available
163
194
  if key and key in self.uncommitted_diffs:
164
195
  terminalreporter.write_line(" Detected changes:", cyan=True)
165
196
  for line in self.uncommitted_diffs[key].split("\n"):
166
197
  if line.strip():
167
198
  terminalreporter.write_line(f" {line}")
168
- terminalreporter.write_line("") # разделение
199
+ terminalreporter.write_line("") # separation
169
200
  terminalreporter.write_line("Use --schema-update to commit these changes", cyan=True)
170
201
 
171
202
  # Deleted
172
203
  if self.deleted:
173
- terminalreporter.write_line(f"Deleted schemas ({len(self.deleted)}):", red=True)
174
- for display, _key in _iter_merged(self.deleted):
175
- terminalreporter.write_line(f" - {display}", red=True)
176
-
177
- # Unused (только если не update_mode)
204
+ schemas = list(self._iter_schemas(self.deleted))
205
+ only_originals = list(self._iter_only_originals(self.deleted))
206
+ if schemas:
207
+ terminalreporter.write_line(f"Deleted schemas ({len(schemas)}):", red=True)
208
+ for display, _key in schemas:
209
+ terminalreporter.write_line(f" - {display}", red=True)
210
+ if only_originals:
211
+ terminalreporter.write_line(
212
+ f"Deleted only originals ({len(only_originals)}):", red=True
213
+ )
214
+ for display in only_originals:
215
+ terminalreporter.write_line(f" - {display}", red=True)
216
+
217
+ # Unused (only if not update_mode)
178
218
  if self.unused and not update_mode:
179
219
  terminalreporter.write_line(f"Unused schemas ({len(self.unused)}):")
180
- for display, _key in _iter_merged(self.unused):
220
+ for display, _key in self._iter_schemas(self.unused): # assuming schemas
181
221
  terminalreporter.write_line(f" - {display}")
182
222
  terminalreporter.write_line("Use --schema-update to delete unused schemas", yellow=True)
183
223
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytest-jsonschema-snapshot
3
- Version: 0.2.2
3
+ Version: 0.2.4
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
@@ -58,6 +58,7 @@ Description-Content-Type: text/markdown
58
58
  [![PyPI - Package Version](https://img.shields.io/pypi/v/pytest-jsonschema-snapshot?color=blue)](https://pypi.org/project/pytest-jsonschema-snapshot/)
59
59
  [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
60
60
  [![BlackCode](https://img.shields.io/badge/code%20style-black-black)](https://github.com/psf/black)
61
+ [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
61
62
  [![mypy](https://img.shields.io/badge/type--checked-mypy-blue?logo=python)](https://mypy.readthedocs.io/en/stable/index.html)
62
63
  [![Discord](https://img.shields.io/discord/792572437292253224?label=Discord&labelColor=%232c2f33&color=%237289da)](https://discord.gg/UnJnGHNbBp)
63
64
  [![Telegram](https://img.shields.io/badge/Telegram-24A1DE)](https://t.me/miskler_dev)
@@ -1,15 +1,15 @@
1
- pytest_jsonschema_snapshot/__init__.py,sha256=5TgaOzEtd_zql5fMk737SJ342QjH3l3atIBWuko_20g,385
2
- pytest_jsonschema_snapshot/core.py,sha256=vkS_aD8L2iqCQF9pGMlE63EuUFRC9sZSsrLQ2waLuEI,11852
1
+ pytest_jsonschema_snapshot/__init__.py,sha256=rUBgKVh7nCLEwfO0XZS-TewnM5eTXpBEI7NaC5p4qLg,385
2
+ pytest_jsonschema_snapshot/core.py,sha256=CoL_W-u6o3N7XDwv-MbePJiZaGX0LtGS6BLbj9MHROU,11995
3
3
  pytest_jsonschema_snapshot/plugin.py,sha256=nvAfxtLSX_B5FzaWu7DfsiWRxFjxDvnQNNOhkRrRnbw,8677
4
4
  pytest_jsonschema_snapshot/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- pytest_jsonschema_snapshot/stats.py,sha256=XGGzHY0ytMFOkFpnqNAK1DpV9iI0_fZPWrVvHFNFL3g,7943
5
+ pytest_jsonschema_snapshot/stats.py,sha256=BfhfMoSkRq6Q8BwhVfrpcFl5TP9OzpgpLwnKf1Kslkw,9593
6
6
  pytest_jsonschema_snapshot/tools/__init__.py,sha256=WMS6PdgMABBfTRhPGuoUOXB-R2PcqcadwH8pG1C6MFU,132
7
7
  pytest_jsonschema_snapshot/tools/name_maker.py,sha256=tqss8NCGSo2aQX_-RkCJzy3NJx_TDA-xrn8qsblecf0,5799
8
8
  pytest_jsonschema_snapshot/tools/genson_addon/__init__.py,sha256=nANkqHTaWTZPwBDztsnQvObHUZLSeHenJS--oWfep8c,92
9
9
  pytest_jsonschema_snapshot/tools/genson_addon/format_detector.py,sha256=Wc5pB_xstyr4OtjwJ2qqmV62xET63cN7Nb0gxkrYyW0,1636
10
10
  pytest_jsonschema_snapshot/tools/genson_addon/to_schema_converter.py,sha256=UdQIkZhMrTJNHwI1B1dv3aEwx41B1B_lLyr4KWiUpNY,4168
11
- pytest_jsonschema_snapshot-0.2.2.dist-info/METADATA,sha256=81-o1vJwGBWxE_neIz7kT6LS-Dw8PRxZSWURWzUHW9E,7657
12
- pytest_jsonschema_snapshot-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- pytest_jsonschema_snapshot-0.2.2.dist-info/entry_points.txt,sha256=eJ1x4TMmhcc8YtM7IoCsUJO4-rLeTrGy8tPgkrojjKs,58
14
- pytest_jsonschema_snapshot-0.2.2.dist-info/licenses/LICENSE,sha256=1HRFdSzlJ4BtHv6U7tZun3iCArjbCnm5NUowE9hZpNs,1071
15
- pytest_jsonschema_snapshot-0.2.2.dist-info/RECORD,,
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,,