cbpr-usage-rules 0.1.3__tar.gz → 0.2.0__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.
Files changed (55) hide show
  1. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/CHANGELOG.md +9 -0
  2. {cbpr_usage_rules-0.1.3/src/cbpr_usage_rules.egg-info → cbpr_usage_rules-0.2.0}/PKG-INFO +20 -3
  3. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/README.md +19 -2
  4. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/__init__.py +1 -1
  5. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/cli.py +54 -2
  6. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0/src/cbpr_usage_rules.egg-info}/PKG-INFO +20 -3
  7. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/LICENSE +0 -0
  8. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/MANIFEST.in +0 -0
  9. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/pyproject.toml +0 -0
  10. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/setup.cfg +0 -0
  11. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/engine.py +0 -0
  12. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/helpers.py +0 -0
  13. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/loader.py +0 -0
  14. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/message.py +0 -0
  15. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/models.py +0 -0
  16. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/py.typed +0 -0
  17. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/reference/__init__.py +0 -0
  18. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/reference/countries.py +0 -0
  19. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/reference/currencies.py +0 -0
  20. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/registry.py +0 -0
  21. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/__init__.py +0 -0
  22. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/__init__.py +0 -0
  23. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/camt_052.py +0 -0
  24. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/camt_054.py +0 -0
  25. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_002.py +0 -0
  26. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_004.py +0 -0
  27. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_008.py +0 -0
  28. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_008_stp.py +0 -0
  29. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_009.py +0 -0
  30. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_009_adv.py +0 -0
  31. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pacs_009_cov.py +0 -0
  32. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2025/pain_001.py +0 -0
  33. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/__init__.py +0 -0
  34. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/camt_052.py +0 -0
  35. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/camt_054.py +0 -0
  36. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_002.py +0 -0
  37. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_004.py +0 -0
  38. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_008.py +0 -0
  39. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_008_stp.py +0 -0
  40. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_009.py +0 -0
  41. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_009_adv.py +0 -0
  42. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pacs_009_cov.py +0 -0
  43. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/rules/y2026/pain_001.py +0 -0
  44. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/schema.py +0 -0
  45. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/__init__.py +0 -0
  46. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/bic.py +0 -0
  47. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/country.py +0 -0
  48. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/currency.py +0 -0
  49. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/iban.py +0 -0
  50. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_rules/validators/lei.py +0 -0
  51. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/SOURCES.txt +0 -0
  52. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/dependency_links.txt +0 -0
  53. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/entry_points.txt +0 -0
  54. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/requires.txt +0 -0
  55. {cbpr_usage_rules-0.1.3 → cbpr_usage_rules-0.2.0}/src/cbpr_usage_rules.egg-info/top_level.txt +0 -0
@@ -4,6 +4,15 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
5
5
  to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.2.0]
8
+
9
+ ### Added
10
+ - `--format {text,compact,json}` CLI option. The new `compact` format prints one
11
+ finding per line, compiler/linter-style (`file:line: severity [rule] message | at
12
+ xpath`) with a stable `VALID:`/`INVALID:` summary line — easy for tools and coding
13
+ agents to parse. `--json` is now an alias for `--format json`; the default rich
14
+ text output is unchanged.
15
+
7
16
  ## [0.1.3]
8
17
 
9
18
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cbpr-usage-rules
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
5
5
  Author: Pete Houghton
6
6
  License: MIT License
@@ -112,8 +112,11 @@ result = cbpr_rules.validate_string(xml_text, year=2026)
112
112
  # Human-readable (default). Exit code is non-zero if there are violations.
113
113
  cbpr-validate payment.xml --year 2025
114
114
 
115
- # JSON output
116
- cbpr-validate payment.xml --year 2025 --json
115
+ # Compact, one finding per line (compiler-style) — for tools and coding agents
116
+ cbpr-validate payment.xml --year 2025 --format compact
117
+
118
+ # JSON output (full machine-readable result)
119
+ cbpr-validate payment.xml --year 2025 --json # same as --format json
117
120
 
118
121
  # Read from stdin
119
122
  cat payment.xml | cbpr-validate --year 2026
@@ -127,6 +130,20 @@ cbpr-validate payment.xml --year 2025 --xsd pacs.008.001.08.xsd
127
130
 
128
131
  Each reported violation shows the rule, **why** this instance failed (`Problem:`),
129
132
  the **offending XML** from your file (`Found:`), and its line + xpath (`At:`).
