proxy-mock 2.13.0__tar.gz → 3.0.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 (80) hide show
  1. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/CHANGELOG.md +114 -0
  2. proxy_mock-3.0.0/MIGRATING.md +425 -0
  3. proxy_mock-3.0.0/PKG-INFO +703 -0
  4. proxy_mock-3.0.0/README.md +671 -0
  5. proxy_mock-3.0.0/examples/README.md +79 -0
  6. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/examples/compose/README.md +18 -15
  7. proxy_mock-3.0.0/proxy_mock/_server.py +15 -0
  8. proxy_mock-3.0.0/proxy_mock/api/errors.py +51 -0
  9. proxy_mock-3.0.0/proxy_mock/api/openapi.py +112 -0
  10. proxy_mock-3.0.0/proxy_mock/api/routes/admin.py +324 -0
  11. proxy_mock-3.0.0/proxy_mock/api/schemas.py +87 -0
  12. proxy_mock-3.0.0/proxy_mock/app/__init__.py +7 -0
  13. proxy_mock-3.0.0/proxy_mock/app/factory.py +157 -0
  14. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/cli.py +11 -2
  15. proxy_mock-3.0.0/proxy_mock/client/__init__.py +12 -0
  16. proxy_mock-3.0.0/proxy_mock/client/async_client.py +203 -0
  17. proxy_mock-3.0.0/proxy_mock/client/calls.py +154 -0
  18. proxy_mock-3.0.0/proxy_mock/client/client.py +124 -0
  19. proxy_mock-3.0.0/proxy_mock/client/migration.py +40 -0
  20. proxy_mock-3.0.0/proxy_mock/client/route.py +67 -0
  21. proxy_mock-3.0.0/proxy_mock/client/service_endpoints.py +14 -0
  22. proxy_mock-3.0.0/proxy_mock/client/transport.py +49 -0
  23. proxy_mock-3.0.0/proxy_mock/domain/models.py +187 -0
  24. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/pytest_plugin.py +7 -3
  25. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/repositories/mock_storage.py +8 -17
  26. proxy_mock-3.0.0/proxy_mock/services/mock_service.py +112 -0
  27. proxy_mock-3.0.0/proxy_mock/services/recordings.py +141 -0
  28. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/request_parser.py +2 -2
  29. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/rule_engine.py +22 -6
  30. proxy_mock-3.0.0/proxy_mock/services/sequences.py +59 -0
  31. proxy_mock-3.0.0/proxy_mock/services/snapshot.py +240 -0
  32. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/utils.py +49 -32
  33. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/pyproject.toml +6 -3
  34. proxy_mock-2.13.0/MIGRATING.md +0 -128
  35. proxy_mock-2.13.0/PKG-INFO +0 -425
  36. proxy_mock-2.13.0/README.md +0 -393
  37. proxy_mock-2.13.0/examples/README.md +0 -40
  38. proxy_mock-2.13.0/proxy_mock/api/routes/admin.py +0 -25
  39. proxy_mock-2.13.0/proxy_mock/api/routes/configure.py +0 -51
  40. proxy_mock-2.13.0/proxy_mock/api/routes/service.py +0 -25
  41. proxy_mock-2.13.0/proxy_mock/api/routes/storage.py +0 -88
  42. proxy_mock-2.13.0/proxy_mock/api/routes/traffic.py +0 -98
  43. proxy_mock-2.13.0/proxy_mock/api/schemas.py +0 -10
  44. proxy_mock-2.13.0/proxy_mock/app/__init__.py +0 -3
  45. proxy_mock-2.13.0/proxy_mock/app/factory.py +0 -122
  46. proxy_mock-2.13.0/proxy_mock/client/__init__.py +0 -4
  47. proxy_mock-2.13.0/proxy_mock/client/async_client.py +0 -287
  48. proxy_mock-2.13.0/proxy_mock/client/client.py +0 -220
  49. proxy_mock-2.13.0/proxy_mock/client/migration.py +0 -42
  50. proxy_mock-2.13.0/proxy_mock/client/route.py +0 -106
  51. proxy_mock-2.13.0/proxy_mock/client/service_endpoints.py +0 -15
  52. proxy_mock-2.13.0/proxy_mock/core/deprecation.py +0 -51
  53. proxy_mock-2.13.0/proxy_mock/domain/models.py +0 -95
  54. proxy_mock-2.13.0/proxy_mock/repositories/ttl_cache.py +0 -35
  55. proxy_mock-2.13.0/proxy_mock/services/cache_service.py +0 -46
  56. proxy_mock-2.13.0/proxy_mock/services/mock_service.py +0 -106
  57. proxy_mock-2.13.0/proxy_mock/services/snapshot.py +0 -153
  58. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/.gitignore +0 -0
  59. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/LICENSE +0 -0
  60. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/__init__.py +0 -0
  61. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/__main__.py +0 -0
  62. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/any_catcher.py +0 -0
  63. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/api/__init__.py +0 -0
  64. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/api/deps.py +0 -0
  65. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/api/routes/__init__.py +0 -0
  66. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/app/state.py +0 -0
  67. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/__init__.py +0 -0
  68. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/logging.py +0 -0
  69. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/serializers.py +0 -0
  70. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/settings.py +0 -0
  71. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/time.py +0 -0
  72. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/core/urls.py +0 -0
  73. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/domain/__init__.py +0 -0
  74. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/domain/constants.py +0 -0
  75. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/repositories/__init__.py +0 -0
  76. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/repositories/traffic_store.py +0 -0
  77. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/__init__.py +0 -0
  78. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/proxy_service.py +0 -0
  79. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/response_factory.py +0 -0
  80. {proxy_mock-2.13.0 → proxy_mock-3.0.0}/proxy_mock/services/traffic_service.py +0 -0
