pactship 0.1.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 (58) hide show
  1. pactship-0.1.0/LICENSE +21 -0
  2. pactship-0.1.0/PKG-INFO +540 -0
  3. pactship-0.1.0/README.md +510 -0
  4. pactship-0.1.0/pactship/__init__.py +104 -0
  5. pactship-0.1.0/pactship/broker.py +181 -0
  6. pactship-0.1.0/pactship/cli.py +236 -0
  7. pactship-0.1.0/pactship/config.py +143 -0
  8. pactship-0.1.0/pactship/contract_io.py +104 -0
  9. pactship-0.1.0/pactship/diff.py +311 -0
  10. pactship-0.1.0/pactship/dsl.py +170 -0
  11. pactship-0.1.0/pactship/filters.py +132 -0
  12. pactship-0.1.0/pactship/generator.py +218 -0
  13. pactship-0.1.0/pactship/graph.py +196 -0
  14. pactship-0.1.0/pactship/hooks.py +124 -0
  15. pactship-0.1.0/pactship/linter.py +209 -0
  16. pactship-0.1.0/pactship/matchers.py +455 -0
  17. pactship-0.1.0/pactship/matrix.py +165 -0
  18. pactship-0.1.0/pactship/models.py +357 -0
  19. pactship-0.1.0/pactship/openapi.py +187 -0
  20. pactship-0.1.0/pactship/reporting.py +143 -0
  21. pactship-0.1.0/pactship/schema.py +248 -0
  22. pactship-0.1.0/pactship/stats.py +152 -0
  23. pactship-0.1.0/pactship/transform.py +130 -0
  24. pactship-0.1.0/pactship/validator.py +214 -0
  25. pactship-0.1.0/pactship/verifier.py +245 -0
  26. pactship-0.1.0/pactship.egg-info/PKG-INFO +540 -0
  27. pactship-0.1.0/pactship.egg-info/SOURCES.txt +56 -0
  28. pactship-0.1.0/pactship.egg-info/dependency_links.txt +1 -0
  29. pactship-0.1.0/pactship.egg-info/entry_points.txt +2 -0
  30. pactship-0.1.0/pactship.egg-info/requires.txt +11 -0
  31. pactship-0.1.0/pactship.egg-info/top_level.txt +1 -0
  32. pactship-0.1.0/pyproject.toml +49 -0
  33. pactship-0.1.0/setup.cfg +4 -0
  34. pactship-0.1.0/tests/test_broker.py +184 -0
  35. pactship-0.1.0/tests/test_cli.py +154 -0
  36. pactship-0.1.0/tests/test_config.py +141 -0
  37. pactship-0.1.0/tests/test_contract_io.py +211 -0
  38. pactship-0.1.0/tests/test_diff.py +245 -0
  39. pactship-0.1.0/tests/test_dsl.py +200 -0
  40. pactship-0.1.0/tests/test_edge_cases.py +256 -0
  41. pactship-0.1.0/tests/test_filters.py +158 -0
  42. pactship-0.1.0/tests/test_generator.py +210 -0
  43. pactship-0.1.0/tests/test_graph.py +120 -0
  44. pactship-0.1.0/tests/test_hooks.py +140 -0
  45. pactship-0.1.0/tests/test_integration.py +214 -0
  46. pactship-0.1.0/tests/test_linter.py +185 -0
  47. pactship-0.1.0/tests/test_matchers.py +361 -0
  48. pactship-0.1.0/tests/test_matrix.py +195 -0
  49. pactship-0.1.0/tests/test_models.py +377 -0
  50. pactship-0.1.0/tests/test_openapi.py +260 -0
  51. pactship-0.1.0/tests/test_public_api.py +78 -0
  52. pactship-0.1.0/tests/test_reporting.py +182 -0
  53. pactship-0.1.0/tests/test_schema.py +200 -0
  54. pactship-0.1.0/tests/test_stats.py +145 -0
  55. pactship-0.1.0/tests/test_transform.py +148 -0
  56. pactship-0.1.0/tests/test_validator.py +300 -0
  57. pactship-0.1.0/tests/test_verifier.py +117 -0
  58. pactship-0.1.0/tests/test_verifier_async.py +293 -0