133
+
134
+ **For tools and coding agents**, prefer `--format compact` (one finding per line,
135
+ parseable like a compiler/linter) or `--json` (the full structured result):
136
+
137
+ ```text
138
+ $ cbpr-validate payment.xml --year 2025 --format compact
139
+ payment.xml:7: violation [pacs.008:R2] From vs Instructing Agent: ABCDGB2LXXX != GGGGGB2LXXX | at /AppHdr/Fr/FIId/FinInstnId/BICFI
140
+ payment.xml:63: violation [pacs.008:VAL-IBAN] invalid IBAN: 'GB29NWBK60161331926818' | at /Document/.../DbtrAcct/Id/IBAN
141
+ INVALID: 5 violations (87 rules)
142
+ ```
143
+
144
+ Each line is `file:line: severity [rule] message | at xpath`, and the final
145
+ `VALID:` / `INVALID:` line is a stable summary to grep for. With `--xsd`, schema
146
+ errors appear as `schema-error` lines and are counted in the summary.
130
147
  Advisory rules are summarised as a count by default; pass `--advisory` to list them.
131
148
 
132
149
  ---
@@ -57,8 +57,11 @@ result = cbpr_rules.validate_string(xml_text, year=2026)
57
57
  # Human-readable (default). Exit code is non-zero if there are violations.
58
58
  cbpr-validate payment.xml --year 2025
59
59
 
60
- # JSON output
61
- cbpr-validate payment.xml --year 2025 --json
60
+ # Compact, one finding per line (compiler-style) — for tools and coding agents
61
+ cbpr-validate payment.xml --year 2025 --format compact
62
+
63
+ # JSON output (full machine-readable result)
64
+ cbpr-validate payment.xml --year 2025 --json # same as --format json
62
65
 
63
66
  # Read from stdin
64
67
  cat payment.xml | cbpr-validate --year 2026
@@ -72,6 +75,20 @@ cbpr-validate payment.xml --year 2025 --xsd pacs.008.001.08.xsd
72
75
 
73
76
  Each reported violation shows the rule, **why** this instance failed (`Problem:`),
74
77
  the **offending XML** from your file (`Found:`), and its line + xpath (`At:`).
78
+
79
+ **For tools and coding agents**, prefer `--format compact` (one finding per line,
80
+ parseable like a compiler/linter) or `--json` (the full structured result):
81
+
82
+ ```text
83
+ $ cbpr-validate payment.xml --year 2025 --format compact
84
+ payment.xml:7: violation [pacs.008:R2] From vs Instructing Agent: ABCDGB2LXXX != GGGGGB2LXXX | at /AppHdr/Fr/FIId/FinInstnId/BICFI
85
+ payment.xml:63: violation [pacs.008:VAL-IBAN] invalid IBAN: 'GB29NWBK60161331926818' | at /Document/.../DbtrAcct/Id/IBAN
86
+ INVALID: 5 violations (87 rules)
87
+ ```
88
+
89
+ Each line is `file:line: severity [rule] message | at xpath`, and the final
90
+ `VALID:` / `INVALID:` line is a stable summary to grep for. With `--xsd`, schema
91
+ errors appear as `schema-error` lines and are counted in the summary.
75
92
  Advisory rules are summarised as a count by default; pass `--advisory` to list them.
76
93
 
77
94
  ---