@@ -1,3 +1,117 @@
1
+ Version 3.0.0
2
+ =============
3
+
4
+ Released on 2026-10-02.
5
+
6
+ 3.0 is the single breaking release announced in 2.13. Breaking changes are listed first; the
7
+ upgrade path for each of them is in [MIGRATING.md](MIGRATING.md). Projects that need the 2.x
8
+ API can pin `proxy_mock>=2.13,<3`.
9
+
10
+ Breaking changes
11
+ ----------------
12
+
13
+ * **Breaking: administrative REST API.** The configurable administrative namespace defaults
14
+ to `/__admin` and includes docs and OpenAPI. Legacy service routes and `POST` aliases are
15
+ removed; their former paths are available to user mocks. The whole namespace is protected
16
+ from mock shadowing, including unknown administrative paths.
17
+ * Individual mocks use `/__admin/mocks?path=<encoded-path>`. `PUT` creates (`201`) or replaces
18
+ (`200`); `PATCH` updates an existing mock (`200`) and preserves omitted fields. Explicit null
19
+ clears nullable fields, nested response properties merge, and arrays replace in full.
20
+ Missing item reads, patches and deletes return `404`. Administrative errors share
21
+ `{"success": false, "error": {"code": "...", "message": "...", "details": ...}}`, with
22
+ optional details. Invalid msgpack fields, including arbitrary binary values and mixed key
23
+ types, return these structured validation errors without changing existing configuration.
24
+ * Snapshots export format 2 and import formats 1 and 2. `GET /__admin/snapshot` exports;
25
+ `PUT` replaces and `PATCH` merges complete mocks by path. Traffic uses `GET` and
26
+ `DELETE /__admin/traffic`; recording settings use `GET` and `PATCH /__admin/settings`.
27
+ Invalid traffic filters return `422`.
28
+ * The Python clients and pytest fixtures use the administrative API.
29
+
30
+ * **Breaking: removed response caching.** The cache resource, `cache_time`, `clean_cache()`,
31
+ response-cache implementation and TTL storage are removed. Client helpers reject the old
32
+ argument locally; HTTP configuration rejects it with `422`. Static delays apply on every
33
+ request, and ordinary mock/rule proxying fetches the upstream every time. Saved upstream
34
+ replies use explicit record/replay.
35
+ * Legacy snapshot imports discard disabled null/zero cache settings and reject enabled or
36
+ invalid settings atomically with a migration hint. New storage and exports omit the field.
37
+
38
+ * **Breaking: unified Python transports.** `ProxyMock.execute_request()` now returns
39
+ `httpx2.Response`, matching `AsyncProxyMock`. Replace `.ok` / response truth testing with
40
+ explicit status checks, `allow_redirects` with `follow_redirects`, raw `data=` with
41
+ `content=`, and requests session/adapters with native httpx2 clients. Wrapper-created
42
+ clients do not follow redirects by default; both use a 10-second per-operation timeout.
43
+ * Both clients wrap every httpx2 `RequestError`, preserving its cause. HTTP response errors
44
+ expose `.response`; the wrapper's `raise_for_status=True` still checks >=400. All four
45
+ wrapper error classes are exported from `proxy_mock.client`, with shared base classes.
46
+ * Added `http_client=` injection and `.http_client` access for native transport settings.
47
+ Context exit closes owned clients, while injected clients remain caller-owned. Request
48
+ timeouts override the wrapper constructor timeout, including `None` to disable them.
49
+ * Removed requests and its exclusive dependencies from the package and lockfile.
50
+
51
+ * **Breaking: optional server dependencies.** The base `proxy_mock` install depends only on
52
+ httpx2 and msgpack. Use `proxy_mock[server]` for local server startup, the app factory and
53
+ local pytest fixtures; FastAPI, Pydantic and uvicorn retain their existing bounds.
54
+ * Base-only environments support both clients and pytest fixtures using `PROXY_MOCK_URL`.
55
+ CLI help/version do not import server packages; local startup reports how to install the
56
+ extra, with exit code 2 and no traceback. Server code still ships in the same wheel.
57
+
58
+ * **Breaking: removed the deprecated `clean_storage(path=...)` selector** from both clients.
59
+ Use `delete_mock(path)` for one mock or `clean_storage()` for the entire collection.
60
+ Old keyword/positional selectors raise `TypeError` before any data is deleted.
61
+
62
+ New features
63
+ ------------
64
+
65
+ * Added ordered response sequences to mocks and rules. Each entry has its own response body,
66
+ status and headers. Exhaustion repeats the final response by default; the `error` policy
67
+ returns `409`. Reservations are atomic and happen before delays.
68
+ * Added `GET` and `PATCH /__admin/sequence-state` and matching sync/async client helpers to
69
+ inspect and restart a cursor. Unrelated PATCH updates preserve positions; explicit sequence
70
+ or rule replacement restarts the corresponding cursors. Full mock replacement resets all.
71
+ * Sequence configuration rejects incompatible proxy settings on the mock and on the rule.
72
+ Format 1 imports containing sequences return `422`; format 2 preserves their definitions
73
+ and binary responses.
74
+
75
+ * Added explicit mock-level record/replay. Record mode proxies every request and stores the
76
+ last completed HTTP reply per exact method/path/query/body key, with optional selected
77
+ headers. Replay returns the saved reply without upstream access; missing entries return
78
+ `404`. Transport failures are not recorded, while upstream HTTP errors are.
79
+ * Added `GET` and `DELETE /__admin/recordings` with required mock path and optional entry id,
80
+ plus both client helpers. Binary bodies, repeated response headers, redirects, decompressed
81
+ payloads and HEAD representation lengths are supported. An absent HEAD length stays absent
82
+ on replay, and a compressed HEAD length is omitted together with its content encoding.
83
+ * Recording collections have configurable entry and byte limits. Late responses cannot
84
+ restore deleted recordings or mutate a replaced mock generation. Snapshot format 1 rejects
85
+ record/replay explicitly; format 2 preserves completed recordings.
86
+
87
+ * Added snapshot format 2 with mock/rule sequence definitions and completed recording entries,
88
+ preserving binary bodies, repeated header pairs, matching keys and eviction order. Export
89
+ is observational; imported sequence cursors restart at zero. Traffic and pending
90
+ requests are excluded. Format 1 remains accepted for legacy mock configuration.
91
+ * Snapshot validation checks the whole document, including recording integrity, duplicate ids,
92
+ header/body consistency and configured storage limits, before mutating state. Invalid data
93
+ is rejected rather than trimmed. Merge replaces supplied mocks and recordings in full.
94
+ * CLI preload and both clients restore format 2; OpenAPI documents both accepted formats and
95
+ the format 2 export. Ambiguous config bodies containing both body and body_b64 are rejected.
96
+
97
+ Fixes and documentation
98
+ -----------------------
99
+
100
+ * **Fixed:** each application created by `create_app()` owns its mock storage alongside
101
+ routes, recordings and sequence cursors. In 2.x the mock storage was shared by the process,
102
+ so an embedded second application saw and could clear the first one's mocks. Recreating an
103
+ application now starts empty.
104
+ * Rewrote the HTTP reference for the administrative REST API and added a migration guide
105
+ section for every breaking change. The 2.13 alias behaviour is kept as a separate reference.
106
+ * Added runnable REST lifecycle, binary sequence, offline recording snapshot and async-client
107
+ examples. The storefront scripts accept local URLs and a custom administrative prefix, so
108
+ their HTTP behavior is tested without Docker as well as through the Compose CI check.
109
+ * The release workflow no longer publishes a container image to GHCR: the registry package was
110
+ never publicly readable. Build the image from the repository as described in the README.
111
+ * Development instructions, Docker and server CI checks select the `server` extra explicitly.
112
+ Added isolated base/server wheel checks, including clients and fixtures against an external
113
+ server.
114
+
1
115
  Version 2.13.0
