cloudru-qa-http-client 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,615 @@
1
+ Metadata-Version: 2.5
2
+ Name: cloudru-qa-http-client
3
+ Version: 1.0.0
4
+ Summary: Базовый HTTP-клиент
5
+ Project-URL: Repository, https://github.com/cloud-ru-tech/qa-libraries
6
+ Project-URL: Issues, https://github.com/cloud-ru-tech/qa-libraries/issues
7
+ Author-email: Алексей Кирютин <ayukiryutin@cloud.ru>
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Keywords: async,client,http,httpx
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Internet :: WWW/HTTP
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.13
21
+ Requires-Dist: glom>=25.12.0
22
+ Requires-Dist: httpx<0.29,>=0.28.1
23
+ Provides-Extra: truststore
24
+ Requires-Dist: truststore<0.11,>=0.10.4; extra == 'truststore'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # HTTP client
28
+
29
+ [![PyPI](https://img.shields.io/pypi/v/cloudru-qa-http-client)](https://pypi.org/project/cloudru-qa-http-client/)
30
+ [![Python](https://img.shields.io/pypi/pyversions/cloudru-qa-http-client)](https://pypi.org/project/cloudru-qa-http-client/)
31
+ [![License](https://img.shields.io/pypi/l/cloudru-qa-http-client)](https://github.com/cloud-ru-tech/qa-libraries/blob/master/LICENSE)
32
+
33
+ English | [Русский](README.ru.md)
34
+
35
+ - [HTTP client](#http-client)
36
+ - [Installation](#installation)
37
+ - [Skills for AI agents](#skills-for-ai-agents)
38
+ - [Usage](#usage)
39
+ - [Example:](#example)
40
+ - [Base client example](#base-client-example)
41
+ - [Custom client example](#custom-client-example)
42
+ - [Asynchronous client example](#asynchronous-client-example)
43
+ - [Configuration](#configuration)
44
+ - [Available options](#available-options)
45
+ - [EventHooks](#eventhooks)
46
+ - [Decorating the send method](#decorating-the-send-method)
47
+ - [Streaming requests](#streaming-requests)
48
+ - [The `stream` method](#the-stream-method)
49
+ - [The `stream_request` method](#the-stream_request-method)
50
+ - [Decorating the stream\_request method](#decorating-the-stream_request-method)
51
+ - [The request method](#the-request-method)
52
+ - [Timeouts](#timeouts)
53
+ - [Authorization](#authorization)
54
+ - [Request identification](#request-identification)
55
+ - [Tracing](#tracing)
56
+ - [Logging](#logging)
57
+ - [Closing connections](#closing-connections)
58
+ - [TLS connection](#tls-connection)
59
+ - [Default headers](#default-headers)
60
+ - [Development](#development)
61
+
62
+ ## Installation
63
+
64
+ Requires Python 3.13 or later.
65
+
66
+ ```shell
67
+ pip install cloudru-qa-http-client
68
+ ```
69
+
70
+ Import the library as the `http_client` module.
71
+
72
+ ## Skills for AI agents
73
+
74
+ The package includes the [`http-client-usage`](https://github.com/cloud-ru-tech/qa-libraries/blob/master/.agents/skills/http-client-usage/SKILL.md) skill
75
+ with examples and usage rules for AI agents. Install the library
76
+ in your project environment, then run this from its root (requires [`uv`](https://docs.astral.sh/uv/)):
77
+
78
+ ```shell
79
+ uvx library-skills install --skill http-client-usage --yes
80
+ ```
81
+
82
+ For Claude Code, add `--claude`. The instructions are shipped with the library version.
83
+
84
+ ## Usage
85
+
86
+ The library provides `HttpClient` and `AsyncHttpClient`. Use them directly or subclass them to create custom clients.
87
+
88
+ All options (logging, request identification, tracing, default headers, and others) work the same for both clients. Synchronous event hooks are automatically wrapped as async when used with `AsyncHttpClient`.
89
+
90
+ ### Example:
91
+
92
+ #### Base client example
93
+
94
+ ```python
95
+ import logging
96
+
97
+ from http_client import HttpClient, options
98
+ from httpx import BasicAuth
99
+
100
+ with HttpClient(
101
+ "https://api.example.com",
102
+ options.with_auth(BasicAuth("demo", "example-password")),
103
+ options.with_logger(logging.getLogger(__name__)),
104
+ ) as client:
105
+ response = client.post("/test_method", json={"test": True})
106
+ response.raise_for_status()
107
+ ```
108
+
109
+ #### Custom client example
110
+
111
+ ```python
112
+ import logging
113
+
114
+ from http_client import HttpClient, options
115
+ from httpx import Response
116
+
117
+
118
+ class CustomHttpClient(HttpClient):
119
+ def test_method(self, data: dict[str, object]) -> Response:
120
+ """Send data to a service method.
121
+
122
+ :param data: JSON request body.
123
+ :return: service response.
124
+ """
125
+ request = self.client.build_request("POST", "/test_method", json=data)
126
+ return self.send(request)
127
+
128
+
129
+ with CustomHttpClient(
130
+ "https://api.example.com",
131
+ options.with_logger(logging.getLogger(__name__)),
132
+ options.with_autogenerated_request_id(),
133
+ options.with_autogenerated_traceparent(),
134
+ ) as client:
135
+ response = client.test_method({"test": True})
136
+ response.raise_for_status()
137
+ ```
138
+
139
+ #### Asynchronous client example
140
+
141
+ All standard options, including synchronous event hooks, work unchanged with `AsyncHttpClient`:
142
+
143
+ ```python
144
+ import logging
145
+
146
+ from http_client import AsyncHttpClient, options
147
+
148
+ async with AsyncHttpClient(
149
+ "https://api.example.com",
150
+ options.with_logger(logging.getLogger(__name__)),
151
+ options.with_autogenerated_request_id(),
152
+ ) as client:
153
+ response = await client.post("/test_method", json={"test": True})
154
+ response.raise_for_status()
155
+ ```
156
+
157
+ ### Configuration
158
+ Configure the client with `ClientOption` options. Each option receives the actual client instance and runs in its constructor. Register hooks and decorators with `add_request_hook`, `add_response_hook`, `add_send_decorator`, and `add_stream_decorator`:
159
+
160
+ ```python
161
+ from http_client import BaseHttpClient, ClientOption
162
+ from httpx import Request
163
+
164
+
165
+ def with_region(region: str) -> ClientOption:
166
+ """Add a region to outgoing request headers.
167
+
168
+ :param region: region identifier.
169
+ :return: client option.
170
+ """
171
+
172
+ def add_region(request: Request) -> None:
173
+ request.headers["X-Region"] = region
174
+
175
+ def option(client: BaseHttpClient) -> None:
176
+ client.add_request_hook(add_region, prepend=True)
177
+
178
+ return option
179
+ ```
180
+
181
+ Registration is available while constructor options run. After client creation, its transport is already configured.
182
+
183
+ ## Available options
184
+
185
+ The `options` module provides options for configuring the HTTP client. Combine them when creating a client instance.
186
+
187
+ ### EventHooks
188
+
189
+ Event hook options let you process requests and responses:
190
+
191
+ - `with_request_hook` — adds a function to process requests before sending
192
+ - `with_response_hook` — adds a function to process responses after receiving
193
+
194
+ These options provide a flexible way to handle each client request and/or response.
195
+
196
+ Usage example:
197
+
198
+ ```python
199
+ import logging
200
+
201
+ from httpx import Request, Response
202
+
203
+ from http_client import HttpClient, options
204
+
205
+
206
+ def my_request_hook(request: Request) -> None:
207
+ # process the request before sending it
208
+ request.headers["X-Custom-Header"] = "Custom Value"
209
+
210
+
211
+ def my_response_hook(response: Response) -> None:
212
+ # process the response after receiving it
213
+ logging.getLogger(__name__).info("Response status code: %s", response.status_code)
214
+
215
+
216
+ client = HttpClient(
217
+ "http://example.com",
218
+ options.with_request_hook(my_request_hook),
219
+ options.with_response_hook(my_response_hook),
220
+ )
221
+ ```
222
+
223
+ With `AsyncHttpClient`, synchronous hooks are automatically wrapped as async. You can also pass native async hooks:
224
+
225
+ ```python
226
+ from http_client import AsyncHttpClient, options
227
+ from httpx import Request
228
+
229
+
230
+ async def my_async_request_hook(request: Request) -> None:
231
+ request.headers["X-Async"] = "true"
232
+
233
+
234
+ client = AsyncHttpClient(
235
+ "http://example.com",
236
+ options.with_request_hook(my_async_request_hook),
237
+ )
238
+ ```
239
+
240
+ See the [`httpx` documentation](https://www.python-httpx.org/advanced/event-hooks/) for more about hooks.
241
+
242
+ ### Decorating the send method
243
+
244
+ The HTTP client supports decorating its `send` method with `with_send_decorator`. This lets you add behavior before sending the HTTP request and after receiving the response.
245
+
246
+ `with_send_decorator` applies the decorator pattern to `send`, letting you:
247
+ - Preprocess the request
248
+ - Postprocess the response
249
+ - Implement retries, logging, caching, monitoring, and other features
250
+
251
+ Example of a request logging decorator:
252
+
253
+ ```python
254
+ from functools import wraps
255
+ from http_client import HttpClient, options
256
+ from http_client.types import SendOptions, SyncSendMethod
257
+ from typing import Unpack
258
+ import logging
259
+ import httpx
260
+
261
+
262
+ def logging_decorator(func: SyncSendMethod) -> SyncSendMethod:
263
+ @wraps(func)
264
+ def wrapper(request: httpx.Request, **kwargs: Unpack[SendOptions]) -> httpx.Response:
265
+ logging.getLogger(__name__).info("Sending request: %s %s", request.method, request.url)
266
+ response = func(request, **kwargs)
267
+ logging.getLogger(__name__).info("Received response: %s", response.status_code)
268
+ return response
269
+
270
+ return wrapper
271
+
272
+
273
+ client = HttpClient("https://example.com", options.with_send_decorator(logging_decorator))
274
+ ```
275
+
276
+ For the asynchronous HTTP client, the decorator must be asynchronous:
277
+
278
+ ```python
279
+ from functools import wraps
280
+ from http_client import AsyncHttpClient, options
281
+ from http_client.types import AsyncSendMethod, SendOptions
282
+ from typing import Unpack
283
+ import logging
284
+ import httpx
285
+
286
+
287
+ def async_logging_decorator(func: AsyncSendMethod) -> AsyncSendMethod:
288
+ @wraps(func)
289
+ async def wrapper(request: httpx.Request, **kwargs: Unpack[SendOptions]) -> httpx.Response:
290
+ logging.getLogger(__name__).info("Sending request: %s %s", request.method, request.url)
291
+ response = await func(request, **kwargs)
292
+ logging.getLogger(__name__).info("Received response: %s", response.status_code)
293
+ return response
294
+
295
+ return wrapper
296
+
297
+
298
+ client = AsyncHttpClient("https://example.com", options.with_send_decorator(async_logging_decorator))
299
+ ```
300
+
301
+ You can apply several decorators. Each new decorator wraps the previous one, so the last one added is outermost and runs first:
302
+
303
+ ```python
304
+ client = HttpClient(
305
+ "https://example.com",
306
+ options.with_send_decorator(decorator1), # applied first, becomes innermost
307
+ options.with_send_decorator(decorator2), # applied second, becomes outermost
308
+ )
309
+ ```
310
+
311
+ Request calls run in this order: `decorator2` → `decorator1` → `send`. The response passes through them in reverse: `decorator1` finishes first, then `decorator2`.
312
+
313
+ ### Streaming requests
314
+
315
+ The HTTP client supports streaming requests in two ways:
316
+
317
+ #### The `stream` method
318
+
319
+ `stream(method, url, **kwargs)` is fully compatible with the `httpx` API. It takes an HTTP method and URL, builds a request, and returns a context manager:
320
+
321
+ ```python
322
+ from http_client import HttpClient, options
323
+
324
+ client = HttpClient(
325
+ "https://example.com",
326
+ options.with_auth(auth),
327
+ options.with_logger(logger),
328
+ )
329
+
330
+ # Streaming download (httpx contract)
331
+ with client.stream("GET", "/large-file.zip") as response:
332
+ response.raise_for_status()
333
+
334
+ with open("file.zip", "wb") as f:
335
+ for chunk in response.iter_bytes(chunk_size=8192):
336
+ f.write(chunk)
337
+ ```
338
+
339
+ Asynchronous client example:
340
+
341
+ ```python
342
+ from http_client import AsyncHttpClient, options
343
+
344
+ client = AsyncHttpClient(
345
+ "https://example.com",
346
+ options.with_auth(auth),
347
+ options.with_logger(logger),
348
+ )
349
+
350
+ async with client.stream("GET", "/stream-data") as response:
351
+ response.raise_for_status()
352
+
353
+ async for line in response.aiter_lines():
354
+ process_line(line)
355
+ ```
356
+
357
+ #### The `stream_request` method
358
+
359
+ `stream_request(request, **kwargs)` takes a ready-made `httpx.Request` and supports all client options, including decorators:
360
+
361
+ ```python
362
+ request = client.client.build_request("GET", "/large-file.zip")
363
+
364
+ with client.stream_request(request) as response:
365
+ for chunk in response.iter_bytes(chunk_size=8192):
366
+ f.write(chunk)
367
+ ```
368
+
369
+ #### Decorating the stream_request method
370
+
371
+ Like `send`, `stream_request` can be decorated with `with_stream_decorator`:
372
+
373
+ ```python
374
+ from functools import wraps
375
+ from contextlib import contextmanager
376
+ from http_client import HttpClient, options
377
+ from http_client.types import StreamOptions, SyncStreamRequestMethod
378
+ from collections.abc import Iterator
379
+ from typing import Unpack
380
+ import httpx
381
+ import logging
382
+
383
+
384
+ def stream_logging_decorator(func: SyncStreamRequestMethod) -> SyncStreamRequestMethod:
385
+ @wraps(func)
386
+ @contextmanager
387
+ def wrapper(request: httpx.Request, **kwargs: Unpack[StreamOptions]) -> Iterator[httpx.Response]:
388
+ logging.getLogger(__name__).info("Starting stream: %s %s", request.method, request.url)
389
+ with func(request, **kwargs) as response:
390
+ logging.getLogger(__name__).info("Stream status: %s", response.status_code)
391
+ yield response
392
+ logging.getLogger(__name__).info("Stream finished")
393
+
394
+ return wrapper
395
+
396
+
397
+ client = HttpClient("https://example.com", options.with_stream_decorator(stream_logging_decorator))
398
+ ```
399
+
400
+ **Important:** When logging streaming requests, the response body appears as `<streamed content>` because it has not yet been read when the response hook runs.
401
+
402
+ ### The request method
403
+
404
+ `request(method, url, **kwargs)` sends HTTP requests with any method:
405
+
406
+ ```python
407
+ response = client.request("PATCH", "/api/resource", json={"field": "value"})
408
+ ```
409
+
410
+ ### Timeouts
411
+
412
+ `with_timeout` sets different types of HTTP request timeouts:
413
+
414
+ ```python
415
+ from httpx import Timeout
416
+ from http_client import HttpClient, options
417
+
418
+ # Set different timeout types
419
+ timeout = Timeout(
420
+ connect=5.0, # time to establish a connection
421
+ read=10.0, # time to read the response
422
+ write=5.0, # time to write the request
423
+ pool=1.0, # time to wait for a free connection in the pool
424
+ )
425
+
426
+ client = HttpClient("https://example.com", options.with_timeout(timeout))
427
+ ```
428
+
429
+ ### Authorization
430
+
431
+ `with_auth` configures HTTP request authorization. All `httpx` authorization mechanisms are supported. You can also implement your own; see the [`httpx` documentation](https://www.python-httpx.org/advanced/authentication/#custom-authentication-schemes).
432
+
433
+ ```python
434
+ from httpx import BasicAuth, DigestAuth
435
+ from http_client import HttpClient, options
436
+
437
+ # Basic authorization
438
+ client = HttpClient("https://example.com", options.with_auth(BasicAuth(username="user", password="password")))
439
+
440
+ # Digest authorization
441
+ client = HttpClient("https://example.com", options.with_auth(DigestAuth(username="user", password="password")))
442
+
443
+ # Custom Bearer token authorization
444
+ client = HttpClient(
445
+ "https://example.com", options.with_auth(lambda r: r.headers.update({"Authorization": f"Bearer {token}"}))
446
+ )
447
+ ```
448
+
449
+ ### Request identification
450
+
451
+ `with_autogenerated_request_id` automatically adds a unique identifier to each request's `X-Request-ID` header:
452
+
453
+ ```python
454
+ from http_client import HttpClient, options
455
+
456
+ # Automatically generate X-Request-ID
457
+ client = HttpClient("https://example.com", options.with_autogenerated_request_id())
458
+ ```
459
+
460
+ This is useful for tracing requests across microservices and for debugging.
461
+
462
+ ### Tracing
463
+
464
+ `with_autogenerated_traceparent` automatically adds a `traceparent` tracing header compatible with W3C Trace Context:
465
+
466
+ ```python
467
+ from http_client import HttpClient, options
468
+
469
+ # Add a traceparent header to every request
470
+ client = HttpClient("https://example.com", options.with_autogenerated_traceparent())
471
+ ```
472
+
473
+ This lets you track requests across multiple services using different monitoring systems.
474
+
475
+ ### Logging
476
+
477
+ `with_logger` configures HTTP request and response logging. Set shared defaults with a `LoggingConfig` object:
478
+
479
+ ```python
480
+ from http_client import configure_logging
481
+ from http_client.config import LoggingConfig
482
+
483
+ configure_logging(options=LoggingConfig(json_indent=2, use_line_breaks=True))
484
+ ```
485
+
486
+ Pass client-specific settings to `with_logger`:
487
+
488
+ ```python
489
+ import logging
490
+ from http_client import HttpClient, options
491
+
492
+ # Configure a custom logger
493
+ logger = logging.getLogger("http_client")
494
+
495
+ client = HttpClient(
496
+ "https://example.com",
497
+ options.with_logger(
498
+ logger,
499
+ sanitize_keys=["authorization", "password"], # hide secret data
500
+ ),
501
+ )
502
+ ```
503
+
504
+ `sanitize_keys` hides sensitive information in logs, such as authorization tokens and passwords.
505
+
506
+ Common secrets are masked **by default**, even without `sanitize_keys`: `authorization`, `proxy-authorization`, `cookie`, `set-cookie`, `x-api-key`, `api-key`, `api_key`, `access_token`, `refresh_token`, `password`, `secret`, `token`. Keys from `sanitize_keys` extend this list.
507
+
508
+ Disable the default protection with an explicit flag, for example when debugging in a private environment:
509
+
510
+ ```python
511
+ options.with_logger(logger, use_default_sanitize_keys=False)
512
+ ```
513
+
514
+ ⚠️ Masking applies only to access log records written by this library. `httpx` exceptions may contain the full URL, including query parameters, so do not pass secrets in URLs.
515
+
516
+ ### Closing connections
517
+
518
+ The connection pool is created in the client constructor, so close the client. A context manager is the simplest way:
519
+
520
+ ```python
521
+ with HttpClient("https://example.com") as client:
522
+ client.get("/api/resource")
523
+
524
+ async with AsyncHttpClient("https://example.com") as client:
525
+ await client.get("/api/resource")
526
+ ```
527
+
528
+ For a long-lived client created once per application, use explicit methods:
529
+
530
+ ```python
531
+ client.close() # HttpClient
532
+ await client.aclose() # AsyncHttpClient
533
+ ```
534
+
535
+ A closed client cannot be reopened: like `httpx`, this raises `RuntimeError`.
536
+
537
+ ### TLS connection
538
+
539
+ The library provides two options to configure TLS connections. Both work the same with `HttpClient` and `AsyncHttpClient`.
540
+
541
+ 1. `with_verify_context` — sets the TLS verification context:
542
+
543
+ ```python
544
+ import ssl
545
+ from http_client import HttpClient, options
546
+
547
+ # Create an SSL context
548
+ ssl_context = ssl.create_default_context()
549
+ ssl_context.load_verify_locations("/path/to/ca-certificates.crt")
550
+
551
+ # Set the SSL context
552
+ client = HttpClient("https://example.com", options.with_verify_context(verify=ssl_context))
553
+ ```
554
+
555
+ 2. `use_truststore` — uses the system certificate store for verification:
556
+
557
+ ```python
558
+ from http_client import HttpClient, options
559
+
560
+ # Use the system certificate store
561
+ client = HttpClient("https://example.com", options.use_truststore())
562
+ ```
563
+
564
+ To use `use_truststore`, install the extra dependency:
565
+ ```
566
+ pip install "cloudru-qa-http-client[truststore]"
567
+ ```
568
+
569
+ ### Default headers
570
+
571
+ `with_default_headers` sets default headers sent with all client requests.
572
+
573
+ ```python
574
+ from http_client import HttpClient, options
575
+
576
+ headers = {
577
+ "X-My-Custom-Header": "Hello, world!",
578
+ "X-Another-Header": "Another value",
579
+ }
580
+
581
+ client = HttpClient(
582
+ "https://example.com",
583
+ options.with_default_headers(headers),
584
+ )
585
+ ```
586
+ ## Development
587
+
588
+ The library is developed in the [qa-libraries](https://github.com/cloud-ru-tech/qa-libraries) monorepo
589
+ alongside the other `cloudru-qa-*` libraries. You need [`uv`](https://docs.astral.sh/uv/) 0.11.14
590
+ or later, GNU Make, and [`checkmake`](https://github.com/checkmake/checkmake). Run commands from the repository root:
591
+
592
+ ```shell
593
+ make sync # install dependencies for all libraries from uv.lock
594
+ make test-http-client # tests for this library
595
+ make typecheck-http-client # mypy for this library
596
+ make check # formatting, ruff, mypy, and tests for the entire workspace
597
+ ```
598
+
599
+ ⚠️ **Keep** the library lightweight and general-purpose. Discuss new dependencies
600
+ only when you are sure all library users need them.
601
+
602
+ Tests run on Python 3.13 and 3.14. Create a separate environment for another version:
603
+ `make sync test-http-client PYTHON_VERSION=3.14`.
604
+
605
+ Demo scenarios with real requests to local [go-httpbin](https://github.com/mccutchen/go-httpbin)
606
+ are in `examples/httpbin`. Docker must be running; run commands from `packages/http-client`:
607
+
608
+ ```shell
609
+ docker compose up -d
610
+ uv run --no-sync python -m examples.httpbin
611
+ docker compose down
612
+ ```
613
+
614
+ Publish a release with the git tag `http-client/v<version>`. The tag version must match the version
615
+ in `packages/http-client/pyproject.toml`.
@@ -0,0 +1,18 @@
1
+ http_client/__init__.py,sha256=Lk6JzOwSSOWbM-zS9zVUj_iYKfLyA6cpJnohQ4orins,353
2
+ http_client/client.py,sha256=4hLs10MVNUcMABALr7GfzXqSJLEjMwciNTYhzRIMs68,22808
3
+ http_client/config.py,sha256=WzRLmXicM0skU3g0mxhxhMSm9d9epDS6XhjVkIWbPv0,3229
4
+ http_client/event_hooks.py,sha256=vuLCdI8jDDHMsL_2noUPGTA1xa-48cn1domoqd1hJGk,4836
5
+ http_client/options.py,sha256=CmD7sFqGAemlmT-MTUkpvy_IUOw1bAFUDwQtqdfCt78,7473
6
+ http_client/py.typed,sha256=2V-BXHpFdhiPP7ymH5gngkCEyIkBSKRBl5jxiG1YHEM,81
7
+ http_client/types.py,sha256=Xv14tsiQ7AGVTceJMQFdhH23yrhutDidb6q8Z-P-EbE,4010
8
+ http_client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ http_client/utils/logutils.py,sha256=89Rs7tSbfyiub9oN-pwQ_xBdlfEn6wYAj8QBF6KlQuU,8736
10
+ http_client/utils/sanitizer.py,sha256=R-Nc_ymZkmZMaVCdZOnVthYa5AIAPyoygoJaJZEaCEA,2025
11
+ http_client/utils/traceparent.py,sha256=oZXVJ8eLYzNLbAbA0oPmUgfQGf_tVH241TbIG1DHz8E,329
12
+ http_client/.agents/skills/http-client-usage/SKILL.md,sha256=06XX2L-b2xmdKeWFvo5T0auc-8WquqKE87CcOjXqjtY,10007
13
+ http_client/.agents/skills/http-client-usage/references/options.md,sha256=wDtsM4X-iqp1A8Jj-FQVcfRNqH_nQVBo2H6hhI04Mz0,10384
14
+ http_client/.agents/skills/http-client-usage/references/pitfalls.md,sha256=9kRbecUGZbTmHJF-pXPCM5kiAeTPiDnBHTNVsTXY_g4,2733
15
+ cloudru_qa_http_client-1.0.0.dist-info/METADATA,sha256=a8ye2349RUpWmnUxZOWmJ79iLqPGbXvSI1GgS1HSamo,19893
16
+ cloudru_qa_http_client-1.0.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
17
+ cloudru_qa_http_client-1.0.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
18
+ cloudru_qa_http_client-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any