@@ -4,7 +4,7 @@ This package is AI generated. See the README for details and caveats.
4
4
  """
5
5
  from __future__ import annotations
6
6
 
7
- __version__ = "0.1.3"
7
+ __version__ = "0.2.0"
8
8
 
9
9
  from .models import Rule, Severity, Violation
10
10
  from .engine import validate_file, validate_string, list_rules, available
@@ -26,7 +26,18 @@ def _build_parser() -> argparse.ArgumentParser:
26
26
  help="Message type override, e.g. pacs.008 or pacs.008_stp "
27
27
  "(auto-detected from the Document namespace if omitted).",
28
28
  )
29
- p.add_argument("--json", action="store_true", help="Emit JSON instead of text.")
29
+ p.add_argument(
30
+ "--format",
31
+ choices=("text", "compact", "json"),
32
+ default="text",
33
+ help="Output format: text (rich, default), compact (one finding per line, "
34
+ "compiler-style — for tools/agents), or json (full machine-readable).",
35
+ )
36
+ p.add_argument(
37
+ "--json",
38
+ action="store_true",
39
+ help="Alias for --format json.",
40
+ )
30
41
  p.add_argument(
31
42
  "--xsd",
32
43
  action="append",
@@ -125,6 +136,43 @@ def _format_text(result: dict, show_advisory: bool = False) -> str:
125
136
  return "\n".join(lines)
126
137
 
127
138
 
139
+ def _format_compact(result: dict, source: str) -> str:
140
+ """One finding per line, compiler/linter-style, for tools and coding agents.
141
+
142
+ ``<source>:<line>: <severity> [<rule>] <message> | at <xpath>`` per violation,
143
+ followed by a single greppable ``VALID:``/``INVALID:`` summary line. Advisory
144
+ (non-enforced) rules are omitted; use --json or text --advisory for those.
145
+ """
146
+ lines: List[str] = []
147
+ for v in result["violations"]:
148
+ line = v["line"] if v["line"] is not None else "?"
149
+ message = " ".join((v.get("detail") or v["description"] or "").split())
150
+ suffix = f" | at {v['xpath']}" if v.get("xpath") else ""
151
+ lines.append(f"{source}:{line}: {v['severity']} [{v['rule_number']}] {message}{suffix}")
152
+
153
+ xsd = result.get("xsd")
154
+ schema_errors = 0
155
+ if xsd is not None:
156
+ for s in xsd["schemas"]:
157
+ if s.get("load_error"):
158
+ schema_errors += 1
159
+ lines.append(f"{s['file']}: schema-error could not load schema: {s['load_error']}")
160
+ for e in s["errors"]:
161
+ schema_errors += 1
162
+ eline = e["line"] if e.get("line") is not None else "?"
163
+ msg = " ".join((e.get("message") or "").split())
164
+ lines.append(f"{s['file']}:{eline}: schema-error {msg}")
165
+
166
+ n = len(result["violations"])
167
+ verdict = "VALID" if result["valid"] else "INVALID"
168
+ parts = [f"{n} violation{'s' if n != 1 else ''}"]
169
+ if xsd is not None:
170
+ parts.append(f"{schema_errors} schema error{'s' if schema_errors != 1 else ''}")
171
+ summary = f"{verdict}: {', '.join(parts)} ({result['rules_evaluated']} rules)"
172
+ lines.append(summary)
173
+ return "\n".join(lines)
174
+
175
+
128
176
  def main(argv: Optional[List[str]] = None) -> int:
129
177
  args = _build_parser().parse_args(argv)
130
178
 
@@ -153,6 +201,7 @@ def main(argv: Optional[List[str]] = None) -> int:
153
201
  print("error: --year is required to validate", file=sys.stderr)
154
202
  return 2
155
203
 
204
+ source = args.file or "<stdin>"
156
205
  try:
157
206
  if args.file:
158
207
  result = validate_file(args.file, args.year, args.msgtype, xsd=args.xsd)
@@ -163,8 +212,11 @@ def main(argv: Optional[List[str]] = None) -> int:
163
212
  print(f"error: {exc}", file=sys.stderr)
164
213
  return 2
165
214
 
166
- if args.json:
215
+ fmt = "json" if args.json else args.format
216
+ if fmt == "json":
167
217
  print(json.dumps(result, indent=2))
218
+ elif fmt == "compact":
219
+ print(_format_compact(result, source))
168
220
  else:
169
221
  print(_format_text(result, show_advisory=args.advisory))
170
222
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cbpr-usage-rules
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: Validate ISO 20022 CBPR+ XML messages against SWIFT usage (business) rules.
5
5
  Author: Pete Houghton
6
6
  License: MIT License
@@ -112,8 +112,11 @@ result = cbpr_rules.validate_string(xml_text, year=2026)
112
112
  # Human-readable (default). Exit code is non-zero if there are violations.
113
113
  cbpr-validate payment.xml --year 2025
114
114
 
115
- # JSON output
116
- cbpr-validate payment.xml --year 2025 --json
115
+ # Compact, one finding per line (compiler-style) — for tools and coding agents
116
+ cbpr-validate payment.xml --year 2025 --format compact
117
+
118
+ # JSON output (full machine-readable result)
119
+ cbpr-validate payment.xml --year 2025 --json # same as --format json
117
120
 
118
121
  # Read from stdin
119
122
  cat payment.xml | cbpr-validate --year 2026
@@ -127,6 +130,20 @@ cbpr-validate payment.xml --year 2025 --xsd pacs.008.001.08.xsd
127
130
 
128
131
  Each reported violation shows the rule, **why** this instance failed (`Problem:`),
129
132
  the **offending XML** from your file (`Found:`), and its line + xpath (`At:`).
133
+
134
+ **For tools and coding agents**, prefer `--format compact` (one finding per line,
135
+ parseable like a compiler/linter) or `--json` (the full structured result):
136
+
137
+ ```text
138
+ $ cbpr-validate payment.xml --year 2025 --format compact
139
+ payment.xml:7: violation [pacs.008:R2] From vs Instructing Agent: ABCDGB2LXXX != GGGGGB2LXXX | at /AppHdr/Fr/FIId/FinInstnId/BICFI
140
+ payment.xml:63: violation [pacs.008:VAL-IBAN] invalid IBAN: 'GB29NWBK60161331926818' | at /Document/.../DbtrAcct/Id/IBAN
141
+ INVALID: 5 violations (87 rules)
142
+ ```
143
+
144
+ Each line is `file:line: severity [rule] message | at xpath`, and the final
145
+ `VALID:` / `INVALID:` line is a stable summary to grep for. With `--xsd`, schema
146
+ errors appear as `schema-error` lines and are counted in the summary.
130
147
  Advisory rules are summarised as a count by default; pass `--advisory` to list them.
131
148
 
132
149
  ---