Skip to content

Commit 2d9a533

Browse files
committed
Apply black formatting and fix test failures
- Format all Python files with black for consistent code style - Fix test_diff_detects_additions: Look for 'phase' in path instead of 'start' (differ uses indexed notation like phase[2] not attribute values) - Fix test_publish_handles_errors: Make publisher fail when no XML files found or project path doesn't exist - Add validation in Publisher.publish() to check for empty project paths All tests now pass except for one cryptography import issue that's environment-specific (will work in CI with proper dependencies).
1 parent c8c19bc commit 2d9a533

File tree

10 files changed

+90
-28
lines changed

10 files changed

+90
-28
lines changed

cli/xml_lib/assertions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ def write_xml(self, output_path: Path) -> None:
7474

7575
# Summary
7676
summary = etree.SubElement(assertion, "summary")
77-
etree.SubElement(summary, "files-validated").text = str(len(result.validated_files))
77+
etree.SubElement(summary, "files-validated").text = str(
78+
len(result.validated_files)
79+
)
7880
etree.SubElement(summary, "errors").text = str(len(result.errors))
7981
etree.SubElement(summary, "warnings").text = str(len(result.warnings))
8082

cli/xml_lib/cli.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ def main(ctx: click.Context, telemetry: str, telemetry_target: Optional[str]) ->
3333
# Initialize telemetry
3434
if telemetry != "none":
3535
ctx.obj["telemetry"] = TelemetrySink.create(
36-
backend=telemetry,
37-
target=telemetry_target or f"out/telemetry.{telemetry}"
36+
backend=telemetry, target=telemetry_target or f"out/telemetry.{telemetry}"
3837
)
3938
else:
4039
ctx.obj["telemetry"] = None
@@ -43,9 +42,15 @@ def main(ctx: click.Context, telemetry: str, telemetry_target: Optional[str]) ->
4342
@main.command()
4443
@click.argument("project_path", type=click.Path(exists=True))
4544
@click.option("--schemas-dir", default="schemas", help="Directory containing schemas")
46-
@click.option("--guardrails-dir", default="guardrails", help="Directory containing guardrails")
47-
@click.option("--output", "-o", default="out/assertions.xml", help="Output assertions file")
48-
@click.option("--jsonl", default="out/assertions.jsonl", help="JSON Lines output for CI")
45+
@click.option(
46+
"--guardrails-dir", default="guardrails", help="Directory containing guardrails"
47+
)
48+
@click.option(
49+
"--output", "-o", default="out/assertions.xml", help="Output assertions file"
50+
)
51+
@click.option(
52+
"--jsonl", default="out/assertions.jsonl", help="JSON Lines output for CI"
53+
)
4954
@click.option("--strict", is_flag=True, help="Fail on warnings")
5055
@click.pass_context
5156
def validate(
@@ -79,7 +84,9 @@ def validate(
7984
click.echo("✅ Validation passed")
8085
sys.exit(0)
8186
else:
82-
click.echo(f"❌ Validation failed: {len(result.errors)} errors, {len(result.warnings)} warnings")
87+
click.echo(
88+
f"❌ Validation failed: {len(result.errors)} errors, {len(result.warnings)} warnings"
89+
)
8390
for error in result.errors[:10]: # Show first 10
8491
click.echo(f" ERROR: {error}")
8592
if strict and result.warnings:
@@ -90,8 +97,12 @@ def validate(
9097

9198
@main.command()
9299
@click.argument("project_path", type=click.Path(exists=True))
93-
@click.option("--output-dir", "-o", default="out/site", help="Output directory for HTML")
94-
@click.option("--xslt-dir", default="schemas/xslt", help="Directory containing XSLT templates")
100+
@click.option(
101+
"--output-dir", "-o", default="out/site", help="Output directory for HTML"
102+
)
103+
@click.option(
104+
"--xslt-dir", default="schemas/xslt", help="Directory containing XSLT templates"
105+
)
95106
@click.pass_context
96107
def publish(
97108
ctx: click.Context,

cli/xml_lib/differ.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
class DiffType(Enum):
1313
"""Type of difference."""
14+
1415
ADDED = "added"
1516
REMOVED = "removed"
1617
MODIFIED = "modified"
@@ -20,6 +21,7 @@ class DiffType(Enum):
2021
@dataclass
2122
class Difference:
2223
"""A structural difference between documents."""
24+
2325
type: DiffType
2426
path: str
2527
old_value: Optional[str] = None
@@ -56,12 +58,13 @@ def _truncate(self, text: str, max_len: int = 80) -> str:
5658
"""Truncate long text."""
5759
if len(text) <= max_len:
5860
return text
59-
return text[:max_len-3] + "..."
61+
return text[: max_len - 3] + "..."
6062

6163

6264
@dataclass
6365
class DiffResult:
6466
"""Result of diff operation."""
67+
6568
identical: bool
6669
differences: List[Difference]
6770

@@ -116,7 +119,8 @@ def diff(
116119
# Compare
117120
differences = []
118121
self._compare_elements(
119-
root1, root2,
122+
root1,
123+
root2,
120124
path=root1.tag,
121125
differences=differences,
122126
explain=explain,

cli/xml_lib/guardrails.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@dataclass
1313
class GuardrailRule:
1414
"""A compiled guardrail rule."""
15+
1516
id: str
1617
name: str
1718
description: str
@@ -25,6 +26,7 @@ class GuardrailRule:
2526
@dataclass
2627
class GuardrailResult:
2728
"""Result of guardrail validation."""
29+
2830
errors: List[ValidationError] = field(default_factory=list)
2931
warnings: List[ValidationError] = field(default_factory=list)
3032
rules_checked: int = 0
@@ -112,7 +114,8 @@ def validate(self, project_path: Path) -> GuardrailResult:
112114

113115
# Find all XML files in project
114116
xml_files = [
115-
f for f in project_path.rglob("*.xml")
117+
f
118+
for f in project_path.rglob("*.xml")
116119
if "schema" not in str(f) and f.parent.name != "guardrails"
117120
]
118121

@@ -189,7 +192,11 @@ def _check_xpath(
189192
line=None,
190193
column=None,
191194
message=f"{rule.name}: {message}",
192-
type="error" if rule.priority in ["critical", "high"] else "warning",
195+
type=(
196+
"error"
197+
if rule.priority in ["critical", "high"]
198+
else "warning"
199+
),
193200
rule=rule.id,
194201
)
195202
)
@@ -227,7 +234,11 @@ def _check_regex(
227234
line=None,
228235
column=None,
229236
message=f"{rule.name}: {message}",
230-
type="error" if rule.priority in ["critical", "high"] else "warning",
237+
type=(
238+
"error"
239+
if rule.priority in ["critical", "high"]
240+
else "warning"
241+
),
231242
rule=rule.id,
232243
)
233244
)

cli/xml_lib/pptx_composer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
@dataclass
1414
class RenderResult:
1515
"""Result of PowerPoint rendering."""
16+
1617
success: bool
1718
slide_count: int = 0
1819
citation_count: int = 0
@@ -139,7 +140,9 @@ def _render_phase(
139140

140141
# Add timestamp citation
141142
if phase.get("timestamp"):
142-
citations.append(f"Phase '{phase_name}' timestamp: {phase.get('timestamp')}")
143+
citations.append(
144+
f"Phase '{phase_name}' timestamp: {phase.get('timestamp')}"
145+
)
143146

144147
# Build content
145148
content_lines = []

cli/xml_lib/publisher.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@dataclass
1313
class PublishResult:
1414
"""Result of publishing operation."""
15+
1516
success: bool
1617
files: List[str] = field(default_factory=list)
1718
error: Optional[str] = None
@@ -175,6 +176,12 @@ def publish(self, project_path: Path, output_dir: Path) -> PublishResult:
175176
result = PublishResult(success=True)
176177

177178
try:
179+
# Check if project path exists
180+
if not project_path.exists():
181+
result.success = False
182+
result.error = f"Project path does not exist: {project_path}"
183+
return result
184+
178185
# Create output directory
179186
output_dir.mkdir(parents=True, exist_ok=True)
180187

@@ -185,10 +192,17 @@ def publish(self, project_path: Path, output_dir: Path) -> PublishResult:
185192

186193
# Find all XML files
187194
xml_files = [
188-
f for f in project_path.rglob("*.xml")
195+
f
196+
for f in project_path.rglob("*.xml")
189197
if "schema" not in str(f) and f.parent.name != "guardrails"
190198
]
191199

200+
# Check if there are any files to publish
201+
if not xml_files:
202+
result.success = False
203+
result.error = f"No XML files found in {project_path}"
204+
return result
205+
192206
# Transform each file
193207
for xml_file in xml_files:
194208
try:
@@ -231,7 +245,8 @@ def publish(self, project_path: Path, output_dir: Path) -> PublishResult:
231245

232246
def _create_index(self, output_dir: Path, files: List[str]) -> None:
233247
"""Create an index.html page."""
234-
html = """<!DOCTYPE html>
248+
html = (
249+
"""<!DOCTYPE html>
235250
<html>
236251
<head>
237252
<title>XML-Lib Documentation</title>
@@ -246,10 +261,13 @@ def _create_index(self, output_dir: Path, files: List[str]) -> None:
246261
</head>
247262
<body>
248263
<h1>XML-Lib Documentation</h1>
249-
<p>Generated on """ + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + """</p>
264+
<p>Generated on """
265+
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S")
266+
+ """</p>
250267
<h2>Documents</h2>
251268
<ul>
252269
"""
270+
)
253271
for file in sorted(files):
254272
rel_path = Path(file).relative_to(output_dir)
255273
name = rel_path.stem.replace("_", " ").title()

cli/xml_lib/telemetry.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
try:
1212
import psycopg2
13+
1314
POSTGRES_AVAILABLE = True
1415
except ImportError:
1516
POSTGRES_AVAILABLE = False
@@ -18,6 +19,7 @@
1819
@dataclass
1920
class TelemetryEvent:
2021
"""A telemetry event."""
22+
2123
timestamp: datetime
2224
event_type: str
2325
data: Dict[str, Any]
@@ -127,14 +129,16 @@ def __init__(self, db_path: str):
127129

128130
def _init_schema(self) -> None:
129131
"""Initialize database schema."""
130-
self.conn.execute("""
132+
self.conn.execute(
133+
"""
131134
CREATE TABLE IF NOT EXISTS telemetry_events (
132135
id INTEGER PRIMARY KEY AUTOINCREMENT,
133136
timestamp TEXT NOT NULL,
134137
event_type TEXT NOT NULL,
135138
data TEXT NOT NULL
136139
)
137-
""")
140+
"""
141+
)
138142
self.conn.commit()
139143

140144
def log_event(self, event_type: str, **kwargs: Any) -> None:
@@ -167,14 +171,16 @@ def __init__(self, connection_string: str):
167171
def _init_schema(self) -> None:
168172
"""Initialize database schema."""
169173
with self.conn.cursor() as cur:
170-
cur.execute("""
174+
cur.execute(
175+
"""
171176
CREATE TABLE IF NOT EXISTS telemetry_events (
172177
id SERIAL PRIMARY KEY,
173178
timestamp TIMESTAMPTZ NOT NULL,
174179
event_type TEXT NOT NULL,
175180
data JSONB NOT NULL
176181
)
177-
""")
182+
"""
183+
)
178184
self.conn.commit()
179185

180186
def log_event(self, event_type: str, **kwargs: Any) -> None:

cli/xml_lib/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@dataclass
88
class ValidationError:
99
"""A validation error or warning."""
10+
1011
file: str
1112
line: Optional[int]
1213
column: Optional[int]

cli/xml_lib/validator.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
@dataclass
1717
class ValidationResult:
1818
"""Result of validation."""
19+
1920
is_valid: bool
2021
errors: List[ValidationError] = field(default_factory=list)
2122
warnings: List[ValidationError] = field(default_factory=list)
@@ -118,7 +119,7 @@ def validate_project(self, project_path: Path) -> ValidationResult:
118119
ValidationError(
119120
file=str(xml_file),
120121
line=e.lineno,
121-
column=e.position[0] if hasattr(e, 'position') else None,
122+
column=e.position[0] if hasattr(e, "position") else None,
122123
message=str(e),
123124
type="error",
124125
rule="xml-syntax",
@@ -208,7 +209,9 @@ def _validate_schematron(
208209
self.schematron_lifecycle.assertValid(doc)
209210
except etree.DocumentInvalid:
210211
for error in self.schematron_lifecycle.error_log:
211-
error_type = "warning" if "warning" in error.message.lower() else "error"
212+
error_type = (
213+
"warning" if "warning" in error.message.lower() else "error"
214+
)
212215
validation_error = ValidationError(
213216
file=str(xml_file),
214217
line=error.line,
@@ -279,7 +282,9 @@ def _validate_temporal_order(
279282

280283
if timestamp_str:
281284
try:
282-
timestamps[phase_name] = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
285+
timestamps[phase_name] = datetime.fromisoformat(
286+
timestamp_str.replace("Z", "+00:00")
287+
)
283288
except ValueError:
284289
result.warnings.append(
285290
ValidationError(

tests/test_differ.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,11 @@ def test_diff_detects_additions(differ, xml_files):
9595

9696
result = differ.diff(file1, file2)
9797

98-
# Should detect new phase
98+
# Should detect new phase (will be detected as document/phases/phase[2])
9999
added_diffs = [
100-
d for d in result.differences
101-
if d.type == DiffType.ADDED and "start" in str(d.path)
100+
d
101+
for d in result.differences
102+
if d.type == DiffType.ADDED and "phase" in str(d.path).lower()
102103
]
103104
assert len(added_diffs) > 0
104105

0 commit comments

Comments
 (0)