adcp 1.6.1__tar.gz → 2.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.
- {adcp-1.6.1/src/adcp.egg-info → adcp-2.1.0}/PKG-INFO +42 -3
- {adcp-1.6.1 → adcp-2.1.0}/README.md +39 -2
- {adcp-1.6.1 → adcp-2.1.0}/pyproject.toml +17 -2
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/__init__.py +110 -189
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/adagents.py +11 -12
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/client.py +16 -11
- adcp-2.1.0/src/adcp/py.typed +0 -0
- adcp-2.1.0/src/adcp/types/aliases.py +209 -0
- adcp-2.1.0/src/adcp/types/generated.py +614 -0
- adcp-2.1.0/src/adcp/types/generated_poc/__init__.py +3 -0
- adcp-2.1.0/src/adcp/types/generated_poc/activate_signal_request.py +34 -0
- adcp-2.1.0/src/adcp/types/generated_poc/activate_signal_response.py +57 -0
- adcp-2.1.0/src/adcp/types/generated_poc/activation_key.py +30 -0
- adcp-2.1.0/src/adcp/types/generated_poc/adagents.py +266 -0
- adcp-2.1.0/src/adcp/types/generated_poc/asset_type.py +100 -0
- adcp-2.1.0/src/adcp/types/generated_poc/audio_asset.py +26 -0
- adcp-2.1.0/src/adcp/types/generated_poc/brand_manifest.py +260 -0
- adcp-2.1.0/src/adcp/types/generated_poc/brand_manifest_ref.py +361 -0
- adcp-2.1.0/src/adcp/types/generated_poc/build_creative_request.py +43 -0
- adcp-2.1.0/src/adcp/types/generated_poc/build_creative_response.py +57 -0
- adcp-2.1.0/src/adcp/types/generated_poc/channels.py +19 -0
- adcp-2.1.0/src/adcp/types/generated_poc/cpc_option.py +39 -0
- adcp-2.1.0/src/adcp/types/generated_poc/cpcv_option.py +41 -0
- adcp-2.1.0/src/adcp/types/generated_poc/cpm_auction_option.py +54 -0
- adcp-2.1.0/src/adcp/types/generated_poc/cpm_fixed_option.py +39 -0
- adcp-2.1.0/src/adcp/types/generated_poc/cpp_option.py +60 -0
- adcp-2.1.0/src/adcp/types/generated_poc/cpv_option.py +73 -0
- adcp-2.1.0/src/adcp/types/generated_poc/create_media_buy_request.py +96 -0
- adcp-2.1.0/src/adcp/types/generated_poc/create_media_buy_response.py +66 -0
- adcp-2.1.0/src/adcp/types/generated_poc/creative_asset.py +83 -0
- adcp-2.1.0/src/adcp/types/generated_poc/creative_assignment.py +27 -0
- adcp-2.1.0/src/adcp/types/generated_poc/creative_manifest.py +61 -0
- adcp-2.1.0/src/adcp/types/generated_poc/creative_policy.py +34 -0
- adcp-2.1.0/src/adcp/types/generated_poc/creative_status.py +14 -0
- adcp-2.1.0/src/adcp/types/generated_poc/css_asset.py +20 -0
- adcp-2.1.0/src/adcp/types/generated_poc/daast_asset.py +76 -0
- adcp-2.1.0/src/adcp/types/generated_poc/delivery_metrics.py +111 -0
- adcp-2.1.0/src/adcp/types/generated_poc/delivery_type.py +12 -0
- adcp-2.1.0/src/adcp/types/generated_poc/deployment.py +78 -0
- adcp-2.1.0/src/adcp/types/generated_poc/destination.py +43 -0
- adcp-2.1.0/src/adcp/types/generated_poc/error.py +29 -0
- adcp-2.1.0/src/adcp/types/generated_poc/flat_rate_option.py +93 -0
- adcp-2.1.0/src/adcp/types/generated_poc/format.py +260 -0
- adcp-2.1.0/src/adcp/types/generated_poc/format_id.py +29 -0
- adcp-2.1.0/src/adcp/types/generated_poc/frequency_cap.py +19 -0
- adcp-2.1.0/src/adcp/types/generated_poc/frequency_cap_scope.py +16 -0
- adcp-2.1.0/src/adcp/types/generated_poc/get_media_buy_delivery_request.py +65 -0
- adcp-2.1.0/src/adcp/types/generated_poc/get_media_buy_delivery_response.py +220 -0
- adcp-2.1.0/src/adcp/types/generated_poc/get_products_request.py +83 -0
- adcp-2.1.0/src/adcp/types/generated_poc/get_products_response.py +29 -0
- adcp-2.1.0/src/adcp/types/generated_poc/get_signals_request.py +77 -0
- adcp-2.1.0/src/adcp/types/generated_poc/get_signals_response.py +65 -0
- adcp-2.1.0/src/adcp/types/generated_poc/html_asset.py +18 -0
- adcp-2.1.0/src/adcp/types/generated_poc/identifier_types.py +29 -0
- adcp-2.1.0/src/adcp/types/generated_poc/image_asset.py +23 -0
- adcp-2.1.0/src/adcp/types/generated_poc/index.py +17 -0
- adcp-2.1.0/src/adcp/types/generated_poc/javascript_asset.py +25 -0
- adcp-2.1.0/src/adcp/types/generated_poc/list_authorized_properties_request.py +39 -0
- adcp-2.1.0/src/adcp/types/generated_poc/list_authorized_properties_response.py +85 -0
- adcp-2.1.0/src/adcp/types/generated_poc/list_creative_formats_request.py +93 -0
- adcp-2.1.0/src/adcp/types/generated_poc/list_creative_formats_response.py +63 -0
- adcp-2.1.0/src/adcp/types/generated_poc/list_creatives_request.py +154 -0
- adcp-2.1.0/src/adcp/types/generated_poc/list_creatives_response.py +234 -0
- adcp-2.1.0/src/adcp/types/generated_poc/markdown_asset.py +43 -0
- adcp-2.1.0/src/adcp/types/generated_poc/measurement.py +40 -0
- adcp-2.1.0/src/adcp/types/generated_poc/media_buy.py +37 -0
- adcp-2.1.0/src/adcp/types/generated_poc/media_buy_status.py +14 -0
- adcp-2.1.0/src/adcp/types/generated_poc/pacing.py +13 -0
- adcp-2.1.0/src/adcp/types/generated_poc/package.py +61 -0
- adcp-2.1.0/src/adcp/types/generated_poc/package_request.py +61 -0
- adcp-2.1.0/src/adcp/types/generated_poc/package_status.py +14 -0
- adcp-2.1.0/src/adcp/types/generated_poc/performance_feedback.py +89 -0
- adcp-2.1.0/src/adcp/types/generated_poc/placement.py +37 -0
- adcp-2.1.0/src/adcp/types/generated_poc/preview_creative_request.py +163 -0
- adcp-2.1.0/src/adcp/types/generated_poc/preview_creative_response.py +175 -0
- adcp-2.1.0/src/adcp/types/generated_poc/preview_render.py +144 -0
- adcp-2.1.0/src/adcp/types/generated_poc/pricing_model.py +17 -0
- adcp-2.1.0/src/adcp/types/generated_poc/pricing_option.py +365 -0
- adcp-2.1.0/src/adcp/types/generated_poc/product.py +211 -0
- adcp-2.1.0/src/adcp/types/generated_poc/promoted_offerings.py +102 -0
- adcp-2.1.0/src/adcp/types/generated_poc/promoted_products.py +38 -0
- adcp-2.1.0/src/adcp/types/generated_poc/property.py +79 -0
- adcp-2.1.0/src/adcp/types/generated_poc/protocol_envelope.py +61 -0
- adcp-2.1.0/src/adcp/types/generated_poc/provide_performance_feedback_request.py +85 -0
- adcp-2.1.0/src/adcp/types/generated_poc/provide_performance_feedback_response.py +59 -0
- adcp-2.1.0/src/adcp/types/generated_poc/publisher_identifier_types.py +15 -0
- adcp-2.1.0/src/adcp/types/generated_poc/push_notification_config.py +55 -0
- adcp-2.1.0/src/adcp/types/generated_poc/reporting_capabilities.py +68 -0
- adcp-2.1.0/src/adcp/types/generated_poc/response.py +24 -0
- adcp-2.1.0/src/adcp/types/generated_poc/standard_format_ids.py +45 -0
- adcp-2.1.0/src/adcp/types/generated_poc/start_timing.py +13 -0
- adcp-2.1.0/src/adcp/types/generated_poc/sub_asset.py +55 -0
- adcp-2.1.0/src/adcp/types/generated_poc/sync_creatives_request.py +69 -0
- adcp-2.1.0/src/adcp/types/generated_poc/sync_creatives_response.py +117 -0
- adcp-2.1.0/src/adcp/types/generated_poc/targeting.py +53 -0
- adcp-2.1.0/src/adcp/types/generated_poc/task_status.py +19 -0
- adcp-2.1.0/src/adcp/types/generated_poc/task_type.py +15 -0
- adcp-2.1.0/src/adcp/types/generated_poc/tasks_get_request.py +29 -0
- adcp-2.1.0/src/adcp/types/generated_poc/tasks_get_response.py +112 -0
- adcp-2.1.0/src/adcp/types/generated_poc/tasks_list_request.py +121 -0
- adcp-2.1.0/src/adcp/types/generated_poc/tasks_list_response.py +122 -0
- adcp-2.1.0/src/adcp/types/generated_poc/text_asset.py +20 -0
- adcp-2.1.0/src/adcp/types/generated_poc/update_media_buy_request.py +160 -0
- adcp-2.1.0/src/adcp/types/generated_poc/update_media_buy_response.py +67 -0
- adcp-2.1.0/src/adcp/types/generated_poc/url_asset.py +33 -0
- adcp-2.1.0/src/adcp/types/generated_poc/vast_asset.py +86 -0
- adcp-2.1.0/src/adcp/types/generated_poc/vcpm_auction_option.py +57 -0
- adcp-2.1.0/src/adcp/types/generated_poc/vcpm_fixed_option.py +43 -0
- adcp-2.1.0/src/adcp/types/generated_poc/video_asset.py +28 -0
- adcp-2.1.0/src/adcp/types/generated_poc/webhook_asset.py +65 -0
- adcp-2.1.0/src/adcp/types/generated_poc/webhook_payload.py +102 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/utils/preview_cache.py +54 -41
- adcp-2.1.0/src/adcp/validation.py +172 -0
- {adcp-1.6.1 → adcp-2.1.0/src/adcp.egg-info}/PKG-INFO +42 -3
- adcp-2.1.0/src/adcp.egg-info/SOURCES.txt +147 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp.egg-info/requires.txt +2 -0
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_adagents.py +58 -18
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_client.py +19 -2
- adcp-2.1.0/tests/test_code_generation.py +64 -0
- adcp-2.1.0/tests/test_discriminated_unions.py +450 -0
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_format_id_validation.py +5 -6
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_preview_html.py +123 -56
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_protocols.py +3 -1
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_simple_api.py +30 -7
- adcp-2.1.0/tests/test_type_aliases.py +179 -0
- adcp-1.6.1/src/adcp/types/generated.py +0 -1208
- adcp-1.6.1/src/adcp/types/tasks.py +0 -511
- adcp-1.6.1/src/adcp.egg-info/SOURCES.txt +0 -42
- adcp-1.6.1/tests/test_code_generation.py +0 -400
- adcp-1.6.1/tests/test_discriminated_unions.py +0 -404
- {adcp-1.6.1 → adcp-2.1.0}/LICENSE +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/setup.cfg +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/__main__.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/config.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/exceptions.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/protocols/__init__.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/protocols/a2a.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/protocols/base.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/protocols/mcp.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/simple.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/testing/__init__.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/testing/test_helpers.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/types/__init__.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/types/base.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/types/core.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/utils/__init__.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/utils/operation_id.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp/utils/response_parser.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp.egg-info/dependency_links.txt +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp.egg-info/entry_points.txt +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/src/adcp.egg-info/top_level.txt +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_cli.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_helpers.py +0 -0
- {adcp-1.6.1 → adcp-2.1.0}/tests/test_response_parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: adcp
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Official Python client for the Ad Context Protocol (AdCP)
|
|
5
5
|
Author-email: AdCP Community <maintainers@adcontextprotocol.org>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -33,6 +33,8 @@ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
|
33
33
|
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
34
34
|
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
35
|
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: datamodel-code-generator[http]>=0.35.0; extra == "dev"
|
|
37
|
+
Requires-Dist: email-validator>=2.0.0; extra == "dev"
|
|
36
38
|
Dynamic: license-file
|
|
37
39
|
|
|
38
40
|
# adcp - Python Client for Ad Context Protocol
|
|
@@ -92,7 +94,7 @@ print(products.products[0].name)
|
|
|
92
94
|
**Standard API** (`client.*`) - Recommended for production:
|
|
93
95
|
```python
|
|
94
96
|
from adcp.testing import test_agent
|
|
95
|
-
from adcp
|
|
97
|
+
from adcp import GetProductsRequest
|
|
96
98
|
|
|
97
99
|
# Explicit request objects and TaskResult wrapper
|
|
98
100
|
request = GetProductsRequest(brief='Coffee brands')
|
|
@@ -122,6 +124,8 @@ Pre-configured agents (all include `.simple` accessor):
|
|
|
122
124
|
|
|
123
125
|
See [examples/simple_api_demo.py](examples/simple_api_demo.py) for a complete comparison.
|
|
124
126
|
|
|
127
|
+
> **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`) rather than `adcp.types.generated` for better API stability.
|
|
128
|
+
|
|
125
129
|
## Quick Start: Distributed Operations
|
|
126
130
|
|
|
127
131
|
For production use, configure your own agents:
|
|
@@ -185,7 +189,7 @@ from adcp.testing import (
|
|
|
185
189
|
test_agent_no_auth, test_agent_a2a_no_auth,
|
|
186
190
|
creative_agent, test_agent_client, create_test_agent
|
|
187
191
|
)
|
|
188
|
-
from adcp
|
|
192
|
+
from adcp import GetProductsRequest, PreviewCreativeRequest
|
|
189
193
|
|
|
190
194
|
# 1. Single agent with authentication (MCP)
|
|
191
195
|
result = await test_agent.get_products(
|
|
@@ -241,6 +245,7 @@ client = ADCPClient(config)
|
|
|
241
245
|
- **Auto-detection**: Automatically detect which protocol an agent uses
|
|
242
246
|
|
|
243
247
|
### Type Safety
|
|
248
|
+
|
|
244
249
|
Full type hints with Pydantic validation and auto-generated types from the AdCP spec:
|
|
245
250
|
|
|
246
251
|
```python
|
|
@@ -256,6 +261,40 @@ if result.success:
|
|
|
256
261
|
print(product.name, product.pricing_options) # Full IDE autocomplete!
|
|
257
262
|
```
|
|
258
263
|
|
|
264
|
+
#### Semantic Type Aliases
|
|
265
|
+
|
|
266
|
+
For discriminated union types (success/error responses), use semantic aliases for clearer code:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
from adcp import (
|
|
270
|
+
CreateMediaBuySuccessResponse, # Clear: this is the success case
|
|
271
|
+
CreateMediaBuyErrorResponse, # Clear: this is the error case
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
def handle_response(
|
|
275
|
+
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse
|
|
276
|
+
) -> None:
|
|
277
|
+
if isinstance(response, CreateMediaBuySuccessResponse):
|
|
278
|
+
print(f"✅ Media buy created: {response.media_buy_id}")
|
|
279
|
+
else:
|
|
280
|
+
print(f"❌ Errors: {response.errors}")
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Available semantic aliases:**
|
|
284
|
+
- Response types: `*SuccessResponse` / `*ErrorResponse` (e.g., `CreateMediaBuySuccessResponse`)
|
|
285
|
+
- Request variants: `*FormatRequest` / `*ManifestRequest` (e.g., `PreviewCreativeFormatRequest`)
|
|
286
|
+
- Preview renders: `PreviewRenderImage` / `PreviewRenderHtml` / `PreviewRenderIframe`
|
|
287
|
+
- Activation keys: `PropertyIdActivationKey` / `PropertyTagActivationKey`
|
|
288
|
+
|
|
289
|
+
See `examples/type_aliases_demo.py` for more examples.
|
|
290
|
+
|
|
291
|
+
**Import guidelines:**
|
|
292
|
+
- ✅ **DO**: Import from main package: `from adcp import GetProductsRequest`
|
|
293
|
+
- ✅ **DO**: Use semantic aliases: `from adcp import CreateMediaBuySuccessResponse`
|
|
294
|
+
- ⚠️ **AVOID**: Import from internal modules: `from adcp.types.generated import CreateMediaBuyResponse1`
|
|
295
|
+
|
|
296
|
+
The main package exports provide a stable API while internal generated types may change.
|
|
297
|
+
|
|
259
298
|
### Multi-Agent Operations
|
|
260
299
|
Execute across multiple agents simultaneously:
|
|
261
300
|
|
|
@@ -55,7 +55,7 @@ print(products.products[0].name)
|
|
|
55
55
|
**Standard API** (`client.*`) - Recommended for production:
|
|
56
56
|
```python
|
|
57
57
|
from adcp.testing import test_agent
|
|
58
|
-
from adcp
|
|
58
|
+
from adcp import GetProductsRequest
|
|
59
59
|
|
|
60
60
|
# Explicit request objects and TaskResult wrapper
|
|
61
61
|
request = GetProductsRequest(brief='Coffee brands')
|
|
@@ -85,6 +85,8 @@ Pre-configured agents (all include `.simple` accessor):
|
|
|
85
85
|
|
|
86
86
|
See [examples/simple_api_demo.py](examples/simple_api_demo.py) for a complete comparison.
|
|
87
87
|
|
|
88
|
+
> **Tip**: Import types from the main `adcp` package (e.g., `from adcp import GetProductsRequest`) rather than `adcp.types.generated` for better API stability.
|
|
89
|
+
|
|
88
90
|
## Quick Start: Distributed Operations
|
|
89
91
|
|
|
90
92
|
For production use, configure your own agents:
|
|
@@ -148,7 +150,7 @@ from adcp.testing import (
|
|
|
148
150
|
test_agent_no_auth, test_agent_a2a_no_auth,
|
|
149
151
|
creative_agent, test_agent_client, create_test_agent
|
|
150
152
|
)
|
|
151
|
-
from adcp
|
|
153
|
+
from adcp import GetProductsRequest, PreviewCreativeRequest
|
|
152
154
|
|
|
153
155
|
# 1. Single agent with authentication (MCP)
|
|
154
156
|
result = await test_agent.get_products(
|
|
@@ -204,6 +206,7 @@ client = ADCPClient(config)
|
|
|
204
206
|
- **Auto-detection**: Automatically detect which protocol an agent uses
|
|
205
207
|
|
|
206
208
|
### Type Safety
|
|
209
|
+
|
|
207
210
|
Full type hints with Pydantic validation and auto-generated types from the AdCP spec:
|
|
208
211
|
|
|
209
212
|
```python
|
|
@@ -219,6 +222,40 @@ if result.success:
|
|
|
219
222
|
print(product.name, product.pricing_options) # Full IDE autocomplete!
|
|
220
223
|
```
|
|
221
224
|
|
|
225
|
+
#### Semantic Type Aliases
|
|
226
|
+
|
|
227
|
+
For discriminated union types (success/error responses), use semantic aliases for clearer code:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from adcp import (
|
|
231
|
+
CreateMediaBuySuccessResponse, # Clear: this is the success case
|
|
232
|
+
CreateMediaBuyErrorResponse, # Clear: this is the error case
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def handle_response(
|
|
236
|
+
response: CreateMediaBuySuccessResponse | CreateMediaBuyErrorResponse
|
|
237
|
+
) -> None:
|
|
238
|
+
if isinstance(response, CreateMediaBuySuccessResponse):
|
|
239
|
+
print(f"✅ Media buy created: {response.media_buy_id}")
|
|
240
|
+
else:
|
|
241
|
+
print(f"❌ Errors: {response.errors}")
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**Available semantic aliases:**
|
|
245
|
+
- Response types: `*SuccessResponse` / `*ErrorResponse` (e.g., `CreateMediaBuySuccessResponse`)
|
|
246
|
+
- Request variants: `*FormatRequest` / `*ManifestRequest` (e.g., `PreviewCreativeFormatRequest`)
|
|
247
|
+
- Preview renders: `PreviewRenderImage` / `PreviewRenderHtml` / `PreviewRenderIframe`
|
|
248
|
+
- Activation keys: `PropertyIdActivationKey` / `PropertyTagActivationKey`
|
|
249
|
+
|
|
250
|
+
See `examples/type_aliases_demo.py` for more examples.
|
|
251
|
+
|
|
252
|
+
**Import guidelines:**
|
|
253
|
+
- ✅ **DO**: Import from main package: `from adcp import GetProductsRequest`
|
|
254
|
+
- ✅ **DO**: Use semantic aliases: `from adcp import CreateMediaBuySuccessResponse`
|
|
255
|
+
- ⚠️ **AVOID**: Import from internal modules: `from adcp.types.generated import CreateMediaBuyResponse1`
|
|
256
|
+
|
|
257
|
+
The main package exports provide a stable API while internal generated types may change.
|
|
258
|
+
|
|
222
259
|
### Multi-Agent Operations
|
|
223
260
|
Execute across multiple agents simultaneously:
|
|
224
261
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "adcp"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "2.1.0"
|
|
8
8
|
description = "Official Python client for the Ad Context Protocol (AdCP)"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "AdCP Community", email = "maintainers@adcontextprotocol.org"}
|
|
@@ -44,6 +44,8 @@ dev = [
|
|
|
44
44
|
"mypy>=1.0.0",
|
|
45
45
|
"black>=23.0.0",
|
|
46
46
|
"ruff>=0.1.0",
|
|
47
|
+
"datamodel-code-generator[http]>=0.35.0",
|
|
48
|
+
"email-validator>=2.0.0",
|
|
47
49
|
]
|
|
48
50
|
|
|
49
51
|
[project.urls]
|
|
@@ -55,6 +57,9 @@ Issues = "https://github.com/adcontextprotocol/adcp-client-python/issues"
|
|
|
55
57
|
[tool.setuptools.packages.find]
|
|
56
58
|
where = ["src"]
|
|
57
59
|
|
|
60
|
+
[tool.setuptools.package-data]
|
|
61
|
+
adcp = ["py.typed"]
|
|
62
|
+
|
|
58
63
|
[tool.black]
|
|
59
64
|
line-length = 100
|
|
60
65
|
target-version = ["py310", "py311", "py312"]
|
|
@@ -63,7 +68,11 @@ extend-exclude = "/(generated|tasks)\\.py$"
|
|
|
63
68
|
[tool.ruff]
|
|
64
69
|
line-length = 100
|
|
65
70
|
target-version = "py310"
|
|
66
|
-
extend-exclude = [
|
|
71
|
+
extend-exclude = [
|
|
72
|
+
"src/adcp/types/generated.py",
|
|
73
|
+
"src/adcp/types/tasks.py",
|
|
74
|
+
"src/adcp/types/generated_poc/",
|
|
75
|
+
]
|
|
67
76
|
|
|
68
77
|
[tool.ruff.lint]
|
|
69
78
|
select = ["E", "F", "I", "N", "W", "UP"]
|
|
@@ -86,3 +95,9 @@ ignore_errors = true
|
|
|
86
95
|
[tool.pytest.ini_options]
|
|
87
96
|
testpaths = ["tests"]
|
|
88
97
|
asyncio_mode = "auto"
|
|
98
|
+
|
|
99
|
+
[dependency-groups]
|
|
100
|
+
dev = [
|
|
101
|
+
"datamodel-code-generator>=0.35.0",
|
|
102
|
+
"pre-commit>=4.4.0",
|
|
103
|
+
]
|
|
@@ -48,122 +48,87 @@ from adcp.testing import (
|
|
|
48
48
|
test_agent_client,
|
|
49
49
|
test_agent_no_auth,
|
|
50
50
|
)
|
|
51
|
+
|
|
52
|
+
# Import all generated types - users can import what they need from adcp.types.generated
|
|
53
|
+
from adcp.types import aliases, generated
|
|
54
|
+
|
|
55
|
+
# Re-export semantic type aliases for better ergonomics
|
|
56
|
+
from adcp.types.aliases import (
|
|
57
|
+
ActivateSignalErrorResponse,
|
|
58
|
+
ActivateSignalSuccessResponse,
|
|
59
|
+
BuildCreativeErrorResponse,
|
|
60
|
+
BuildCreativeSuccessResponse,
|
|
61
|
+
CreateMediaBuyErrorResponse,
|
|
62
|
+
CreateMediaBuySuccessResponse,
|
|
63
|
+
PreviewCreativeFormatRequest,
|
|
64
|
+
PreviewCreativeInteractiveResponse,
|
|
65
|
+
PreviewCreativeManifestRequest,
|
|
66
|
+
PreviewCreativeStaticResponse,
|
|
67
|
+
PreviewRenderHtml,
|
|
68
|
+
PreviewRenderIframe,
|
|
69
|
+
PreviewRenderImage,
|
|
70
|
+
PropertyIdActivationKey,
|
|
71
|
+
PropertyTagActivationKey,
|
|
72
|
+
ProvidePerformanceFeedbackErrorResponse,
|
|
73
|
+
ProvidePerformanceFeedbackSuccessResponse,
|
|
74
|
+
SyncCreativesErrorResponse,
|
|
75
|
+
SyncCreativesSuccessResponse,
|
|
76
|
+
UpdateMediaBuyErrorResponse,
|
|
77
|
+
UpdateMediaBuyPackagesRequest,
|
|
78
|
+
UpdateMediaBuyPropertiesRequest,
|
|
79
|
+
UpdateMediaBuySuccessResponse,
|
|
80
|
+
)
|
|
51
81
|
from adcp.types.core import AgentConfig, Protocol, TaskResult, TaskStatus, WebhookMetadata
|
|
82
|
+
|
|
83
|
+
# Re-export commonly-used request/response types for convenience
|
|
84
|
+
# Users should import from main package (e.g., `from adcp import GetProductsRequest`)
|
|
85
|
+
# rather than internal modules for better API stability
|
|
52
86
|
from adcp.types.generated import (
|
|
53
|
-
|
|
54
|
-
# Request/Response types
|
|
87
|
+
# Audience & Targeting
|
|
55
88
|
ActivateSignalRequest,
|
|
56
89
|
ActivateSignalResponse,
|
|
57
|
-
|
|
58
|
-
ActivationKey,
|
|
59
|
-
AgentDeployment,
|
|
60
|
-
AgentDestination,
|
|
61
|
-
BothPreviewRender,
|
|
62
|
-
# Brand types
|
|
63
|
-
BrandManifest,
|
|
64
|
-
BrandManifestRef,
|
|
90
|
+
# Creative Operations
|
|
65
91
|
BuildCreativeRequest,
|
|
66
92
|
BuildCreativeResponse,
|
|
67
|
-
#
|
|
68
|
-
Channels,
|
|
69
|
-
CreateMediaBuyError,
|
|
93
|
+
# Media Buy Operations
|
|
70
94
|
CreateMediaBuyRequest,
|
|
71
95
|
CreateMediaBuyResponse,
|
|
72
|
-
|
|
73
|
-
# Creative types
|
|
74
|
-
CreativeAsset,
|
|
75
|
-
CreativeAssignment,
|
|
76
|
-
CreativeManifest,
|
|
77
|
-
CreativePolicy,
|
|
78
|
-
DaastAsset,
|
|
79
|
-
# Metrics types
|
|
80
|
-
DeliveryMetrics,
|
|
81
|
-
# Delivery types
|
|
82
|
-
DeliveryType,
|
|
83
|
-
Deployment,
|
|
84
|
-
# Deployment types
|
|
85
|
-
Destination,
|
|
96
|
+
# Common data types
|
|
86
97
|
Error,
|
|
87
98
|
Format,
|
|
88
|
-
FormatId,
|
|
89
|
-
FrequencyCap,
|
|
90
99
|
GetMediaBuyDeliveryRequest,
|
|
91
100
|
GetMediaBuyDeliveryResponse,
|
|
92
101
|
GetProductsRequest,
|
|
93
102
|
GetProductsResponse,
|
|
94
103
|
GetSignalsRequest,
|
|
95
104
|
GetSignalsResponse,
|
|
96
|
-
HtmlPreviewRender,
|
|
97
|
-
InlineDaastAsset,
|
|
98
|
-
InlineVastAsset,
|
|
99
|
-
Key_valueActivationKey,
|
|
100
105
|
ListAuthorizedPropertiesRequest,
|
|
101
106
|
ListAuthorizedPropertiesResponse,
|
|
102
107
|
ListCreativeFormatsRequest,
|
|
103
108
|
ListCreativeFormatsResponse,
|
|
104
109
|
ListCreativesRequest,
|
|
105
110
|
ListCreativesResponse,
|
|
106
|
-
Measurement,
|
|
107
|
-
# Core domain types
|
|
108
|
-
MediaBuy,
|
|
109
|
-
# Status enums
|
|
110
|
-
MediaBuyStatus,
|
|
111
|
-
# Sub-asset types
|
|
112
|
-
MediaSubAsset,
|
|
113
|
-
Pacing,
|
|
114
|
-
Package,
|
|
115
|
-
PackageStatus,
|
|
116
|
-
PerformanceFeedback,
|
|
117
|
-
Placement,
|
|
118
|
-
PlatformDeployment,
|
|
119
|
-
PlatformDestination,
|
|
120
111
|
PreviewCreativeRequest,
|
|
121
112
|
PreviewCreativeResponse,
|
|
122
|
-
# Preview render types
|
|
123
|
-
PreviewRender,
|
|
124
|
-
PricingModel,
|
|
125
|
-
# Pricing types
|
|
126
|
-
PricingOption,
|
|
127
113
|
Product,
|
|
128
|
-
PromotedProducts,
|
|
129
|
-
# Property and placement types
|
|
130
114
|
Property,
|
|
131
|
-
ProtocolEnvelope,
|
|
132
115
|
ProvidePerformanceFeedbackRequest,
|
|
133
116
|
ProvidePerformanceFeedbackResponse,
|
|
134
|
-
PushNotificationConfig,
|
|
135
|
-
ReportingCapabilities,
|
|
136
|
-
Response,
|
|
137
|
-
Segment_idActivationKey,
|
|
138
|
-
StandardFormatIds,
|
|
139
|
-
StartTiming,
|
|
140
|
-
SubAsset,
|
|
141
|
-
SyncCreativesError,
|
|
142
117
|
SyncCreativesRequest,
|
|
143
118
|
SyncCreativesResponse,
|
|
144
|
-
SyncCreativesSuccess,
|
|
145
|
-
# Targeting types
|
|
146
|
-
Targeting,
|
|
147
|
-
# Task types
|
|
148
|
-
TaskType,
|
|
149
|
-
TextSubAsset,
|
|
150
|
-
UpdateMediaBuyError,
|
|
151
119
|
UpdateMediaBuyRequest,
|
|
152
120
|
UpdateMediaBuyResponse,
|
|
153
|
-
UpdateMediaBuySuccess,
|
|
154
|
-
UrlDaastAsset,
|
|
155
|
-
UrlPreviewRender,
|
|
156
|
-
UrlVastAsset,
|
|
157
|
-
# Asset delivery types (VAST/DAAST)
|
|
158
|
-
VastAsset,
|
|
159
|
-
# Protocol types
|
|
160
|
-
WebhookPayload,
|
|
161
121
|
)
|
|
162
|
-
from adcp.types.generated import
|
|
163
|
-
|
|
122
|
+
from adcp.types.generated import TaskStatus as GeneratedTaskStatus
|
|
123
|
+
from adcp.validation import (
|
|
124
|
+
ValidationError,
|
|
125
|
+
validate_adagents,
|
|
126
|
+
validate_agent_authorization,
|
|
127
|
+
validate_product,
|
|
128
|
+
validate_publisher_properties_item,
|
|
164
129
|
)
|
|
165
130
|
|
|
166
|
-
__version__ = "1.
|
|
131
|
+
__version__ = "2.1.0"
|
|
167
132
|
|
|
168
133
|
__all__ = [
|
|
169
134
|
# Client classes
|
|
@@ -175,6 +140,37 @@ __all__ = [
|
|
|
175
140
|
"TaskResult",
|
|
176
141
|
"TaskStatus",
|
|
177
142
|
"WebhookMetadata",
|
|
143
|
+
# Common request/response types (re-exported for convenience)
|
|
144
|
+
"CreateMediaBuyRequest",
|
|
145
|
+
"CreateMediaBuyResponse",
|
|
146
|
+
"GetMediaBuyDeliveryRequest",
|
|
147
|
+
"GetMediaBuyDeliveryResponse",
|
|
148
|
+
"GetProductsRequest",
|
|
149
|
+
"GetProductsResponse",
|
|
150
|
+
"UpdateMediaBuyRequest",
|
|
151
|
+
"UpdateMediaBuyResponse",
|
|
152
|
+
"BuildCreativeRequest",
|
|
153
|
+
"BuildCreativeResponse",
|
|
154
|
+
"ListCreativeFormatsRequest",
|
|
155
|
+
"ListCreativeFormatsResponse",
|
|
156
|
+
"ListCreativesRequest",
|
|
157
|
+
"ListCreativesResponse",
|
|
158
|
+
"PreviewCreativeRequest",
|
|
159
|
+
"PreviewCreativeResponse",
|
|
160
|
+
"SyncCreativesRequest",
|
|
161
|
+
"SyncCreativesResponse",
|
|
162
|
+
"ActivateSignalRequest",
|
|
163
|
+
"ActivateSignalResponse",
|
|
164
|
+
"GetSignalsRequest",
|
|
165
|
+
"GetSignalsResponse",
|
|
166
|
+
"ListAuthorizedPropertiesRequest",
|
|
167
|
+
"ListAuthorizedPropertiesResponse",
|
|
168
|
+
"ProvidePerformanceFeedbackRequest",
|
|
169
|
+
"ProvidePerformanceFeedbackResponse",
|
|
170
|
+
"Error",
|
|
171
|
+
"Format",
|
|
172
|
+
"Product",
|
|
173
|
+
"Property",
|
|
178
174
|
# Adagents validation
|
|
179
175
|
"fetch_adagents",
|
|
180
176
|
"verify_agent_authorization",
|
|
@@ -210,113 +206,38 @@ __all__ = [
|
|
|
210
206
|
"AdagentsValidationError",
|
|
211
207
|
"AdagentsNotFoundError",
|
|
212
208
|
"AdagentsTimeoutError",
|
|
213
|
-
#
|
|
214
|
-
"
|
|
215
|
-
"
|
|
216
|
-
"
|
|
217
|
-
"
|
|
218
|
-
"
|
|
219
|
-
|
|
220
|
-
"
|
|
221
|
-
"
|
|
222
|
-
"BuildCreativeResponse",
|
|
223
|
-
"CreateMediaBuyRequest",
|
|
224
|
-
"CreateMediaBuyResponse",
|
|
225
|
-
"CreateMediaBuySuccess",
|
|
226
|
-
"CreateMediaBuyError",
|
|
227
|
-
"GetMediaBuyDeliveryRequest",
|
|
228
|
-
"GetMediaBuyDeliveryResponse",
|
|
229
|
-
"GetProductsRequest",
|
|
230
|
-
"GetProductsResponse",
|
|
231
|
-
"GetSignalsRequest",
|
|
232
|
-
"GetSignalsResponse",
|
|
233
|
-
"ListAuthorizedPropertiesRequest",
|
|
234
|
-
"ListAuthorizedPropertiesResponse",
|
|
235
|
-
"ListCreativeFormatsRequest",
|
|
236
|
-
"ListCreativeFormatsResponse",
|
|
237
|
-
"ListCreativesRequest",
|
|
238
|
-
"ListCreativesResponse",
|
|
239
|
-
"PreviewCreativeRequest",
|
|
240
|
-
"PreviewCreativeResponse",
|
|
241
|
-
"ProvidePerformanceFeedbackRequest",
|
|
242
|
-
"ProvidePerformanceFeedbackResponse",
|
|
243
|
-
"SyncCreativesRequest",
|
|
244
|
-
"SyncCreativesResponse",
|
|
245
|
-
"SyncCreativesSuccess",
|
|
246
|
-
"SyncCreativesError",
|
|
247
|
-
"UpdateMediaBuyRequest",
|
|
248
|
-
"UpdateMediaBuyResponse",
|
|
249
|
-
"UpdateMediaBuySuccess",
|
|
250
|
-
"UpdateMediaBuyError",
|
|
251
|
-
# Core domain types
|
|
252
|
-
"MediaBuy",
|
|
253
|
-
"Product",
|
|
254
|
-
"Package",
|
|
255
|
-
"Error",
|
|
256
|
-
# Creative types
|
|
257
|
-
"CreativeAsset",
|
|
258
|
-
"CreativeManifest",
|
|
259
|
-
"CreativeAssignment",
|
|
260
|
-
"CreativePolicy",
|
|
261
|
-
"Format",
|
|
262
|
-
"FormatId",
|
|
263
|
-
# Property and placement types
|
|
264
|
-
"Property",
|
|
265
|
-
"Placement",
|
|
266
|
-
# Targeting types
|
|
267
|
-
"Targeting",
|
|
268
|
-
"FrequencyCap",
|
|
269
|
-
"Pacing",
|
|
270
|
-
# Brand types
|
|
271
|
-
"BrandManifest",
|
|
272
|
-
"BrandManifestRef",
|
|
273
|
-
# Metrics types
|
|
274
|
-
"DeliveryMetrics",
|
|
275
|
-
"Measurement",
|
|
276
|
-
"PerformanceFeedback",
|
|
277
|
-
# Status enums
|
|
278
|
-
"MediaBuyStatus",
|
|
279
|
-
"PackageStatus",
|
|
280
|
-
# Pricing types
|
|
281
|
-
"PricingOption",
|
|
282
|
-
"PricingModel",
|
|
283
|
-
# Delivery types
|
|
284
|
-
"DeliveryType",
|
|
285
|
-
"StartTiming",
|
|
286
|
-
# Channel types
|
|
287
|
-
"Channels",
|
|
288
|
-
"StandardFormatIds",
|
|
289
|
-
# Protocol types
|
|
290
|
-
"WebhookPayload",
|
|
291
|
-
"ProtocolEnvelope",
|
|
292
|
-
"Response",
|
|
293
|
-
"PromotedProducts",
|
|
294
|
-
"PushNotificationConfig",
|
|
295
|
-
"ReportingCapabilities",
|
|
296
|
-
# Deployment types
|
|
297
|
-
"Destination",
|
|
298
|
-
"Deployment",
|
|
299
|
-
"PlatformDestination",
|
|
300
|
-
"AgentDestination",
|
|
301
|
-
"PlatformDeployment",
|
|
302
|
-
"AgentDeployment",
|
|
303
|
-
# Sub-asset types
|
|
304
|
-
"MediaSubAsset",
|
|
305
|
-
"SubAsset",
|
|
306
|
-
"TextSubAsset",
|
|
307
|
-
# Asset delivery types (VAST/DAAST)
|
|
308
|
-
"VastAsset",
|
|
309
|
-
"UrlVastAsset",
|
|
310
|
-
"InlineVastAsset",
|
|
311
|
-
"DaastAsset",
|
|
312
|
-
"UrlDaastAsset",
|
|
313
|
-
"InlineDaastAsset",
|
|
314
|
-
# Preview render types
|
|
315
|
-
"PreviewRender",
|
|
316
|
-
"UrlPreviewRender",
|
|
317
|
-
"HtmlPreviewRender",
|
|
318
|
-
"BothPreviewRender",
|
|
319
|
-
# Task types
|
|
320
|
-
"TaskType",
|
|
209
|
+
# Validation utilities
|
|
210
|
+
"ValidationError",
|
|
211
|
+
"validate_adagents",
|
|
212
|
+
"validate_agent_authorization",
|
|
213
|
+
"validate_product",
|
|
214
|
+
"validate_publisher_properties_item",
|
|
215
|
+
# Generated types modules
|
|
216
|
+
"generated",
|
|
217
|
+
"aliases",
|
|
321
218
|
"GeneratedTaskStatus",
|
|
219
|
+
# Semantic type aliases (for better API ergonomics)
|
|
220
|
+
"ActivateSignalSuccessResponse",
|
|
221
|
+
"ActivateSignalErrorResponse",
|
|
222
|
+
"BuildCreativeSuccessResponse",
|
|
223
|
+
"BuildCreativeErrorResponse",
|
|
224
|
+
"CreateMediaBuySuccessResponse",
|
|
225
|
+
"CreateMediaBuyErrorResponse",
|
|
226
|
+
"ProvidePerformanceFeedbackSuccessResponse",
|
|
227
|
+
"ProvidePerformanceFeedbackErrorResponse",
|
|
228
|
+
"SyncCreativesSuccessResponse",
|
|
229
|
+
"SyncCreativesErrorResponse",
|
|
230
|
+
"UpdateMediaBuySuccessResponse",
|
|
231
|
+
"UpdateMediaBuyErrorResponse",
|
|
232
|
+
"PreviewCreativeFormatRequest",
|
|
233
|
+
"PreviewCreativeManifestRequest",
|
|
234
|
+
"PreviewCreativeStaticResponse",
|
|
235
|
+
"PreviewCreativeInteractiveResponse",
|
|
236
|
+
"PreviewRenderImage",
|
|
237
|
+
"PreviewRenderHtml",
|
|
238
|
+
"PreviewRenderIframe",
|
|
239
|
+
"PropertyIdActivationKey",
|
|
240
|
+
"PropertyTagActivationKey",
|
|
241
|
+
"UpdateMediaBuyPackagesRequest",
|
|
242
|
+
"UpdateMediaBuyPropertiesRequest",
|
|
322
243
|
]
|
|
@@ -14,6 +14,7 @@ from urllib.parse import urlparse
|
|
|
14
14
|
import httpx
|
|
15
15
|
|
|
16
16
|
from adcp.exceptions import AdagentsNotFoundError, AdagentsTimeoutError, AdagentsValidationError
|
|
17
|
+
from adcp.validation import ValidationError, validate_adagents
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def _normalize_domain(domain: str) -> str:
|
|
@@ -56,9 +57,7 @@ def _validate_publisher_domain(domain: str) -> str:
|
|
|
56
57
|
suspicious_chars = ["\\", "@", "\n", "\r", "\t"]
|
|
57
58
|
for char in suspicious_chars:
|
|
58
59
|
if char in domain:
|
|
59
|
-
raise AdagentsValidationError(
|
|
60
|
-
f"Invalid character in publisher domain: {char!r}"
|
|
61
|
-
)
|
|
60
|
+
raise AdagentsValidationError(f"Invalid character in publisher domain: {char!r}")
|
|
62
61
|
|
|
63
62
|
domain = domain.strip()
|
|
64
63
|
|
|
@@ -70,9 +69,7 @@ def _validate_publisher_domain(domain: str) -> str:
|
|
|
70
69
|
|
|
71
70
|
# Check for spaces after stripping leading/trailing whitespace
|
|
72
71
|
if " " in domain:
|
|
73
|
-
raise AdagentsValidationError(
|
|
74
|
-
"Invalid character in publisher domain: ' '"
|
|
75
|
-
)
|
|
72
|
+
raise AdagentsValidationError("Invalid character in publisher domain: ' '")
|
|
76
73
|
|
|
77
74
|
# Remove protocol if present (common user error) - do this BEFORE checking for slashes
|
|
78
75
|
if "://" in domain:
|
|
@@ -87,9 +84,7 @@ def _validate_publisher_domain(domain: str) -> str:
|
|
|
87
84
|
|
|
88
85
|
# Final validation - must look like a domain
|
|
89
86
|
if "." not in domain:
|
|
90
|
-
raise AdagentsValidationError(
|
|
91
|
-
f"Publisher domain must contain at least one dot: {domain!r}"
|
|
92
|
-
)
|
|
87
|
+
raise AdagentsValidationError(f"Publisher domain must contain at least one dot: {domain!r}")
|
|
93
88
|
|
|
94
89
|
return domain
|
|
95
90
|
|
|
@@ -359,13 +354,17 @@ async def fetch_adagents(
|
|
|
359
354
|
raise AdagentsValidationError("adagents.json must be a JSON object")
|
|
360
355
|
|
|
361
356
|
if "authorized_agents" not in data:
|
|
362
|
-
raise AdagentsValidationError(
|
|
363
|
-
"adagents.json must have 'authorized_agents' field"
|
|
364
|
-
)
|
|
357
|
+
raise AdagentsValidationError("adagents.json must have 'authorized_agents' field")
|
|
365
358
|
|
|
366
359
|
if not isinstance(data["authorized_agents"], list):
|
|
367
360
|
raise AdagentsValidationError("'authorized_agents' must be an array")
|
|
368
361
|
|
|
362
|
+
# Validate mutual exclusivity constraints
|
|
363
|
+
try:
|
|
364
|
+
validate_adagents(data)
|
|
365
|
+
except ValidationError as e:
|
|
366
|
+
raise AdagentsValidationError(f"Invalid adagents.json structure: {e}") from e
|
|
367
|
+
|
|
369
368
|
return data
|
|
370
369
|
|
|
371
370
|
except httpx.TimeoutException as e:
|