Coverage for pytest_jsonschema_snapshot/stats.py: 63%
100 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-02 00:37 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-02 00:37 +0000
1"""
2Module for collecting and displaying statistics about schemas.
3"""
5from typing import Dict, Generator, List, Optional
7import pytest
10class SchemaStats:
11 """Class for collecting and displaying statistics about schemas"""
13 def __init__(self) -> None:
14 self.created: List[str] = []
15 self.updated: List[str] = []
16 self.updated_diffs: Dict[str, str] = {} # schema_name -> diff
17 self.uncommitted: List[str] = [] # New category for uncommitted changes
18 self.uncommitted_diffs: Dict[str, str] = {} # schema_name -> diff
19 self.deleted: List[str] = []
20 self.unused: List[str] = []
22 def add_created(self, schema_name: str) -> None:
23 """Adds created schema"""
24 self.created.append(schema_name)
26 def add_updated(self, schema_name: str, diff: Optional[str] = None) -> None:
27 """Adds updated schema"""
28 # Generate diff if both schemas are provided
29 if diff and diff.strip():
30 self.updated.append(schema_name)
31 self.updated_diffs[schema_name] = diff
32 else:
33 # If schemas are not provided, assume it was an update
34 self.updated.append(schema_name)
36 def add_uncommitted(self, schema_name: str, diff: Optional[str] = None) -> None:
37 """Adds schema with uncommitted changes"""
38 # Add only if there are real changes
39 if diff and diff.strip():
40 self.uncommitted.append(schema_name)
41 self.uncommitted_diffs[schema_name] = diff
43 def add_deleted(self, schema_name: str) -> None:
44 """Adds deleted schema"""
45 self.deleted.append(schema_name)
47 def add_unused(self, schema_name: str) -> None:
48 """Adds unused schema"""
49 self.unused.append(schema_name)
51 def has_changes(self) -> bool:
52 """Returns True if any schema has changes"""
53 return bool(self.created or self.updated or self.deleted)
55 def has_any_info(self) -> bool:
56 """Is there any information about schemas"""
57 return bool(self.created or self.updated or self.deleted or self.unused or self.uncommitted)
59 def __str__(self) -> str:
60 parts = []
61 if self.created:
62 parts.append(
63 f"Created schemas ({len(self.created)}): "
64 + ", ".join(f"`{s}`" for s in self.created)
65 )
66 if self.updated:
67 parts.append(
68 f"Updated schemas ({len(self.updated)}): "
69 + ", ".join(f"`{s}`" for s in self.updated)
70 )
71 if self.deleted:
72 parts.append(
73 f"Deleted schemas ({len(self.deleted)}): "
74 + ", ".join(f"`{s}`" for s in self.deleted)
75 )
76 if self.unused:
77 parts.append(
78 f"Unused schemas ({len(self.unused)}): " + ", ".join(f"`{s}`" for s in self.unused)
79 )
81 return "\n".join(parts)
83 def print_summary(self, terminalreporter: pytest.TerminalReporter, update_mode: bool) -> None:
84 """
85 Prints schema summary to pytest terminal output.
86 Pairs of "<name>.schema.json" + "<name>.json" are merged into one line:
87 "<name>.schema.json + original" (if original is present).
88 """
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"
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 }
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
128 if not self.has_any_info():
129 return
131 terminalreporter.write_sep("=", "Schema Summary")
133 # Created
134 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)
139 # Updated
140 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 )
156 # Uncommitted
157 if self.uncommitted:
158 terminalreporter.write_line(
159 f"Uncommitted minor updates ({len(self.uncommitted)}):", bold=True
160 )
161 for display, key in _iter_merged(self.uncommitted):
162 terminalreporter.write_line(f" - {display}", cyan=True)
163 if key and key in self.uncommitted_diffs:
164 terminalreporter.write_line(" Detected changes:", cyan=True)
165 for line in self.uncommitted_diffs[key].split("\n"):
166 if line.strip():
167 terminalreporter.write_line(f" {line}")
168 terminalreporter.write_line("") # разделение
169 terminalreporter.write_line("Use --schema-update to commit these changes", cyan=True)
171 # Deleted
172 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)
177 # Unused (только если не update_mode)
178 if self.unused and not update_mode:
179 terminalreporter.write_line(f"Unused schemas ({len(self.unused)}):")
180 for display, _key in _iter_merged(self.unused):
181 terminalreporter.write_line(f" - {display}")
182 terminalreporter.write_line("Use --schema-update to delete unused schemas", yellow=True)
185GLOBAL_STATS = SchemaStats()