2
116
  ==============
3
117
 
@@ -0,0 +1,425 @@
1
+ # Migrating from 2.x to 3.0
2
+
3
+ 3.0 is a breaking release. It replaces the administrative endpoints with a resource-oriented
4
+ REST API, adds response sequences and record/replay, removes response caching and moves both
5
+ Python clients to `httpx2`. 2.13 keeps the old routes, status codes, response bodies and client
6
+ transports; see [the 2.13 reference](#released-213-compatibility-reference) to stay on 2.x for now.
7
+
8
+ Both clients now use `httpx2`. The base install contains clients; the optional `server` extra
9
+ provides dependencies for a local server as described below.
10
+
11
+ ## Upgrade checklist
12
+
13
+ 1. Upgrade clients and server together; a 2.x client is not HTTP-compatible with a 3.0 server,
14
+ even when both use `/__admin`.
15
+ 2. Install the base package for an external server, or the `server` extra for local startup.
16
+ Match the administrative prefix in both environments.
17
+ 3. Move handwritten HTTP calls to the resource table below. Review replacement versus partial
18
+ update, missing-item errors, and administrative error envelopes.
19
+ 4. Update Python response checks, request body arguments, redirects, timeouts and native client
20
+ customization using the transport section below.
21
+ 5. Remove caching calls and fields. Choose explicit record/replay for saved upstream replies;
22
+ ordinary proxying fetches every time.
23
+ 6. Keep a copy of existing format 1 snapshots. Import is supported, but new format 2 exports
24
+ cannot be loaded by a 2.x server. Sequence cursors restart on import.
25
+ 7. Run the [3.0 examples](examples/README.md) against a dedicated instance; pytest fixture
26
+ cleanup clears its mocks and traffic.
27
+
28
+ ## Administrative REST API
29
+
30
+ The server and both Python clients now default to `/__admin`. Set `PROXY_MOCK_ADMIN_PREFIX` on
31
+ the server and pass the same `admin_prefix` to clients to change it. The pytest fixtures read
32
+ the environment setting for both local and externally configured servers. An unset value uses
33
+ `/__admin`; an empty value is invalid. Prefixes use absolute paths of letters, digits,
34
+ underscores, hyphens and optional path segments, without a trailing slash.
35
+
36
+ The whole administrative namespace is reserved, including unknown children. User mocks cannot
37
+ shadow it. Legacy service paths are removed and become ordinary mock paths: `/storage`,
38
+ `/traffic`, `/configure_mock`, `/docs`, `/redoc` and `/openapi.json` no longer provide service
39
+ operations. A prefix provides routing isolation, not authentication.
40
+
41
+ | Operation in 2.x | Resource and method in 3.0 |
42
+ |-----------------|----------------------------|
43
+ | `GET /proxy_mock` | `GET /__admin` |
44
+ | `POST /configure_mock` | `PUT /__admin/mocks?path=<encoded-path>` |
45
+ | `PATCH /configure_mock` | `PATCH /__admin/mocks?path=<encoded-path>` |
46
+ | `GET /storage` | `GET /__admin/mocks` |
47
+ | `GET /storage?path=...` | `GET /__admin/mocks?path=<encoded-path>` |
48
+ | `DELETE /storage`, `POST /storage/clean` without a path | `DELETE /__admin/mocks` |
49
+ | Delete a specific mock | `DELETE /__admin/mocks?path=<encoded-path>` |
50
+ | `GET /traffic` | `GET /__admin/traffic` |
51
+ | `DELETE /traffic`, `POST /traffic/clean` | `DELETE /__admin/traffic` |
52
+ | `GET /traffic/settings` | `GET /__admin/settings` |
53
+ | `PATCH /traffic/settings`, `POST /traffic/settings` | `PATCH /__admin/settings` |
54
+ | `GET /storage/snapshot` | `GET /__admin/snapshot` |
55
+ | `POST /storage/snapshot?mode=replace` | `PUT /__admin/snapshot` |
56
+ | `POST /storage/snapshot?mode=merge` | `PATCH /__admin/snapshot` |
57
+ | `POST /cache/clean` | Removed; no administrative cache resource |
58
+ | `GET /docs`, `/redoc`, `/openapi.json` | `GET /__admin/docs`, `/__admin/redoc`, `/__admin/openapi.json` |
59
+
60
+ There are no action-style `POST` aliases. A query is part of the resource URI, so
61
+ `/__admin/mocks?path=%2Finventory` identifies the configuration for `/inventory` without
62
+ mixing its potentially nested or encoded request path into the administrative path hierarchy.
63
+ Use a client's query encoder rather than concatenating arbitrary paths into a URL.
64
+
65
+ ### Mock creation, replacement and partial updates
66
+
67
+ The mock path moves to the required query parameter on `PUT` and `PATCH`. A body `path` is
68
+ optional and, when supplied, must match. `GET` and `DELETE` without a query address the whole
69
+ collection; `?path=` is invalid and never means all mocks.
70
+
71
+ Before, in 2.x:
72
+
73
+ ```sh
74
+ curl -X POST http://localhost:5000/configure_mock \
75
+ -H 'Content-Type: application/json' \
76
+ -d '{"path":"/inventory","mock_data":{"body":{"available":3}}}'
77
+ ```
78
+
79
+ After, in 3.0:
80
+
81
+ ```sh
82
+ curl -X PUT 'http://localhost:5000/__admin/mocks?path=%2Finventory' \
83
+ -H 'Content-Type: application/json' \
84
+ -d '{"mock_data":{"body":{"available":3}}}'
85
+ ```
86
+
87
+ `PUT` creates the mock with `201` or replaces it with `200`. Repeating it produces the same
88
+ configuration, and omitted properties return to defaults. `PATCH` returns `200` only for an
89
+ existing mock and preserves omitted properties. It merges supplied `mock_data` properties,
90
+ while replacing a supplied response body or array as a whole. It is not JSON Merge Patch.
91
+ Explicit `null` clears nullable fields; non-nullable fields reject null. For example:
92
+
93
+ ```sh
94
+ curl -X PATCH 'http://localhost:5000/__admin/mocks?path=%2Finventory' \
95
+ -H 'Content-Type: application/json' \
96
+ -d '{"proxy_host":null,"mock_data":{"body":null,"status_code":204},"rules":[]}'
97
+ ```
98
+
99
+ This clears the proxy target and response body, changes the status and removes every rule,
100
+ while retaining response headers and other omitted fields. JSON and msgpack mock bodies remain
101
+ supported. Missing item reads, patches and deletes return `404`; a repeated deletion therefore
102
+ returns `404` after its first successful `200`, while remaining idempotent in its effect.
103
+
104
+ ### Responses and validation
105
+
106
+ Successful responses retain their operation-specific `success`/data envelopes; snapshot
107
+ export still returns the snapshot document directly. Administrative errors now use:
108
+
109
+ ```json
110
+ {"success": false, "error": {"code": "mock_not_found", "message": "No mock found for /inventory"}}
111
+ ```
112
+
113
+ `error.details` is optional. Update callers that expected a string or list in `error`, a
114
+ successful empty result for a missing mock, or `201` for replacement. Invalid representations,
115
+ empty paths, body/query path mismatches and invalid traffic filters return `422`. Malformed
116
+ JSON/msgpack returns `400`; unsupported mock body content types return `415`. Unsupported
117
+ administrative methods on known resources return `405`. These errors do not change user-defined mock responses.
118
+
119
+ ### Traffic, settings and snapshots
120
+
121
+ Traffic reads accept `path`, `method` and `limit`. Deletion clears all traffic and accepts no
122
+ query parameters; supplying a filter returns `422` without deleting records. Settings remain a partial update
123
+ of `record_unknown_traffic` and/or positive `max_items` through `PATCH /__admin/settings`.
124
+
125
+ Snapshots export format 2; imports and CLI preload accept formats 1 and 2, including binary
126
+ `body_b64`. Format 2 adds sequence definitions and completed recordings. Use `PUT` to replace
127
+ storage or `PATCH` to merge complete mocks keyed by path. Snapshot PATCH is a custom merge:
128
+ it replaces every included mock in full, keeps other paths, and is not RFC 7396 JSON Merge
129
+ Patch. Both methods accept the exported snapshot document. Invalid imports leave storage
130
+ unchanged. The old `POST` and `mode` query switch are removed.
131
+
132
+ ```sh
133
+ curl http://localhost:5000/__admin/snapshot > mocks.json
134
+ curl -X PUT http://localhost:5000/__admin/snapshot \
135
+ -H 'Content-Type: application/json' --data-binary @mocks.json
136
+ ```
137
+
138
+ ### Python clients
139
+
140
+ Use 3.0 clients with a 3.0 server. The public convenience methods select
141
+ the new resource methods; `configure_mock()` uses PUT and `patch_mock()` uses PATCH.
142
+ `import_mocks(..., mode="replace")` uses PUT and `mode="merge"` uses PATCH. Generic
143
+ `execute_request()` calls still use the exact route you provide, so migrate any handwritten
144
+ administrative requests. 2.13 clients use a different HTTP contract even when their
145
+ opt-in prefix is `/__admin`.
146
+
147
+ Both clients return `httpx2.Response`; migrate request arguments and response handling
148
+ as described below. Response caching is removed; local startup requires the `server` extra.
149
+ `clean_storage()` deletes the whole collection. The deprecated `path` argument is removed
150
+ and raises `TypeError`, including explicit null; use `delete_mock(path)` for one mock.
151
+
152
+ ## Record/replay
153
+
154
+ Record/replay is explicit: configure `recording: {"mode": "record"}` with a mock-level
155
+ `proxy_host`, then switch with `PATCH` to `recording: {"mode": "replay"}`. This preserves
156
+ completed replies while upstream and `match_headers` remain unchanged. Repeat non-default
157
+ recording settings when switching; the object is replaced in full. Both clients accept
158
+ `recording=...`, `get_recordings()` and `delete_recordings()`.
159
+
160
+ `GET` and `DELETE /__admin/recordings?path=...` inspect and clear a mock's recorded replies;
161
+ an optional `id` selects one entry. Modes belong to the mock resource rather than action
162
+ endpoints. Matching uses method, raw path/query and exact body bytes, optionally selected
163
+ headers. Replay does not contact the upstream and returns `404` on a missing key. Completed
164
+ HTTP errors are recorded; transport errors are not. Collections have configurable count and
165
+ byte limits, and record-mode responses report whether storage succeeded through
166
+ `X-Proxy-Mock-Recording`. See the [full contract](README.md#recordreplay), including lifecycle,
167
+ concurrent requests, binary bodies and repeated headers.
168
+
169
+ Snapshot format 2 preserves recording configuration and completed replies, including binary
170
+ bodies, repeated header pairs and eviction order. Invalid keys or collections exceeding their
171
+ configured limits return `422` before mutation. Format 1 has no recording representation and
172
+ rejects this data. 2.13 has no record/replay API.
173
+
174
+ ## Response sequences
175
+
176
+ Mocks and rules accept `sequence: {"responses": [...], "on_exhaustion": "repeat_last"}`.
177
+ The default repeats the final response; `on_exhaustion: "error"` returns `409` when all entries
178
+ have been consumed. Existing static mocks and rule matching keep their behavior when no
179
+ sequence is configured. See [the sequence contract](README.md#response-sequences) for examples.
180
+
181
+ Inspect cursors with `GET /__admin/sequence-state?path=...`; reset one with `PATCH` and
182
+ `{"position": 0}`. Add `rule=<zero-based-index>` to select a rule cursor. Both clients provide
183
+ `get_sequence_state()` and `reset_sequence()`. Full mock replacement starts fresh; unrelated
184
+ partial updates keep cursor positions. Requests already assigned a response keep it across
185
+ reset, reconfiguration or deletion.
186
+
187
+ Format 2 exports sequence definitions and binary responses; imported cursors start at zero.
188
+ Export does not change positions. Format 1 imports with sequences return `422` atomically.
189
+ Ordinary format 1 snapshots remain supported. A 2.x server cannot read format 2; do not
190
+ relabel its envelope as format 1. Traffic and in-flight requests are excluded.
191
+ Snapshot merge replaces supplied mocks and recordings completely while preserving omitted
192
+ mocks. Missing recording arrays mean empty collections. The CLI restores format 2 at startup.
193
+ See the [snapshot contract](README.md#snapshots-of-the-storage).
194
+
195
+ ## Python client transport
196
+
197
+ Both `ProxyMock` and `AsyncProxyMock` now use `httpx2` and return its buffered `Response`
198
+ from `execute_request()`. `requests`, `charset-normalizer` and `urllib3` are no longer
199
+ installed by proxy-mock. Early supported httpx2 versions can still depend on `certifi`;
200
+ it is absent from the current lockfile. 2.13 retains `requests.Session` and
201
+ `requests.Response` for the synchronous client.
202
+
203
+ ### Response handling and request arguments
204
+
205
+ Replace `.ok` and response truth testing with an explicit status check. `httpx2.Response`
206
+ is always truthy, including HTTP errors; `.is_success` is true only for 200–299. If your
207
+ old `.ok` check accepted redirects, use `response.status_code < 400` instead.
208
+
209
+ Before (2.x sync client):
210
+
211
+ ```python
212
+ with ProxyMock(url) as client:
213
+ response = client.execute_request("POST", "/upload", data=b"raw", allow_redirects=False)
214
+ assert response.ok
215
+ cookies = response.raw.headers.getlist("Set-Cookie")
216
+ ```
217
+
218
+ After (3.0 sync client):
219
+
220
+ ```python
221
+ with ProxyMock(url) as client:
222
+ response = client.execute_request("POST", "/upload", content=b"raw", follow_redirects=False)
223
+ assert response.is_success
224
+ cookies = response.headers.get_list("Set-Cookie")
225
+ ```
226
+
227
+ The async client accepts the same arguments using `await`. Request options follow the
228
+ native `httpx2` request API: `content=` sends raw bytes or text, `json=` serializes JSON,
229
+ `data=` accepts a form mapping, and `files=` uploads multipart data. Raw text or bytes in
230
+ `data=` raise `TypeError`. Replace `allow_redirects` with `follow_redirects`. `stream=` is
231
+ unsupported: wrapper calls read the complete response. Use the native client's streaming
232
+ API when needed. Use `response.headers.raw` for original header bytes; convenience header
233
+ accessors may decode UTF-8 rather than Latin-1.
234
+
235
+ JSON, forms and multipart encoding can differ from requests. Recording keys include exact
236
+ request bytes: reuse explicit `content=` when byte identity matters, or capture a new
237
+ recording after migrating the request encoding. Generic relative routes append to the
238
+ wrapper host's base path, preserving encoded paths, raw query strings and trailing slashes;
239
+ absolute request URLs override the host.
240
+
241
+ ### Defaults and exceptions
242
+
243
+ Clients created by the wrapper do not follow redirects by default. Set `follow_redirects=True`
244
+ per request to follow them. The constructor timeout defaults to 10 seconds for each connect,
245
+ read, write and pool operation, not a total deadline. Pass a number, `httpx2.Timeout` or
246
+ `None` (disable timeouts); a per-request `timeout=` overrides the constructor value.
247
+ Native TLS verification and environment proxy settings remain enabled by default.
248
+
249
+ HTTP errors return a response by default. `execute_request(..., raise_for_status=True)`
250
+ raises `ProxyMockResponseError` for status codes >=400, or `AsyncProxyMockResponseError`
251
+ for async calls; the exception exposes `.response`. Redirects do not raise through this
252
+ flag. Calling the native `response.raise_for_status()` instead raises
253
+ `httpx2.HTTPStatusError` for any non-2xx status, including redirects.
254
+
255
+ All native `httpx2.RequestError` failures are wrapped in `ProxyMockRequestError` or
256
+ `AsyncProxyMockRequestError`, with the original exception in `__cause__`. This includes
257
+ connection, read/write, timeout, protocol, decoding and redirect-limit errors. Native
258
+ `httpx2.InvalidURL` and argument errors such as `TypeError` propagate unchanged. All four
259
+ wrapper exception classes are exported from `proxy_mock.client`; the async error classes
260
+ also subclass the corresponding sync error classes, so shared callers can catch the common
261
+ base classes. Their existing module import locations remain available.
262
+
263
+ ### Custom native clients and ownership
264
+
265
+ Replace `.session` access and requests adapters with an `httpx2.Client` or `AsyncClient`.
266
+ Pass it as `http_client=` to preserve configured headers, cookies, authentication, TLS,
267
+ proxy, transport and event hooks. The wrapper exposes it through `.http_client` and always
268
+ uses its own host for relative routes, regardless of the native client's `base_url`.
269
+
270
+ ```python
271
+ import httpx2
272
+ from proxy_mock.client import ProxyMock
273
+
274
+ with httpx2.Client(headers={"X-Test": "yes"}, trust_env=False) as http:
275
+ with ProxyMock(url, http_client=http, timeout=5.0) as client:
276
+ response = client.execute_request("GET", "/item")
277
+ assert response.is_success
278
+ # The borrowed native client is still open here.
279
+ ```
280
+
281
+ For async use, nest `async with httpx2.AsyncClient(...) as http` and
282
+ `async with AsyncProxyMock(url, http_client=http) as client`. The injected client's
283
+ `follow_redirects` default is honored, while the wrapper's constructor timeout applies
284
+ unless overridden per request. A wrapper-created native client is closed by `close()` /
285
+ `aclose()` or context exit, including exceptional exit. An injected client is borrowed:
286
+ the caller closes it. Passing the wrong native client type raises `TypeError`.
287
+
288
+ ## Installation and startup
289
+
290
+ The base install, `proxy_mock`, depends only on `httpx2` and `msgpack`. Server source files
291
+ still ship in the same wheel, but FastAPI, Pydantic and uvicorn move to `proxy_mock[server]`.
292
+ Their version bounds are unchanged. Use the extra to run the server, embed `create_app()`,
293
+ validate snapshots with the CLI or start a local instance through pytest fixtures.
294
+
295
+ ```sh
296
+ python -m pip install proxy_mock # clients talking to an existing server
297
+ python -m pip install 'proxy_mock[server]' # local server
298
+ ```
299
+
300
+ 2.13 has no server extra and installs server dependencies by default. For the CLI in an
301
+ isolated environment, use `uvx --from 'proxy_mock[server]' proxy-mock` or
302
+ `pipx run --spec 'proxy_mock[server]' proxy-mock`. Development and tests of proxy-mock itself
303
+ use `uv sync --locked --extra server`.
304
+
305
+ A base-only environment supports both clients, snapshot export/import against a running
306
+ server, pytest auto-loading and fixtures using `PROXY_MOCK_URL`. It does not need server
307
+ packages even if unrelated pytest tests never use the plugin. `proxy-mock --help` and
308
+ `--version` (also through `python -m proxy_mock`) work without the extra. Attempting local
309
+ startup, importing the app factory or using local pytest fixtures gives an installation hint
310
+ for `proxy_mock[server]`. The CLI exits with code 2 without a traceback. Installing an extra
311
+ resolves dependencies; it does not change HTTP routes or choose a different wheel.
312
+
313
+ `make install` includes the extra. When using uv directly, include `--extra server` in both
314
+ `uv sync` and `uv run` commands for server work; plain `uv run` can remove optional packages
315
+ from the project environment. Docker and the server checks in CI select the extra explicitly.
316
+
317
+ Switch existing uvicorn entry points from `proxy_mock.any_catcher:app` to the supported factory
318
+ `uvicorn proxy_mock.app:create_app --factory`, or use the `proxy-mock` console command already
319
+ available in 2.x. Only one worker is supported because storage is in process memory.
320
+
321
+ ## Removed response caching
322
+
323
+ `cache_time`, `clean_cache()` and both the legacy and administrative cache resources are
324
+ removed. Passing the field to either client's configure/patch helper raises `TypeError`
325
+ locally, and direct HTTP mock configuration returns `422` even for null or zero. Calling
326
+ `clean_cache()` raises `AttributeError`; `/__admin/cache` returns `404` and remains protected
327
+ from user-mock shadowing. Remove cleanup calls and the field from your tests.
328
+
329
+ Static mock replies stay available until reconfiguration or deletion, and delays apply to
330
+ every request. Ordinary proxying reaches the upstream on each request. Use explicit
331
+ [record/replay](README.md#recordreplay) to capture and serve upstream replies.
332
+
333
+ For migration, snapshot imports accept and discard disabled legacy cache values (`null` or
334
+ integer zero). Enabled or invalid cache values return `422` without changing configuration,
335
+ recordings or cursors. Remove the field from those files or migrate the scenario explicitly;
336
+ import never contacts an upstream to populate recordings. New exports omit the field.
337
+ 2.13 retains its caching API and transport behavior.
338
+
339
+ gRPC and an authentication token are outside the 3.0 scope.
340
+
341
+ ## Released 2.13 compatibility reference
342
+
343
+ The following sections describe 2.13 only, for projects that stay on 2.x for now.
344
+
345
+ ### Stay on 2.x until you are ready
346
+
347
+ Pin `proxy_mock>=2.13,<3` in a test project that needs the existing contract. Upgrade the server
348
+ first within the 2.x line, then the clients. A 2.13 client without `admin_prefix` still uses the
349
+ legacy paths and can talk to an older 2.x server. Keep `requests` behaviour until you have checked your callers.
350
+ Already installed old versions do not acquire warnings remotely: read release notes and update
351
+ to 2.13 to see these notices. There is no network update check or telemetry.
352
+
353
+ ### Try the administrative aliases in 2.13
354
+
355
+ Aliases are **opt-in** so that an existing mock at `/__admin` is not unexpectedly shadowed.
356
+ Start the server with:
357
+
358
+ ```sh
359
+ PROXY_MOCK_ADMIN_PREFIX=/__admin proxy-mock --host 127.0.0.1 --port 5000
360
+ ```
361
+
362
+ Use the same prefix explicitly in either client:
363
+
364
+ ```python
365
+ from proxy_mock.client import ProxyMock, AsyncProxyMock
366
+
367
+ with ProxyMock("http://127.0.0.1:5000", admin_prefix="/__admin") as client:
368
+ client.configure_mock(path="/inventory", body={"available": True})
369
+ assert client.execute_request("GET", "/inventory").json()["available"]
370
+
371
+ # In async code:
372
+ # async with AsyncProxyMock("http://127.0.0.1:5000", admin_prefix="/__admin") as client:
373
+ # await client.get_storage()
374
+ ```
375
+
376
+ The prefix must be an absolute path of letters, digits, underscores, hyphens and optional
377
+ path segments, without a trailing slash. It must not overlap an existing service path such as
378
+ `/storage`, `/traffic`, `/docs` or their children. A prefix is not authentication. Reserve its
379
+ administrative paths for the server and choose a prefix that does not collide with your mocks.
380
+ Leaving the environment variable unset disables aliases; an explicitly empty value is invalid.
381
+ Generic `execute_request()` calls always use the route you provide, without rewriting mock URLs.
382
+ The pytest fixture continues using the legacy routes in 2.13; construct a client explicitly to
383
+ exercise the aliases.
384
+
385
+ | Legacy operation | Released 2.13 opt-in alias |
386
+ | --- | --- |
387
+ | `GET /proxy_mock` | `GET /__admin` |
388
+ | `POST /configure_mock` | `POST /__admin/mocks` |
389
+ | `PATCH /configure_mock` | `PATCH /__admin/mocks` |
390
+ | `GET /storage` | `GET /__admin/mocks` |
391
+ | `DELETE /storage` | `DELETE /__admin/mocks` |
392
+ | `POST /storage/clean` | `DELETE /__admin/mocks` |
393
+ | `GET /traffic` | `GET /__admin/traffic` |
394
+ | `DELETE /traffic`, `POST /traffic/clean` | `DELETE /__admin/traffic` |
395
+ | `GET /traffic/settings` | `GET /__admin/settings` |
396
+ | `PATCH /traffic/settings`, `POST /traffic/settings` | `PATCH /__admin/settings` |
397
+ | `GET /storage/snapshot` | `GET /__admin/snapshot` |
398
+ | `POST /storage/snapshot` | `POST /__admin/snapshot` |
399
+ | `POST /cache/clean` | No alias; the legacy cache endpoint remains |
400
+
401
+ In 2.13 aliases keep the existing JSON/msgpack bodies, query parameters and status codes.
402
+ `?path=...`, traffic filters and snapshot `?mode=merge|replace` work unchanged. These aliases do not implement the new
403
+ PUT/PATCH contract above. Old paths remain available even when aliases are enabled in 2.13.
404
+
405
+ `clean_storage(path=...)` still calls its legacy endpoint, even with `admin_prefix`, because
406
+ it returns `200` with `success: false` for a missing mock. Switch to `delete_mock(path)`, which
407
+ returns `404` when the mock is missing. `clean_cache()` also stays on its legacy path in 2.13.
408
+
409
+ ### Understand the 2.13 warnings
410
+
411
+ Every matched legacy service operation returns `Deprecation: @1789603200` (the announcement
412
+ date, 2026-09-17 UTC) and a `Link` with `rel="deprecation"` pointing to this guide. The date
413
+ syntax follows [RFC 9745](https://www.rfc-editor.org/rfc/rfc9745.html); it replaces the old
414
+ non-standard `Deprecation: true` value. When aliases are enabled, another link with
415
+ `rel="successor-version"` points to the replacement resource. Consult the table for the HTTP
416
+ method: a URI alone does not tell you to change `POST` to `DELETE` or `PATCH`.
417
+ The same notices accompany handled error responses. User mock responses are not marked.
418
+ Aliases themselves have no route-deprecation header. Server warnings are logged once per
419
+ operation per process, rather than for every request. No `Sunset` date is promised yet.
420
+
421
+ Python emits `DeprecationWarning` for the synchronous client's upcoming transport change,
422
+ `cache_time`, `clean_cache()` and `clean_storage(path=...)`. Python's standard warning filters
423
+ control repetition and visibility; use `python -W default::DeprecationWarning -m pytest` to
424
+ review them. Warnings do not change successful responses or retries. Projects treating warnings
425
+ as errors should review these notices before enabling that policy for a dependency upgrade.