httpr 0.2.5__cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.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.
httpr/httpr.pyi ADDED
@@ -0,0 +1,701 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from collections.abc import Iterator
5
+ from contextlib import AbstractAsyncContextManager, AbstractContextManager
6
+ from typing import Any, Literal, TypedDict
7
+
8
+ if sys.version_info <= (3, 11):
9
+ from typing_extensions import Unpack
10
+ else:
11
+ from typing import Unpack
12
+
13
+ HttpMethod = Literal["GET", "HEAD", "OPTIONS", "DELETE", "POST", "PUT", "PATCH"]
14
+
15
+ class RequestParams(TypedDict, total=False):
16
+ auth: tuple[str, str | None] | None
17
+ auth_bearer: str | None
18
+ params: dict[str, str] | None
19
+ headers: dict[str, str] | None
20
+ cookies: dict[str, str] | None
21
+ timeout: float | None
22
+ content: bytes | None
23
+ data: dict[str, Any] | None
24
+ json: Any | None
25
+ files: dict[str, str] | None
26
+
27
+ class ClientRequestParams(RequestParams):
28
+ verify: bool | None
29
+ ca_cert_file: str | None
30
+ client_pem: str | None
31
+
32
+ class CaseInsensitiveHeaderMap:
33
+ """
34
+ A case-insensitive dictionary-like class for HTTP headers.
35
+
36
+ HTTP headers are case-insensitive per the HTTP specification.
37
+ This class allows accessing headers using any case while preserving
38
+ the original case for iteration.
39
+
40
+ Example:
41
+ ```python
42
+ headers = response.headers
43
+ content_type = headers["Content-Type"] # Works
44
+ content_type = headers["content-type"] # Also works
45
+
46
+ if "Content-Type" in headers:
47
+ print("Has content type")
48
+
49
+ for key, value in headers.items():
50
+ print(f"{key}: {value}")
51
+ ```
52
+ """
53
+ def __getitem__(self, key: str) -> str:
54
+ """Get a header value by name (case-insensitive)."""
55
+ ...
56
+ def __contains__(self, key: str) -> bool:
57
+ """Check if a header exists (case-insensitive)."""
58
+ ...
59
+ def __iter__(self) -> Iterator[str]:
60
+ """Iterate over header names."""
61
+ ...
62
+ def items(self) -> list[tuple[str, str]]:
63
+ """Return a list of (name, value) tuples."""
64
+ ...
65
+ def keys(self) -> list[str]:
66
+ """Return a list of header names."""
67
+ ...
68
+ def values(self) -> list[str]:
69
+ """Return a list of header values."""
70
+ ...
71
+ def get(self, key: str, default: str | None = None) -> str:
72
+ """
73
+ Get a header value by name (case-insensitive).
74
+
75
+ Args:
76
+ key: Header name to look up.
77
+ default: Value to return if header is not found. Defaults to None.
78
+
79
+ Returns:
80
+ The header value, or the default if not found.
81
+ """
82
+ ...
83
+
84
+ class Response:
85
+ """
86
+ An HTTP response object.
87
+
88
+ This class provides access to the response status, headers, cookies,
89
+ and body content. The body can be accessed as bytes, text, JSON, or CBOR.
90
+
91
+ Example:
92
+ ```python
93
+ response = client.get("https://httpbin.org/get")
94
+
95
+ # Status
96
+ print(response.status_code) # 200
97
+ print(response.url) # Final URL after redirects
98
+
99
+ # Headers (case-insensitive access)
100
+ print(response.headers["content-type"])
101
+
102
+ # Body
103
+ print(response.text) # Decoded text
104
+ print(response.json()) # Parsed JSON
105
+ print(response.content) # Raw bytes
106
+ ```
107
+ """
108
+ @property
109
+ def content(self) -> bytes:
110
+ """Raw response body as bytes."""
111
+ ...
112
+ @property
113
+ def cookies(self) -> dict[str, str]:
114
+ """Response cookies as a dictionary."""
115
+ ...
116
+ @property
117
+ def headers(self) -> CaseInsensitiveHeaderMap:
118
+ """
119
+ Response headers as a case-insensitive dictionary-like object.
120
+
121
+ Headers can be accessed using any case (e.g., "Content-Type" or "content-type").
122
+ """
123
+ ...
124
+ @property
125
+ def status_code(self) -> int:
126
+ """HTTP status code (e.g., 200, 404, 500)."""
127
+ ...
128
+ @property
129
+ def url(self) -> str:
130
+ """Final URL after any redirects."""
131
+ ...
132
+ @property
133
+ def encoding(self) -> str:
134
+ """
135
+ Character encoding of the response.
136
+
137
+ Detected from Content-Type header or response body.
138
+ Can be set to override auto-detection.
139
+ """
140
+ ...
141
+ @encoding.setter
142
+ def encoding(self, value: str) -> None: ...
143
+ @property
144
+ def text(self) -> str:
145
+ """Response body decoded as text using the detected encoding."""
146
+ ...
147
+ def json(self) -> Any:
148
+ """
149
+ Parse response body as JSON.
150
+
151
+ If Content-Type is application/cbor, automatically deserializes as CBOR.
152
+
153
+ Returns:
154
+ Parsed JSON/CBOR data as Python objects.
155
+ """
156
+ ...
157
+ def cbor(self) -> Any:
158
+ """
159
+ Parse response body as CBOR (Concise Binary Object Representation).
160
+
161
+ Returns:
162
+ Parsed CBOR data as Python objects.
163
+ """
164
+ ...
165
+ @property
166
+ def text_markdown(self) -> str:
167
+ """
168
+ Response body converted from HTML to Markdown format.
169
+
170
+ Useful for reading HTML content as plain text.
171
+ """
172
+ ...
173
+ @property
174
+ def text_plain(self) -> str:
175
+ """
176
+ Response body converted from HTML to plain text.
177
+
178
+ Strips all formatting and returns only the text content.
179
+ """
180
+ ...
181
+ @property
182
+ def text_rich(self) -> str:
183
+ """
184
+ Response body converted from HTML to rich text format.
185
+
186
+ Preserves some formatting using Unicode characters.
187
+ """
188
+ ...
189
+
190
+ class TextIterator:
191
+ """Iterator for text chunks from a streaming response."""
192
+ def __iter__(self) -> TextIterator: ...
193
+ def __next__(self) -> str: ...
194
+
195
+ class LineIterator:
196
+ """Iterator for lines from a streaming response."""
197
+ def __iter__(self) -> LineIterator: ...
198
+ def __next__(self) -> str: ...
199
+
200
+ class StreamingResponse:
201
+ """
202
+ A streaming HTTP response that allows iterating over chunks of data.
203
+
204
+ This class provides methods to iterate over the response body in chunks
205
+ without buffering the entire response in memory.
206
+ """
207
+
208
+ @property
209
+ def cookies(self) -> dict[str, str]:
210
+ """Response cookies."""
211
+ ...
212
+ @property
213
+ def headers(self) -> CaseInsensitiveHeaderMap:
214
+ """Response headers (case-insensitive access)."""
215
+ ...
216
+ @property
217
+ def status_code(self) -> int:
218
+ """HTTP status code."""
219
+ ...
220
+ @property
221
+ def url(self) -> str:
222
+ """Final URL after any redirects."""
223
+ ...
224
+ @property
225
+ def is_closed(self) -> bool:
226
+ """Whether the stream has been closed."""
227
+ ...
228
+ @property
229
+ def is_consumed(self) -> bool:
230
+ """Whether the stream has been fully consumed."""
231
+ ...
232
+ def __iter__(self) -> Iterator[bytes]:
233
+ """Iterate over the response body as bytes chunks."""
234
+ ...
235
+ def __next__(self) -> bytes:
236
+ """Get the next chunk of bytes."""
237
+ ...
238
+ def iter_bytes(self) -> Iterator[bytes]:
239
+ """
240
+ Iterate over the response body as bytes chunks.
241
+
242
+ Yields chunks of bytes as they are received from the server.
243
+ """
244
+ ...
245
+ def iter_text(self) -> TextIterator:
246
+ """
247
+ Iterate over the response body as text chunks.
248
+
249
+ Decodes each chunk using the response's encoding.
250
+ """
251
+ ...
252
+ def iter_lines(self) -> LineIterator:
253
+ """
254
+ Iterate over the response body line by line.
255
+
256
+ Yields complete lines including newline characters.
257
+ """
258
+ ...
259
+ def read(self) -> bytes:
260
+ """
261
+ Read the entire remaining response body into memory.
262
+
263
+ This consumes the stream.
264
+ """
265
+ ...
266
+ def close(self) -> None:
267
+ """
268
+ Close the streaming response and release resources.
269
+ """
270
+ ...
271
+
272
+ class RClient:
273
+ def __init__(
274
+ self,
275
+ auth: tuple[str, str | None] | None = None,
276
+ auth_bearer: str | None = None,
277
+ params: dict[str, str] | None = None,
278
+ headers: dict[str, str] | None = None,
279
+ cookies: dict[str, str] | None = None,
280
+ timeout: float | None = None,
281
+ cookie_store: bool | None = True,
282
+ referer: bool | None = True,
283
+ proxy: str | None = None,
284
+ follow_redirects: bool | None = True,
285
+ max_redirects: int | None = 20,
286
+ verify: bool | None = True,
287
+ ca_cert_file: str | None = None,
288
+ client_pem: str | None = None,
289
+ https_only: bool | None = False,
290
+ http2_only: bool | None = False,
291
+ ): ...
292
+ @property
293
+ def headers(self) -> dict[str, str]: ...
294
+ @headers.setter
295
+ def headers(self, headers: dict[str, str] | None) -> None: ...
296
+ @property
297
+ def cookies(self) -> dict[str, str]: ...
298
+ @cookies.setter
299
+ def cookies(self, cookies: dict[str, str] | None) -> None: ...
300
+ @property
301
+ def proxy(self) -> str | None: ...
302
+ @proxy.setter
303
+ def proxy(self, proxy: str) -> None: ...
304
+ @property
305
+ def auth(self) -> tuple[str, str | None] | None: ...
306
+ @auth.setter
307
+ def auth(self, auth: tuple[str, str | None] | None) -> None: ...
308
+ @property
309
+ def auth_bearer(self) -> str | None: ...
310
+ @auth_bearer.setter
311
+ def auth_bearer(self, auth_bearer: str | None) -> None: ...
312
+ @property
313
+ def params(self) -> dict[str, str] | None: ...
314
+ @params.setter
315
+ def params(self, params: dict[str, str] | None) -> None: ...
316
+ @property
317
+ def timeout(self) -> float | None: ...
318
+ @timeout.setter
319
+ def timeout(self, timeout: float | None) -> None: ...
320
+ def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
321
+ def _stream(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> StreamingResponse: ...
322
+ def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
323
+ def head(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
324
+ def options(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
325
+ def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
326
+ def post(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
327
+ def put(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
328
+ def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
329
+
330
+ class Client(RClient):
331
+ """
332
+ A synchronous HTTP client with connection pooling.
333
+
334
+ The Client class provides a high-level interface for making HTTP requests.
335
+ It supports connection pooling, automatic cookie handling, and various
336
+ authentication methods.
337
+
338
+ Example:
339
+ ```python
340
+ import httpr
341
+
342
+ # Using context manager (recommended)
343
+ with httpr.Client() as client:
344
+ response = client.get("https://httpbin.org/get")
345
+ print(response.json())
346
+
347
+ # Or manually
348
+ client = httpr.Client()
349
+ response = client.get("https://httpbin.org/get")
350
+ client.close()
351
+ ```
352
+
353
+ Attributes:
354
+ headers: Default headers sent with all requests. Excludes Cookie header.
355
+ cookies: Default cookies sent with all requests.
356
+ auth: Basic auth credentials as (username, password) tuple.
357
+ params: Default query parameters added to all requests.
358
+ timeout: Default timeout in seconds.
359
+ proxy: Proxy URL for requests.
360
+ """
361
+ def __init__(
362
+ self,
363
+ auth: tuple[str, str | None] | None = None,
364
+ auth_bearer: str | None = None,
365
+ params: dict[str, str] | None = None,
366
+ headers: dict[str, str] | None = None,
367
+ cookies: dict[str, str] | None = None,
368
+ cookie_store: bool | None = True,
369
+ referer: bool | None = True,
370
+ proxy: str | None = None,
371
+ timeout: float | None = 30,
372
+ follow_redirects: bool | None = True,
373
+ max_redirects: int | None = 20,
374
+ verify: bool | None = True,
375
+ ca_cert_file: str | None = None,
376
+ client_pem: str | None = None,
377
+ https_only: bool | None = False,
378
+ http2_only: bool | None = False,
379
+ ) -> None:
380
+ """
381
+ Initialize an HTTP client.
382
+
383
+ Args:
384
+ auth: Basic auth credentials as (username, password). Password can be None.
385
+ auth_bearer: Bearer token for Authorization header.
386
+ params: Default query parameters to include in all requests.
387
+ headers: Default headers to send with all requests.
388
+ cookies: Default cookies to send with all requests.
389
+ cookie_store: Enable persistent cookie store. Default is True.
390
+ referer: Automatically set Referer header. Default is True.
391
+ proxy: Proxy URL (e.g., "http://proxy:8080" or "socks5://127.0.0.1:1080").
392
+ timeout: Request timeout in seconds. Default is 30.
393
+ follow_redirects: Follow HTTP redirects. Default is True.
394
+ max_redirects: Maximum redirects to follow. Default is 20.
395
+ verify: Verify SSL certificates. Default is True.
396
+ ca_cert_file: Path to CA certificate bundle (PEM format).
397
+ client_pem: Path to client certificate for mTLS (PEM format).
398
+ https_only: Only allow HTTPS requests. Default is False.
399
+ http2_only: Use HTTP/2 only. Default is False.
400
+ """
401
+ ...
402
+ def __enter__(self) -> Client: ...
403
+ def __exit__(self, *args: Any) -> None: ...
404
+ def close(self) -> None:
405
+ """Close the client and release resources."""
406
+ ...
407
+ def stream(
408
+ self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]
409
+ ) -> AbstractContextManager[StreamingResponse]:
410
+ """
411
+ Make a streaming HTTP request.
412
+
413
+ Returns a context manager that yields a StreamingResponse for iterating
414
+ over the response body in chunks.
415
+
416
+ Args:
417
+ method: HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS).
418
+ url: Request URL.
419
+ **kwargs: Request parameters.
420
+
421
+ Yields:
422
+ StreamingResponse: A response object that can be iterated.
423
+
424
+ Example:
425
+ ```python
426
+ with client.stream("GET", "https://example.com/large-file") as response:
427
+ for chunk in response.iter_bytes():
428
+ process(chunk)
429
+ ```
430
+ """
431
+ ...
432
+
433
+ class AsyncClient(Client):
434
+ """
435
+ An asynchronous HTTP client for use with asyncio.
436
+
437
+ AsyncClient wraps the synchronous Client using asyncio.run_in_executor(),
438
+ providing an async interface while leveraging the Rust implementation's
439
+ performance.
440
+
441
+ Example:
442
+ ```python
443
+ import asyncio
444
+ import httpr
445
+
446
+ async def main():
447
+ async with httpr.AsyncClient() as client:
448
+ response = await client.get("https://httpbin.org/get")
449
+ print(response.json())
450
+
451
+ asyncio.run(main())
452
+ ```
453
+
454
+ Note:
455
+ AsyncClient runs synchronous Rust code in a thread executor.
456
+ It provides concurrency benefits for I/O-bound tasks but is not
457
+ native async I/O.
458
+ """
459
+ def __init__(
460
+ self,
461
+ auth: tuple[str, str | None] | None = None,
462
+ auth_bearer: str | None = None,
463
+ params: dict[str, str] | None = None,
464
+ headers: dict[str, str] | None = None,
465
+ cookies: dict[str, str] | None = None,
466
+ cookie_store: bool | None = True,
467
+ referer: bool | None = True,
468
+ proxy: str | None = None,
469
+ timeout: float | None = 30,
470
+ follow_redirects: bool | None = True,
471
+ max_redirects: int | None = 20,
472
+ verify: bool | None = True,
473
+ ca_cert_file: str | None = None,
474
+ client_pem: str | None = None,
475
+ https_only: bool | None = False,
476
+ http2_only: bool | None = False,
477
+ ) -> None:
478
+ """Initialize an async HTTP client. Accepts the same parameters as Client."""
479
+ ...
480
+ async def __aenter__(self) -> AsyncClient: ...
481
+ async def __aexit__(self, *args: Any) -> None: ...
482
+ async def aclose(self) -> None:
483
+ """Close the async client."""
484
+ ...
485
+ async def request( # type: ignore[override]
486
+ self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]
487
+ ) -> Response:
488
+ """Make an async HTTP request."""
489
+ ...
490
+ async def get( # type: ignore[override]
491
+ self, url: str, **kwargs: Unpack[RequestParams]
492
+ ) -> Response:
493
+ """Make an async GET request."""
494
+ ...
495
+ async def head( # type: ignore[override]
496
+ self, url: str, **kwargs: Unpack[RequestParams]
497
+ ) -> Response:
498
+ """Make an async HEAD request."""
499
+ ...
500
+ async def options( # type: ignore[override]
501
+ self, url: str, **kwargs: Unpack[RequestParams]
502
+ ) -> Response:
503
+ """Make an async OPTIONS request."""
504
+ ...
505
+ async def delete( # type: ignore[override]
506
+ self, url: str, **kwargs: Unpack[RequestParams]
507
+ ) -> Response:
508
+ """Make an async DELETE request."""
509
+ ...
510
+ async def post( # type: ignore[override]
511
+ self, url: str, **kwargs: Unpack[RequestParams]
512
+ ) -> Response:
513
+ """Make an async POST request."""
514
+ ...
515
+ async def put( # type: ignore[override]
516
+ self, url: str, **kwargs: Unpack[RequestParams]
517
+ ) -> Response:
518
+ """Make an async PUT request."""
519
+ ...
520
+ async def patch( # type: ignore[override]
521
+ self, url: str, **kwargs: Unpack[RequestParams]
522
+ ) -> Response:
523
+ """Make an async PATCH request."""
524
+ ...
525
+ def stream( # type: ignore[override]
526
+ self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]
527
+ ) -> AbstractAsyncContextManager[StreamingResponse]:
528
+ """
529
+ Make an async streaming HTTP request.
530
+
531
+ Returns an async context manager that yields a StreamingResponse.
532
+
533
+ Example:
534
+ ```python
535
+ async with client.stream("GET", "https://example.com/large-file") as response:
536
+ for chunk in response.iter_bytes():
537
+ process(chunk)
538
+ ```
539
+ """
540
+ ...
541
+
542
+ def request(method: HttpMethod, url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
543
+ def get(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
544
+ def head(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
545
+ def options(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
546
+ def delete(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
547
+ def post(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
548
+ def put(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
549
+ def patch(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
550
+
551
+ # Exception hierarchy - Base exceptions
552
+ class HTTPError(Exception):
553
+ """Base class for all httpr exceptions."""
554
+
555
+ class RequestError(HTTPError):
556
+ """Base class for all exceptions that may occur when issuing a .request()."""
557
+
558
+ class TransportError(RequestError):
559
+ """Base class for all exceptions that occur at the level of the Transport API."""
560
+
561
+ class NetworkError(TransportError):
562
+ """The base class for network-related errors."""
563
+
564
+ class TimeoutException(TransportError):
565
+ """The base class for timeout errors."""
566
+
567
+ class ProtocolError(TransportError):
568
+ """The protocol was violated."""
569
+
570
+ class StreamError(Exception):
571
+ """The base class for stream exceptions."""
572
+
573
+ # Timeout exceptions
574
+ class ConnectTimeout(TimeoutException):
575
+ """Timed out while connecting to the host."""
576
+
577
+ class ReadTimeout(TimeoutException):
578
+ """Timed out while receiving data from the host."""
579
+
580
+ class WriteTimeout(TimeoutException):
581
+ """Timed out while sending data to the host."""
582
+
583
+ class PoolTimeout(TimeoutException):
584
+ """Timed out waiting to acquire a connection from the pool."""
585
+
586
+ # Network exceptions
587
+ class ConnectError(NetworkError):
588
+ """Failed to establish a connection."""
589
+
590
+ class ReadError(NetworkError):
591
+ """Failed to receive data from the network."""
592
+
593
+ class WriteError(NetworkError):
594
+ """Failed to send data through the network."""
595
+
596
+ class CloseError(NetworkError):
597
+ """Failed to close a connection."""
598
+
599
+ # Protocol exceptions
600
+ class LocalProtocolError(ProtocolError):
601
+ """A protocol was violated by the client."""
602
+
603
+ class RemoteProtocolError(ProtocolError):
604
+ """The protocol was violated by the server."""
605
+
606
+ # Other transport/request exceptions
607
+ class UnsupportedProtocol(TransportError):
608
+ """Attempted to make a request to an unsupported protocol."""
609
+
610
+ class ProxyError(TransportError):
611
+ """An error occurred while establishing a proxy connection."""
612
+
613
+ class TooManyRedirects(RequestError):
614
+ """Too many redirects."""
615
+
616
+ class HTTPStatusError(HTTPError):
617
+ """The response had an error HTTP status of 4xx or 5xx."""
618
+
619
+ class DecodingError(RequestError):
620
+ """Decoding of the response failed, due to a malformed encoding."""
621
+
622
+ # Stream exceptions
623
+ class StreamConsumed(StreamError):
624
+ """Attempted to read or stream content, but the content has already been consumed."""
625
+
626
+ class ResponseNotRead(StreamError):
627
+ """Attempted to access streaming response content, without having called read()."""
628
+
629
+ class RequestNotRead(StreamError):
630
+ """Attempted to access streaming request content, without having called read()."""
631
+
632
+ class StreamClosed(StreamError):
633
+ """Attempted to read or stream response content, but the request has been closed."""
634
+
635
+ # Other exceptions
636
+ class InvalidURL(Exception):
637
+ """URL is improperly formed or cannot be parsed."""
638
+
639
+ class CookieConflict(Exception):
640
+ """Attempted to lookup a cookie by name, but multiple cookies existed."""
641
+
642
+ __all__ = [
643
+ # Type aliases
644
+ "HttpMethod",
645
+ "RequestParams",
646
+ "ClientRequestParams",
647
+ # Response types
648
+ "Response",
649
+ "StreamingResponse",
650
+ "CaseInsensitiveHeaderMap",
651
+ "TextIterator",
652
+ "LineIterator",
653
+ # Client classes
654
+ "RClient",
655
+ "Client",
656
+ "AsyncClient",
657
+ # Module-level functions
658
+ "request",
659
+ "get",
660
+ "head",
661
+ "options",
662
+ "delete",
663
+ "post",
664
+ "put",
665
+ "patch",
666
+ # Base exceptions
667
+ "HTTPError",
668
+ "RequestError",
669
+ "TransportError",
670
+ "NetworkError",
671
+ "TimeoutException",
672
+ "ProtocolError",
673
+ "StreamError",
674
+ # Timeout exceptions
675
+ "ConnectTimeout",
676
+ "ReadTimeout",
677
+ "WriteTimeout",
678
+ "PoolTimeout",
679
+ # Network exceptions
680
+ "ConnectError",
681
+ "ReadError",
682
+ "WriteError",
683
+ "CloseError",
684
+ # Protocol exceptions
685
+ "LocalProtocolError",
686
+ "RemoteProtocolError",
687
+ # Other transport/request exceptions
688
+ "UnsupportedProtocol",
689
+ "ProxyError",
690
+ "TooManyRedirects",
691
+ "HTTPStatusError",
692
+ "DecodingError",
693
+ # Stream exceptions
694
+ "StreamConsumed",
695
+ "ResponseNotRead",
696
+ "RequestNotRead",
697
+ "StreamClosed",
698
+ # Other exceptions
699
+ "InvalidURL",
700
+ "CookieConflict",
701
+ ]
httpr/py.typed ADDED
File without changes