pactship-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JSLEEKR
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,540 @@
1
+ Metadata-Version: 2.4
2
+ Name: pactship
3
+ Version: 0.1.0
4
+ Summary: Lightweight consumer-driven contract testing for microservices APIs
5
+ Author-email: JSLEEKR <93jslee@gmail.com>
6
+ License: MIT
7
+ Keywords: contract-testing,api,microservices,pact,testing
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Testing
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: click>=8.0
20
+ Requires-Dist: pyyaml>=6.0
21
+ Requires-Dist: jsonschema>=4.0
22
+ Requires-Dist: rich>=13.0
23
+ Requires-Dist: httpx>=0.24
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == "dev"
26
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
27
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
28
+ Requires-Dist: respx>=0.20; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ <div align="center">
32
+
33
+ # :handshake: pactship
34
+
35
+ ### Broker-less contract testing for microservices
36
+
37
+ [![GitHub Stars](https://img.shields.io/github/stars/JSLEEKR/pactship?style=for-the-badge&logo=github&color=yellow)](https://github.com/JSLEEKR/pactship/stargazers)
38
+ [![License](https://img.shields.io/badge/license-MIT-blue?style=for-the-badge)](LICENSE)
39
+ [![Python](https://img.shields.io/badge/Python-3.10+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://python.org)
40
+ [![Tests](https://img.shields.io/badge/tests-525%20passing-brightgreen?style=for-the-badge)](#)
41
+
42
+ <br/>
43
+
44
+ **Define contracts. Verify providers. Catch breaking changes before they ship.**
45
+
46
+ [Quick Start](#quick-start) | [CLI](#cli-commands) | [Matchers](#matchers) | [API](#programmatic-api) | [Architecture](#architecture)
47
+
48
+ </div>
49
+
50
+ ---
51
+
52
+ ## Why This Exists
53
+
54
+ Microservices break in production when providers change their APIs without telling consumers. The consumer expects `GET /users/1` to return `{ id, name, email }` -- the provider ships a rename from `name` to `full_name` and three downstream services crash at 2 AM.
55
+
56
+ Existing contract testing tools solve this -- but they bring heavyweight infrastructure with them. Pact JVM needs a broker server. Spring Cloud Contract requires a JVM toolchain. Both demand CI/CD plumbing that takes longer to set up than the contracts themselves.
57
+
58
+ **pactship** is a zero-infrastructure, file-based contract testing tool for Python. Write contracts in YAML or JSON, verify them against live providers with async HTTP, diff versions to detect breaking changes, and store everything locally -- no broker server needed, no background processes, no Docker containers.
59
+
60
+ - **No infrastructure** -- contracts live as files in your repo, verified locally or in CI
61
+ - **Fluent Python DSL** -- build contracts programmatically with type-checked builders
62
+ - **16 matcher types** -- from exact match to regex, UUID, email, ISO dates, nullable, and range
63
+ - **Breaking change detection** -- diff two contract versions with breaking/non-breaking classification
64
+
65
+ ## Requirements
66
+
67
+ - Python 3.10+
68
+ - Dependencies: `click`, `pyyaml`, `jsonschema`, `rich`, `httpx`
69
+
70
+ ## Quick Start
71
+
72
+ ```bash
73
+ pip install pactship
74
+ ```
75
+
76
+ ### Define a Contract (YAML)
77
+
78
+ ```yaml
79
+ consumer: order-service
80
+ provider: user-api
81
+ interactions:
82
+ - description: Get user by ID
83
+ request:
84
+ method: GET
85
+ path: /users/1
86
+ response:
87
+ status: 200
88
+ body:
89
+ id: 1
90
+ name: Alice
91
+ email: alice@example.com
92
+ ```
93
+
94
+ ### Define a Contract (Python DSL)
95
+
96
+ ```python
97
+ from pactship import ContractBuilder, InteractionBuilder
98
+ from pactship import like, email_match, integer_match
99
+
100
+ contract = (
101
+ ContractBuilder("order-service", "user-api")
102
+ .add_interaction(
103
+ InteractionBuilder("Get user by ID")
104
+ .given("user 1 exists")
105
+ .with_request("GET", "/users/1")
106
+ .will_respond_with(200)
107
+ .with_response_body(
108
+ {"id": 1, "name": "Alice", "email": "alice@example.com"},
109
+ matchers={
110
+ "body.id": integer_match(),
111
+ "body.name": like("string"),
112
+ "body.email": email_match(),
113
+ },
114
+ )
115
+ .build()
116
+ )
117
+ .build()
118
+ )
119
+ ```
120
+
121
+ ### Verify Against a Provider
122
+
123
+ ```bash
124
+ pactship verify contract.yaml http://localhost:8080
125
+ ```
126
+
127
+ ### Detect Breaking Changes
128
+
129
+ ```bash
130
+ pactship diff old-contract.yaml new-contract.yaml
131
+ ```
132
+
133
+ ### Publish to Local Broker
134
+
135
+ ```bash
136
+ pactship publish contract.yaml --broker-dir .pactship
137
+ pactship list --broker-dir .pactship
138
+ ```
139
+
140
+ ## How It Works
141
+
142
+ ```
143
+ Define Verify Diff Report
144
+ ────────── ────────── ────────── ──────────
145
+ YAML/JSON → Provider → Version A → Breaking
146
+ or DSL Verification vs B changes
147
+ (async HTTP) classified
148
+ ```
149
+
150
+ 1. **Define** -- write contracts as YAML/JSON files or build them with the fluent Python DSL
151
+ 2. **Verify** -- run contracts against a live provider using async HTTP via `httpx`
152
+ 3. **Diff** -- compare two contract versions to detect breaking vs non-breaking changes
153
+ 4. **Report** -- get results in JSON, JUnit XML, Markdown, or TAP format
154
+
155
+ ## Features
156
+
157
+ ### Contract Definition
158
+ - **Fluent DSL** -- `ContractBuilder` and `InteractionBuilder` with method chaining
159
+ - **YAML and JSON** -- read and write contracts in both formats via `load_contract` / `save_contract`
160
+ - **Provider states** -- define preconditions with `.given("user 1 exists")`
161
+ - **Request/response specs** -- method, path, headers, query params, body, status code
162
+
163
+ ### Matcher System (16 Types)
164
+ - **Type matching** -- `like("string")` matches any string, `integer_match()` matches any int
165
+ - **Regex patterns** -- `regex(r"\d{3}-\d{4}")` for custom format validation
166
+ - **Structural matchers** -- `array_like(min_len)`, `each_like(example)` for arrays
167
+ - **Domain matchers** -- `email_match()`, `uuid_match()`, `iso_date()`, `iso_datetime()`
168
+ - **Constraint matchers** -- `range_match(0, 100)`, `any_of(["a", "b"])`, `nullable("string")`
169
+
170
+ ### Verification
171
+ - **Async HTTP verification** -- verify contracts against running providers using `httpx`
172
+ - **Mock provider** -- in-process mock for consumer-side testing without a real server
173
+ - **Request matching** -- method, path, headers, query params validated against spec
174
+ - **Matcher evaluation** -- response body verified against all declared matchers
175
+ - **Provider state setup** -- optional setup URL for test data preparation
176
+ - **Timeout configuration** -- per-verification timeout control
177
+
178
+ ### Breaking Change Detection
179
+ - **Contract diffing** -- `diff_contracts(old, new)` returns a structured diff report
180
+ - **Change classification** -- each change tagged as `breaking` or `non-breaking`
181
+ - **Interaction-level diff** -- detects added, removed, and modified interactions
182
+ - **Field-level diff** -- tracks changes to individual request/response fields
183
+
184
+ ### Local Broker
185
+ - **Filesystem-based storage** -- contracts stored as files in a configurable directory
186
+ - **Versioning** -- publish contracts with version numbers, retrieve specific versions
187
+ - **Verification history** -- track which provider versions were verified against which contracts
188
+ - **No server needed** -- everything runs locally, works offline, no network dependency
189
+
190
+ ### Contract Linting
191
+ - **REST best practices** -- validate path naming, HTTP method usage, status codes
192
+ - **Custom lint rules** -- extensible linting with severity-based issue reporting
193
+ - **Pre-publish validation** -- catch contract quality issues before sharing
194
+
195
+ ### OpenAPI Import
196
+ - **OpenAPI 3.x conversion** -- convert OpenAPI specs to pactship contracts automatically
197
+ - **Path and method extraction** -- generates interactions from OpenAPI path definitions
198
+ - **Response schema mapping** -- maps OpenAPI response schemas to pactship response specs
199
+
200
+ ### Code Generation
201
+ - **CRUD generator** -- `generate_crud_contract()` creates full CRUD contracts from resource specs
202
+ - **Endpoint generator** -- `generate_from_endpoints()` builds contracts from endpoint definitions
203
+ - **Customizable templates** -- configure generated interactions per HTTP method
204
+
205
+ ### Service Graph
206
+ - **Dependency visualization** -- build service dependency graphs from contract sets
207
+ - **Mermaid diagram output** -- generate Mermaid diagrams for documentation
208
+ - **Cycle detection** -- identify circular dependencies between services
209
+
210
+ ### Compatibility Matrix
211
+ - **Version tracking** -- `CompatibilityMatrix` tracks which consumer/provider versions work together
212
+ - **Matrix queries** -- check compatibility between specific version pairs
213
+ - **History management** -- add, query, and export compatibility records
214
+
215
+ ### Reporting
216
+ - **JSON reports** -- structured verification results as JSON
217
+ - **JUnit XML** -- integrate with CI systems expecting JUnit format
218
+ - **Markdown reports** -- human-readable reports for PR comments
219
+ - **TAP output** -- Test Anything Protocol for pipeline integration
220
+
221
+ ### Configuration
222
+ - **File-based config** -- `.pactship.yaml` or `.pactship.json` project configuration
223
+ - **Environment variables** -- `PACTSHIP_BROKER_DIR`, `PACTSHIP_TIMEOUT`, etc.
224
+ - **Priority ordering** -- env vars override file config, file config overrides defaults
225
+
226
+ ### Statistics
227
+ - **Method distribution** -- analyze HTTP method usage across contracts
228
+ - **Path coverage** -- track which API paths are covered by contracts
229
+ - **Complexity metrics** -- measure contract complexity and matcher density
230
+
231
+ ### Lifecycle Hooks
232
+ - **Before/after verification** -- run custom logic around verification cycles
233
+ - **Setup/teardown** -- provider state preparation and cleanup
234
+ - **Hook registration** -- register hooks via the `HookRegistry`
235
+
236
+ ### Contract Transformation
237
+ - **Path rewriting** -- transform contract paths for different environments
238
+ - **Header injection** -- add/modify headers across all interactions
239
+ - **Body transformation** -- apply transforms to request/response bodies
240
+
241
+ ### Filtering
242
+ - **Interaction filters** -- filter by HTTP method, path pattern, or description
243
+ - **Tag-based filtering** -- filter contracts by metadata tags
244
+ - **Composable filters** -- combine multiple filters with AND/OR logic
245
+
246
+ ## CLI Commands
247
+
248
+ ```bash
249
+ # Validate a contract file
250
+ pactship validate contract.yaml
251
+
252
+ # Verify against a live provider
253
+ pactship verify contract.yaml http://localhost:8080 \
254
+ --timeout 30 \
255
+ --header "Authorization:Bearer token" \
256
+ --setup-url http://localhost:8080/_setup \
257
+ --output report.json
258
+
259
+ # Diff two contract versions
260
+ pactship diff v1/contract.yaml v2/contract.yaml
261
+
262
+ # Publish to local broker
263
+ pactship publish contract.yaml \
264
+ --broker-dir .pactship \
265
+ --version 1.0.0 \
266
+ --tag production
267
+
268
+ # List contracts in broker
269
+ pactship list --broker-dir .pactship
270
+
271
+ # Convert between formats
272
+ pactship convert contract.yaml contract.json
273
+ ```
274
+
275
+ | Command | Description |
276
+ |---------|-------------|
277
+ | `pactship validate <file>` | Validate contract file syntax and structure |
278
+ | `pactship verify <file> <url>` | Verify contract against a running provider |
279
+ | `pactship diff <old> <new>` | Compare two contract versions for breaking changes |
280
+ | `pactship publish <file>` | Publish contract to local filesystem broker |
281
+ | `pactship list` | List all contracts stored in the broker |
282
+ | `pactship convert <in> <out>` | Convert between YAML and JSON formats |
283
+
284
+ ## Matchers
285
+
286
+ | Matcher | Description | Example |
287
+ |---------|-------------|---------|
288
+ | `exact(value)` | Exact value match | `exact("hello")` |
289
+ | `like(type)` | Type-based match | `like("string")` |
290
+ | `regex(pattern)` | Regex pattern match | `regex(r"\d{3}-\d{4}")` |
291
+ | `range_match(min, max)` | Numeric range constraint | `range_match(0, 100)` |
292
+ | `array_like(min_len)` | Array with minimum length | `array_like(1)` |
293
+ | `each_like(example)` | Each element matches structure | `each_like({"id": 0})` |
294
+ | `any_of(values)` | One of allowed values | `any_of(["a", "b"])` |
295
+ | `nullable(type)` | Null or specified type | `nullable("string")` |
296
+ | `iso_date()` | ISO 8601 date string | `iso_date()` |
297
+ | `iso_datetime()` | ISO 8601 datetime string | `iso_datetime()` |
298
+ | `uuid_match()` | UUID v4 format | `uuid_match()` |
299
+ | `email_match()` | Email address format | `email_match()` |
300
+ | `integer_match()` | Integer value | `integer_match()` |
301
+ | `decimal_match()` | Decimal number | `decimal_match()` |
302
+ | `boolean_match()` | Boolean value | `boolean_match()` |
303
+ | `string_match()` | String value | `string_match()` |
304
+
305
+ ## Programmatic API
306
+
307
+ ### Contract Building
308
+
309
+ ```python
310
+ from pactship import ContractBuilder, InteractionBuilder
311
+ from pactship import like, regex, integer_match, email_match
312
+
313
+ contract = (
314
+ ContractBuilder("order-service", "user-api")
315
+ .with_metadata({"version": "1.0.0"})
316
+ .add_interaction(
317
+ InteractionBuilder("Get user by ID")
318
+ .given("user 1 exists")
319
+ .with_request("GET", "/users/1")
320
+ .with_request_header("Accept", "application/json")
321
+ .will_respond_with(200)
322
+ .with_response_header("Content-Type", "application/json")
323
+ .with_response_body(
324
+ {"id": 1, "name": "Alice", "email": "alice@example.com"},
325
+ matchers={
326
+ "body.id": integer_match(),
327
+ "body.name": like("string"),
328
+ "body.email": email_match(),
329
+ },
330
+ )
331
+ .build()
332
+ )
333
+ .build()
334
+ )
335
+ ```
336
+
337
+ ### Contract I/O
338
+
339
+ ```python
340
+ from pactship import save_contract, load_contract
341
+
342
+ # Save to YAML or JSON (auto-detected from extension)
343
+ save_contract(contract, "contracts/user-api.yaml")
344
+
345
+ # Load from file
346
+ loaded = load_contract("contracts/user-api.yaml")
347
+ ```
348
+
349
+ ### Verification
350
+
351
+ ```python
352
+ from pactship import ProviderVerifier, MockProvider
353
+
354
+ # Verify against a live provider
355
+ verifier = ProviderVerifier(base_url="http://localhost:8080", timeout=30.0)
356
+ report = await verifier.verify(contract)
357
+ print(f"Passed: {report.success}")
358
+ for result in report.results:
359
+ print(f" {result.interaction}: {'PASS' if result.passed else 'FAIL'}")
360
+
361
+ # Use mock provider for consumer testing
362
+ mock = MockProvider(contract)
363
+ response = mock.handle_request("GET", "/users/1")
364
+ assert response.status == 200
365
+ ```
366
+
367
+ ### Breaking Change Detection
368
+
369
+ ```python
370
+ from pactship import diff_contracts
371
+
372
+ diff = diff_contracts(old_contract, new_contract)
373
+ print(f"Breaking changes: {diff.has_breaking_changes}")
374
+ for change in diff.changes:
375
+ print(f" [{change.change_type}] {change.description}")
376
+ ```
377
+
378
+ ### Local Broker
379
+
380
+ ```python
381
+ from pactship import ContractBroker
382
+
383
+ broker = ContractBroker(broker_dir=".pactship")
384
+ broker.publish(contract, version="1.0.0", tags=["production"])
385
+ contracts = broker.list_contracts()
386
+ specific = broker.get_contract("order-service", "user-api", version="1.0.0")
387
+ ```
388
+
389
+ ### OpenAPI Import
390
+
391
+ ```python
392
+ from pactship.openapi import openapi_to_contracts
393
+
394
+ contracts = openapi_to_contracts("openapi.yaml", consumer="my-service")
395
+ for contract in contracts:
396
+ save_contract(contract, f"contracts/{contract.provider}.yaml")
397
+ ```
398
+
399
+ ### Service Graph
400
+
401
+ ```python
402
+ from pactship import ServiceGraph
403
+
404
+ graph = ServiceGraph()
405
+ graph.add_contract(contract)
406
+ mermaid = graph.to_mermaid()
407
+ print(mermaid)
408
+ # graph TD
409
+ # order-service --> user-api
410
+ ```
411
+
412
+ ### Contract Linting
413
+
414
+ ```python
415
+ from pactship import lint_contract
416
+
417
+ result = lint_contract(contract)
418
+ print(f"Passed: {result.passed}")
419
+ for issue in result.issues:
420
+ print(f" [{issue.severity}] {issue.rule}: {issue.message}")
421
+ ```
422
+
423
+ ### Reporting
424
+
425
+ ```python
426
+ from pactship.reporting import (
427
+ report_json,
428
+ report_junit,
429
+ report_markdown,
430
+ report_tap,
431
+ )
432
+
433
+ # Generate reports in multiple formats
434
+ json_report = report_json(verification_report)
435
+ junit_xml = report_junit(verification_report)
436
+ markdown = report_markdown(verification_report)
437
+ tap_output = report_tap(verification_report)
438
+ ```
439
+
440
+ ### Statistics
441
+
442
+ ```python
443
+ from pactship.stats import contract_stats
444
+
445
+ stats = contract_stats(contract)
446
+ print(f"Methods: {stats['method_distribution']}")
447
+ print(f"Paths: {stats['path_count']}")
448
+ print(f"Matchers: {stats['matcher_count']}")
449
+ ```
450
+
451
+ ## Architecture
452
+
453
+ ```
454
+ pactship/
455
+ __init__.py # Public API exports (54 symbols)
456
+ models.py # Core data models (Contract, Interaction, Matcher, etc.)
457
+ dsl.py # Fluent builder DSL (ContractBuilder, InteractionBuilder)
458
+ matchers.py # 16 matcher types (exact, like, regex, range, etc.)
459
+ validator.py # Contract structure validation
460
+ verifier.py # Async HTTP provider verification + MockProvider
461
+ contract_io.py # YAML/JSON serialization and deserialization
462
+ schema.py # JSON Schema generation from contracts
463
+ diff.py # Contract version diffing with change classification
464
+ broker.py # Filesystem-based contract broker with versioning
465
+ cli.py # Click CLI (validate, verify, diff, publish, list, convert)
466
+ config.py # File + env var configuration loading
467
+ generator.py # CRUD and endpoint-based contract generation
468
+ graph.py # Service dependency graph with Mermaid output
469
+ matrix.py # Consumer/provider compatibility matrix
470
+ openapi.py # OpenAPI 3.x to pactship contract conversion
471
+ linter.py # Contract linting with REST best practice rules
472
+ reporting.py # Multi-format reports (JSON, JUnit, Markdown, TAP)
473
+ stats.py # Contract statistics and complexity metrics
474
+ hooks.py # Lifecycle hook registry (before/after verification)
475
+ transform.py # Contract transformation (paths, headers, bodies)
476
+ filters.py # Interaction filtering (method, path, tags)
477
+ ```
478
+
479
+ ### Data Flow
480
+
481
+ ```
482
+ ┌─────────────┐
483
+ │ YAML/JSON │
484
+ │ Contract │
485
+ └──────┬──────┘
486
+
487
+ ┌────────────┼────────────┐
488
+ │ │ │
489
+ ┌─────▼─────┐ ┌───▼───┐ ┌─────▼─────┐
490
+ │ Validator │ │ Diff │ │ Linter │
491
+ └─────┬─────┘ └───┬───┘ └─────┬─────┘
492
+ │ │ │
493
+ ┌─────▼─────┐ ┌───▼───┐ ┌─────▼─────┐
494
+ │ Verifier │ │Report │ │ Issues │
495
+ │(async HTTP)│ │ │ │ │
496
+ └─────┬─────┘ └───────┘ └───────────┘
497
+
498
+ ┌─────▼─────┐
499
+ │ Report │
500
+ │JSON/JUnit │
501
+ │ MD / TAP │
502
+ └───────────┘
503
+ ```
504
+
505
+ ## CI/CD Integration
506
+
507
+ ### GitHub Actions
508
+
509
+ ```yaml
510
+ name: Contract Tests
511
+ on: [push, pull_request]
512
+
513
+ jobs:
514
+ contracts:
515
+ runs-on: ubuntu-latest
516
+ steps:
517
+ - uses: actions/checkout@v4
518
+ - uses: actions/setup-python@v5
519
+ with:
520
+ python-version: "3.12"
521
+ - run: pip install pactship
522
+ - run: |
523
+ for f in contracts/*.yaml; do
524
+ pactship validate "$f"
525
+ done
526
+ - run: pactship diff contracts/v1.yaml contracts/v2.yaml || true
527
+ ```
528
+
529
+ ### Pre-commit Hook
530
+
531
+ ```bash
532
+ #!/bin/sh
533
+ for f in contracts/*.yaml; do
534
+ pactship validate "$f" || exit 1
535
+ done
536
+ ```
537
+
538
+ ## License
539
+
540
+ MIT