impit 0.2.1__cp313-cp313-musllinux_1_2_x86_64.whl → 0.9.0__cp313-cp313-musllinux_1_2_x86_64.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.
impit/__init__.py CHANGED
@@ -1,9 +1,39 @@
1
+ from importlib import metadata
1
2
  from typing import Literal
2
3
 
4
+ from .cookies import Cookies
3
5
  from .impit import (
4
6
  AsyncClient,
5
7
  Client,
8
+ CloseError,
9
+ ConnectError,
10
+ ConnectTimeout,
11
+ CookieConflict,
12
+ DecodingError,
13
+ HTTPError,
14
+ HTTPStatusError,
15
+ InvalidURL,
16
+ LocalProtocolError,
17
+ NetworkError,
18
+ PoolTimeout,
19
+ ProtocolError,
20
+ ProxyError,
21
+ ReadError,
22
+ ReadTimeout,
23
+ RemoteProtocolError,
24
+ RequestError,
25
+ RequestNotRead,
6
26
  Response,
27
+ ResponseNotRead,
28
+ StreamClosed,
29
+ StreamConsumed,
30
+ StreamError,
31
+ TimeoutException,
32
+ TooManyRedirects,
33
+ TransportError,
34
+ UnsupportedProtocol,
35
+ WriteError,
36
+ WriteTimeout,
7
37
  delete,
8
38
  get,
9
39
  head,
@@ -11,14 +41,46 @@ from .impit import (
11
41
  patch,
12
42
  post,
13
43
  put,
44
+ stream,
14
45
  trace,
15
46
  )
16
47
 
48
+ __version__ = metadata.version('impit')
49
+
17
50
  __all__ = [
18
51
  'AsyncClient',
19
52
  'Browser',
20
53
  'Client',
54
+ 'CloseError',
55
+ 'ConnectError',
56
+ 'ConnectTimeout',
57
+ 'CookieConflict',
58
+ 'Cookies',
59
+ 'DecodingError',
60
+ 'HTTPError',
61
+ 'HTTPStatusError',
62
+ 'InvalidURL',
63
+ 'LocalProtocolError',
64
+ 'NetworkError',
65
+ 'PoolTimeout',
66
+ 'ProtocolError',
67
+ 'ProxyError',
68
+ 'ReadError',
69
+ 'ReadTimeout',
70
+ 'RemoteProtocolError',
71
+ 'RequestError',
72
+ 'RequestNotRead',
21
73
  'Response',
74
+ 'ResponseNotRead',
75
+ 'StreamClosed',
76
+ 'StreamConsumed',
77
+ 'StreamError',
78
+ 'TimeoutException',
79
+ 'TooManyRedirects',
80
+ 'TransportError',
81
+ 'UnsupportedProtocol',
82
+ 'WriteError',
83
+ 'WriteTimeout',
22
84
  'delete',
23
85
  'get',
24
86
  'head',
@@ -26,6 +88,7 @@ __all__ = [
26
88
  'patch',
27
89
  'post',
28
90
  'put',
91
+ 'stream',
29
92
  'trace',
30
93
  ]
31
94
 
impit/cookies.py ADDED
@@ -0,0 +1,177 @@
1
+ """Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8
+
9
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10
+
11
+ * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
+ """
15
+ # ruff: noqa: SIM102, E501
16
+
17
+ ## The Cookies class below is a modification of the `httpx.Cookies` class, licensed under the BSD 3-Clause "New" License.
18
+
19
+ from __future__ import annotations
20
+
21
+ import typing
22
+ from http.cookiejar import Cookie, CookieJar
23
+
24
+ from .impit import CookieConflict
25
+
26
+ CookieTypes = typing.Union['Cookies', CookieJar, dict[str, str], list[tuple[str, str]]]
27
+
28
+
29
+ class Cookies(typing.MutableMapping[str, str]):
30
+ """HTTP Cookies, as a mutable mapping."""
31
+
32
+ def __init__(self, cookies: CookieTypes | None = None) -> None:
33
+ if cookies is None or isinstance(cookies, dict):
34
+ self.jar = CookieJar()
35
+ if isinstance(cookies, dict):
36
+ for key, value in cookies.items():
37
+ self.set(key, value)
38
+ elif isinstance(cookies, list):
39
+ self.jar = CookieJar()
40
+ for key, value in cookies:
41
+ self.set(key, value)
42
+ elif isinstance(cookies, Cookies):
43
+ self.jar = CookieJar()
44
+ for cookie in cookies.jar:
45
+ self.jar.set_cookie(cookie)
46
+ else:
47
+ self.jar = cookies
48
+
49
+ def set(self, name: str, value: str, domain: str = '', path: str = '/') -> None:
50
+ """Set a cookie value by name. May optionally include domain and path."""
51
+ kwargs = {
52
+ 'version': 0,
53
+ 'name': name,
54
+ 'value': value,
55
+ 'port': None,
56
+ 'port_specified': False,
57
+ 'domain': domain,
58
+ 'domain_specified': bool(domain),
59
+ 'domain_initial_dot': domain.startswith('.'),
60
+ 'path': path,
61
+ 'path_specified': bool(path),
62
+ 'secure': False,
63
+ 'expires': None,
64
+ 'discard': True,
65
+ 'comment': None,
66
+ 'comment_url': None,
67
+ 'rest': {'HttpOnly': None},
68
+ 'rfc2109': False,
69
+ }
70
+ cookie = Cookie(**kwargs) # type: ignore[arg-type]
71
+ self.jar.set_cookie(cookie)
72
+
73
+ def get( # type: ignore[override]
74
+ self,
75
+ name: str,
76
+ default: str | None = None,
77
+ domain: str | None = None,
78
+ path: str | None = None,
79
+ ) -> str | None:
80
+ """Get a cookie by name.
81
+
82
+ May optionally include domain and path in order to specify exactly which cookie to retrieve.
83
+ """
84
+ value = None
85
+ for cookie in self.jar:
86
+ if cookie.name == name:
87
+ if domain is None or cookie.domain == domain:
88
+ if path is None or cookie.path == path:
89
+ if value is not None:
90
+ message = f'Multiple cookies exist with name={name}'
91
+ raise CookieConflict(message)
92
+ value = cookie.value
93
+
94
+ if value is None:
95
+ return default
96
+ return value
97
+
98
+ def delete(
99
+ self,
100
+ name: str,
101
+ domain: str | None = None,
102
+ path: str | None = None,
103
+ ) -> None:
104
+ """Delete a cookie by name.
105
+
106
+ May optionally include domain and path in order to specify exactly which cookie to delete.
107
+ """
108
+ if domain is not None and path is not None:
109
+ return self.jar.clear(domain, path, name)
110
+
111
+ remove = [
112
+ cookie
113
+ for cookie in self.jar
114
+ if cookie.name == name
115
+ and (domain is None or cookie.domain == domain)
116
+ and (path is None or cookie.path == path)
117
+ ]
118
+
119
+ for cookie in remove:
120
+ self.jar.clear(cookie.domain, cookie.path, cookie.name)
121
+ return None
122
+
123
+ def clear(self, domain: str | None = None, path: str | None = None) -> None:
124
+ """Delete all cookies.
125
+
126
+ Optionally include a domain and path in order to only delete a subset of all the cookies.
127
+ """
128
+ args = []
129
+ if domain is not None:
130
+ args.append(domain)
131
+ if path is not None:
132
+ assert domain is not None # noqa: S101
133
+ args.append(path)
134
+ self.jar.clear(*args)
135
+
136
+ def update(self, cookies: CookieTypes | None = None) -> None: # type: ignore[override]
137
+ """Update the cookie jar with new cookies. Accepts various types."""
138
+ cookies = Cookies(cookies)
139
+ for cookie in cookies.jar:
140
+ self.jar.set_cookie(cookie)
141
+
142
+ def __setitem__(self, name: str, value: str) -> None:
143
+ """Set a cookie by name."""
144
+ return self.set(name, value)
145
+
146
+ def __getitem__(self, name: str) -> str:
147
+ """Get a cookie by name."""
148
+ value = self.get(name)
149
+ if value is None:
150
+ raise KeyError(name)
151
+ return value
152
+
153
+ def __delitem__(self, name: str) -> None:
154
+ """Delete a cookie by name."""
155
+ return self.delete(name)
156
+
157
+ def __len__(self) -> int:
158
+ """Return the number of cookies in the jar."""
159
+ return len(self.jar)
160
+
161
+ def __iter__(self) -> typing.Iterator[str]:
162
+ """Return an iterator over the names of the cookies in the jar."""
163
+ return (cookie.name for cookie in self.jar)
164
+
165
+ def __bool__(self) -> bool:
166
+ """Return True if there are any cookies in the jar."""
167
+ for _ in self.jar:
168
+ return True
169
+ return False
170
+
171
+ def __repr__(self) -> str:
172
+ """Return a string representation of the Cookies object."""
173
+ cookies_repr = ', '.join(
174
+ [f'<Cookie {cookie.name}={cookie.value} for {cookie.domain} />' for cookie in self.jar]
175
+ )
176
+
177
+ return f'<Cookies[{cookies_repr}]>'
impit/impit.pyi CHANGED
@@ -1,44 +1,449 @@
1
1
  from __future__ import annotations
2
+ from http.cookiejar import CookieJar
3
+ from .cookies import Cookies
2
4
 
3
- from typing import Literal
5
+ from typing import Literal, Any
6
+ from collections.abc import Iterator, AsyncIterator
7
+ from contextlib import AbstractAsyncContextManager, AbstractContextManager
4
8
 
5
9
 
6
10
  Browser = Literal['chrome', 'firefox']
7
11
 
8
12
 
13
+ class HTTPError(Exception):
14
+ """Represents an HTTP-related error."""
15
+
16
+
17
+ class RequestError(HTTPError):
18
+ """Represents an error during the request process."""
19
+
20
+
21
+ class TransportError(RequestError):
22
+ """Represents a transport-layer error."""
23
+
24
+
25
+ class TimeoutException(TransportError):
26
+ """Represents a timeout error."""
27
+
28
+
29
+ class ConnectTimeout(TimeoutException):
30
+ """Represents a connection timeout error."""
31
+
32
+
33
+ class ReadTimeout(TimeoutException):
34
+ """Represents a read timeout error."""
35
+
36
+
37
+ class WriteTimeout(TimeoutException):
38
+ """Represents a write timeout error."""
39
+
40
+
41
+ class PoolTimeout(TimeoutException):
42
+ """Represents a connection pool timeout error."""
43
+
44
+
45
+ class NetworkError(TransportError):
46
+ """Represents a network-related error."""
47
+
48
+
49
+ class ConnectError(NetworkError):
50
+ """Represents a connection error."""
51
+
52
+
53
+ class ReadError(NetworkError):
54
+ """Represents a read error."""
55
+
56
+
57
+ class WriteError(NetworkError):
58
+ """Represents a write error."""
59
+
60
+
61
+ class CloseError(NetworkError):
62
+ """Represents an error when closing a connection."""
63
+
64
+
65
+ class ProtocolError(TransportError):
66
+ """Represents a protocol-related error."""
67
+
68
+
69
+ class LocalProtocolError(ProtocolError):
70
+ """Represents a local protocol error."""
71
+
72
+
73
+ class RemoteProtocolError(ProtocolError):
74
+ """Represents a remote protocol error."""
75
+
76
+
77
+ class ProxyError(TransportError):
78
+ """Represents a proxy-related error."""
79
+
80
+
81
+ class UnsupportedProtocol(TransportError):
82
+ """Represents an unsupported protocol error."""
83
+
84
+
85
+ class DecodingError(RequestError):
86
+ """Represents an error during response decoding."""
87
+
88
+
89
+ class TooManyRedirects(RequestError):
90
+ """Represents an error due to excessive redirects."""
91
+
92
+
93
+ class HTTPStatusError(HTTPError):
94
+ """Represents an error related to HTTP status codes."""
95
+
96
+
97
+ class InvalidURL(Exception):
98
+ """Represents an error due to an invalid URL."""
99
+
100
+
101
+ class CookieConflict(Exception):
102
+ """Represents a cookie conflict error."""
103
+
104
+
105
+ class StreamError(Exception):
106
+ """Represents a stream-related error."""
107
+
108
+
109
+ class StreamConsumed(StreamError):
110
+ """Represents an error when a stream is already consumed."""
111
+
112
+
113
+ class ResponseNotRead(StreamError):
114
+ """Represents an error when a response is not read."""
115
+
116
+
117
+ class RequestNotRead(StreamError):
118
+ """Represents an error when a request is not read."""
119
+
120
+
121
+ class StreamClosed(StreamError):
122
+ """Represents an error when a stream is closed."""
123
+
9
124
  class Response:
10
- """Response object returned by impit requests."""
125
+ """Response object returned by impit clients (:class:`Client` or :class:`AsyncClient` instances).
126
+
127
+ When constructed manually (e.g. for testing purposes), the following parameters can be provided:
128
+
129
+ Args:
130
+ status_code: HTTP status code of the response (e.g., 200, 404)
131
+ content: Response body as bytes (or None for empty body)
132
+ headers: Response headers as a dictionary (or None for empty headers)
133
+ default_encoding: Default encoding for the response text. Used only if `content-type` header is not present or does not specify a charset.
134
+ url: Final URL of the response.
135
+ """
11
136
 
12
137
  status_code: int
13
- """HTTP status code (e.g., 200, 404)"""
138
+ """HTTP status code of the response (e.g., 200, 404).
139
+
140
+ .. tip::
141
+
142
+ If the status code indicates an error (4xx or 5xx), you can raise an `HTTPStatusError` exception using the :meth:`raise_for_status` method.
143
+
144
+ .. code-block:: python
145
+
146
+ response = await client.get("https://crawlee.dev")
147
+ print(response.status_code) # 200
148
+ """
14
149
 
15
150
  reason_phrase: str
16
- """HTTP reason phrase (e.g., 'OK', 'Not Found')"""
151
+ """HTTP reason phrase for the response (e.g., 'OK', 'Not Found'). This maps the numerical :attr:`status_code` to a human-readable string.
152
+
153
+ .. code-block:: python
154
+
155
+ response = await client.get("https://crawlee.dev")
156
+ print(response.reason_phrase) # 'OK'
157
+ """
17
158
 
18
159
  http_version: str
19
- """HTTP version (e.g., 'HTTP/1.1', 'HTTP/2')"""
160
+ """HTTP version (e.g., 'HTTP/1.1', 'HTTP/2') negotiated for the response during the TLS handshake.
161
+
162
+ .. code-block:: python
163
+
164
+ response = await client.get("https://crawlee.dev")
165
+ print(response.http_version) # 'HTTP/2'
166
+ """
20
167
 
21
168
  headers: dict[str, str]
22
- """Response headers as a dictionary"""
169
+ """Response headers as a Python dictionary.
170
+
171
+ .. code-block:: python
172
+
173
+ response = await client.get("https://crawlee.dev")
174
+ print(response.headers) # {'content-type': 'text/html; charset=utf-8', ... }
175
+ """
23
176
 
24
177
  text: str
25
- """Response body as text. Decoded from `content` using `encoding`."""
178
+ """Response body as text. Decoded from :attr:`content` using :attr:`encoding`.
179
+
180
+ .. code-block:: python
181
+
182
+ response = await client.get("https://crawlee.dev")
183
+ print(response.text) # '<!DOCTYPE html>...'
184
+ """
26
185
 
27
186
  encoding: str
28
- """Response content encoding"""
187
+ """Response content encoding. Determined from `content-type` header or by bytestream prescan. Falls back to 'utf-8' if not found.
188
+
189
+ .. code-block:: python
190
+
191
+ response = await client.get("https://crawlee.dev")
192
+ print(response.encoding) # 'utf-8'
193
+
194
+ This can be used to decode the `Response` body manually. By default, :attr:`text` uses this encoding to decode :attr:`content`.
195
+ """
29
196
 
30
197
  is_redirect: bool
31
- """Whether the response is a redirect"""
198
+ """`True` if the response is a redirect (has a 3xx status code), `False` otherwise.
199
+
200
+ .. code-block:: python
201
+
202
+ response = await client.get("https://crawlee.dev")
203
+ print(response.is_redirect) # False
204
+ """
32
205
 
33
206
  url: str
34
- """Final URL"""
207
+ """The final URL of the response. This may differ from the requested URL if redirects were followed (see the `follow_redirects` parameter in :class:`Client` and :class:`AsyncClient`).
208
+
209
+ .. code-block:: python
210
+
211
+ response = await client.get("https://crawlee.dev")
212
+ print(response.url) # 'https://crawlee.dev'
213
+ """
35
214
 
36
215
  content: bytes
37
- """Response body as bytes"""
216
+ """Contains the response body as bytes. If the response was created with `stream=True`, this will be empty until the content is read using :meth:`read` or :meth:`iter_bytes`.
217
+
218
+ .. code-block:: python
219
+
220
+ response = await client.get("https://crawlee.dev")
221
+ print(response.content) # b'<!DOCTYPE html>...'
222
+ """
223
+
224
+ is_closed: bool
225
+ """
226
+ True if the response has been closed using the :meth:`close` method, `False` otherwise.
227
+
228
+ Closing a response releases any underlying resources (e.g., network connections).
229
+
230
+ .. code-block:: python
231
+
232
+ response = await client.get("https://crawlee.dev")
233
+ print(response.is_closed) # False
234
+ response.close()
235
+ print(response.is_closed) # True
236
+ """
237
+
238
+ is_stream_consumed: bool
239
+ """Whether the response stream has been consumed or closed.
240
+
241
+ If this is `True`, calling :meth:`read` or :meth:`iter_bytes` will raise a :class:`StreamConsumed` or :class:`StreamClosed` error.
242
+
243
+ The read response body is still available in the :attr:`content` attribute.
244
+
245
+ .. code-block:: python
246
+
247
+ response = await client.get("https://crawlee.dev", stream=True)
248
+ print(response.is_stream_consumed) # False
249
+ for chunk in response.iter_bytes():
250
+ pass
251
+ print(response.is_stream_consumed) # True
252
+ # calling response.read() or response.iter_bytes() again will raise StreamConsumed error
253
+ # read the content of the response using response.content
254
+ """
255
+
256
+ def __init__(
257
+ self,
258
+ status_code: int,
259
+ *,
260
+ content: bytes | None = None,
261
+ headers: dict[str, str] | None = None,
262
+ default_encoding: str | None = None,
263
+ url: str | None = None,
264
+ ) -> None:
265
+ """Initialize a Response object.
266
+
267
+ Args:
268
+ status_code: HTTP status code
269
+ content: Response body as bytes
270
+ headers: Response headers as a dictionary
271
+ default_encoding: Default encoding for the response text. Used only if `content-type` header is not present or does not specify a charset.
272
+ url: Final URL of the response
273
+ """
274
+
275
+ def read(self) -> bytes:
276
+ """Read the response content as bytes. Synchronous version of :meth:`aread`.
277
+
278
+ Useful for consuming the entire response body in one go (not chunked).
279
+
280
+ .. code-block:: python
281
+
282
+ with Client() as client:
283
+ with client.stream("GET", "https://example.com/largefile") as response:
284
+ content = response.read()
285
+ process(content) # Process the entire content at once
286
+ """
287
+ def iter_bytes(self) -> Iterator[bytes]:
288
+ """Iterate over the response content in chunks. Synchronous version of :meth:`aiter_bytes`.
289
+
290
+ Useful for streaming large responses without loading the entire content into memory.
291
+ .. code-block:: python
292
+
293
+ with Client() as client:
294
+ with client.stream("GET", "https://example.com/largefile") as response:
295
+ for chunk in response.iter_bytes():
296
+ process(chunk) # Process each chunk as it is received
297
+ """
298
+
299
+ async def aread(self) -> bytes:
300
+ """Asynchronously read the response content as bytes. Asynchronous version of :meth:`read`.
301
+
302
+ Useful for consuming the entire response body in one go (not chunked).
303
+
304
+ .. code-block:: python
305
+
306
+ async with AsyncClient() as client:
307
+ async with client.stream("GET", "https://example.com/largefile") as response:
308
+ content = await response.aread()
309
+ process(content) # Process the entire content at once
310
+ """
311
+
312
+ def aiter_bytes(self) -> AsyncIterator[bytes]:
313
+ """Asynchronously iterate over the response content in chunks. Asynchronous version of :meth:`iter_bytes`.
314
+
315
+ Useful for streaming large responses without loading the entire content into memory.
316
+
317
+ .. code-block:: python
318
+
319
+ async with AsyncClient() as client:
320
+ async with client.stream("GET", "https://example.com/largefile") as response:
321
+ async for chunk in response.aiter_bytes():
322
+ process(chunk) # Process each chunk as it is received
323
+ """
324
+
325
+ def json(self) -> Any:
326
+ """Parse the response content as JSON.
327
+
328
+ .. note::
329
+ This method will raise a `DecodingError` if the response content is not valid JSON.
330
+
331
+ .. code-block:: python
332
+
333
+ response = await client.get("https://api.example.com/data")
334
+ data = response.json()
335
+ print(data) # Parsed JSON data as a Python object (dict, list, etc.)
336
+ """
337
+
338
+ def close(self) -> None:
339
+ """Close the response and release resources.
340
+
341
+ .. warning::
342
+ You should not need to call this method directly.
343
+
344
+ Use the `with` statement to ensure proper resource management when working with synchronous clients.
345
+
346
+ .. code-block:: python
38
347
 
348
+ with impit.stream('GET', get_httpbin_url('/')) as response:
349
+ assert response.status_code == 200
350
+
351
+ assert response.is_closed is True
352
+ """
353
+
354
+ async def aclose(self) -> None:
355
+ """Asynchronously close the response and release resources.
356
+
357
+ .. note::
358
+ This method is for internal use only.
359
+
360
+ Use the `async with` statement to ensure proper resource management when working with asynchronous clients.
361
+
362
+ .. code-block:: python
363
+
364
+ async with impit.stream('GET', get_httpbin_url('/')) as response:
365
+ assert response.status_code == 200
366
+
367
+ assert response.is_closed is True
368
+ """
39
369
 
40
370
  class Client:
41
- """Synchronous HTTP client with browser impersonation capabilities."""
371
+ """Synchronous HTTP client with browser impersonation capabilities.
372
+
373
+ .. note::
374
+ You can reuse the :class:`Client` instance to make multiple requests.
375
+
376
+ All requests made by the same client will share the same configuration, resources (e.g., cookie jar and connection pool), and other settings.
377
+
378
+ Args:
379
+ browser: Browser to impersonate (`"chrome"` or `"firefox"`).
380
+
381
+ If this is `None` (default), no impersonation is performed.
382
+ http3:
383
+
384
+ If set to `True`, Impit will try to connect to the target servers using HTTP/3 protocol (if supported by the server).
385
+
386
+ .. note::
387
+ The HTTP/3 support is experimental and may not work with all servers. The impersonation capabilities are limited when using HTTP/3.
388
+
389
+ proxy:
390
+
391
+ The proxy URL to use for all the requests made by this client.
392
+
393
+ This can be an HTTP, HTTPS, or SOCKS proxy.
394
+ timeout:
395
+ Default request timeout in seconds.
396
+
397
+ This value can be overridden for individual requests.
398
+ verify:
399
+ If set to `False`, impit will not verify SSL certificates.
400
+
401
+ This can be used to ignore TLS errors (e.g., self-signed certificates).
402
+
403
+ True by default.
404
+ default_encoding:
405
+ Default encoding for response.text field (e.g., "utf-8", "cp1252").
406
+
407
+ Overrides `content-type` headers and bytestream prescan.
408
+ follow_redirects:
409
+
410
+ If set to `True` the client will automatically follow HTTP redirects (3xx responses).
411
+
412
+ `False` by default.
413
+ max_redirects:
414
+
415
+ Maximum number of redirects to follow if the `follow_redirects` option is enabled.
416
+
417
+ Default is 20.
418
+ cookie_jar:
419
+
420
+ Cookie jar to store cookies in.
421
+
422
+ This is a `http.cookiejar.CookieJar` instance.
423
+
424
+ By default, :class:`Client` doesn't store cookies between requests.
425
+ cookies: httpx-compatible cookies object.
426
+
427
+ These are the cookies to include in all requests (to the matching servers) made by this client.
428
+ headers: Default HTTP headers to include in requests.
429
+
430
+ These headers will be included in all requests made by this client.
431
+
432
+ Default is an empty dictionary.
433
+ local_address:
434
+
435
+ Local address to bind the client to.
436
+
437
+ Useful for testing purposes or when you want to bind the client to a specific network interface.
438
+ Can be an IP address in the format `xxx.xxx.xxx.xxx` (for IPv4) or `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff` (for IPv6).
439
+ """
440
+
441
+ def __enter__(self) -> Client:
442
+ """Enter the runtime context related to this object."""
443
+
444
+ def __exit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: object | None) -> None:
445
+ """Exit the runtime context related to this object."""
446
+
42
447
 
43
448
  def __init__(
44
449
  self,
@@ -50,6 +455,10 @@ class Client:
50
455
  default_encoding: str | None = None,
51
456
  follow_redirects: bool | None = None,
52
457
  max_redirects: int | None = None,
458
+ cookie_jar: CookieJar | None = None,
459
+ cookies: Cookies | None = None,
460
+ headers: dict[str, str] | None = None,
461
+ local_address: str | None = None,
53
462
  ) -> None:
54
463
  """Initialize a synchronous HTTP client.
55
464
 
@@ -63,6 +472,11 @@ class Client:
63
472
  header and bytestream prescan.
64
473
  follow_redirects: Whether to follow redirects (default: False)
65
474
  max_redirects: Maximum number of redirects to follow (default: 20)
475
+ cookie_jar: Cookie jar to store cookies in.
476
+ cookies: httpx-compatible cookies object.
477
+ headers: Default HTTP headers to include in requests.
478
+ local_address: Local address to bind the client to. Useful for testing purposes or when you want to bind the client to a specific network interface.
479
+ Can be an IP address in the format "xxx.xxx.xxx.xxx" (for IPv4) or "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" (for IPv6).
66
480
  """
67
481
 
68
482
  def get(
@@ -235,9 +649,44 @@ class Client:
235
649
  headers: dict[str, str] | None = None,
236
650
  timeout: float | None = None,
237
651
  force_http3: bool | None = None,
652
+ stream: bool = False,
238
653
  ) -> Response:
239
654
  """Make an HTTP request with the specified method.
240
655
 
656
+ Args:
657
+ method: HTTP method (e.g., "get", "post")
658
+ url: URL to request
659
+ content: Raw content to send
660
+ data: Form data to send in request body
661
+ headers: HTTP headers
662
+ timeout: Request timeout in seconds (overrides default timeout)
663
+ force_http3: Force HTTP/3 protocol
664
+ stream: Whether to return a streaming response (default: False)
665
+ """
666
+
667
+ def stream(
668
+ self,
669
+ method: str,
670
+ url: str,
671
+ content: bytes | bytearray | list[int] | None = None,
672
+ data: dict[str, str] | None = None,
673
+ headers: dict[str, str] | None = None,
674
+ timeout: float | None = None,
675
+ force_http3: bool | None = None,
676
+ ) -> AbstractContextManager[Response]:
677
+ """Make a streaming request with the specified method.
678
+
679
+ This method returns a context manager that yields a streaming :class:`Response` object.
680
+
681
+ See the following example for usage:
682
+
683
+ .. code-block:: python
684
+
685
+ with Client() as client:
686
+ with client.stream("GET", "https://example.com/largefile") as response:
687
+ for chunk in response.iter_bytes():
688
+ process(chunk) # Process each chunk as it is received
689
+
241
690
  Args:
242
691
  method: HTTP method (e.g., "get", "post")
243
692
  url: URL to request
@@ -250,7 +699,81 @@ class Client:
250
699
 
251
700
 
252
701
  class AsyncClient:
253
- """Asynchronous HTTP client with browser impersonation capabilities."""
702
+ """Asynchronous HTTP client with browser impersonation capabilities.
703
+
704
+ .. note::
705
+ You can reuse the :class:`Client` instance to make multiple requests.
706
+
707
+ All requests made by the same client will share the same configuration, resources (e.g., cookie jar and connection pool), and other settings.
708
+
709
+ Args:
710
+ browser: Browser to impersonate (`"chrome"` or `"firefox"`).
711
+
712
+ If this is `None` (default), no impersonation is performed.
713
+ http3:
714
+
715
+ If set to `True`, Impit will try to connect to the target servers using HTTP/3 protocol (if supported by the server).
716
+
717
+ .. note::
718
+ The HTTP/3 support is experimental and may not work with all servers. The impersonation capabilities are limited when using HTTP/3.
719
+
720
+ proxy:
721
+
722
+ The proxy URL to use for all the requests made by this client.
723
+
724
+ This can be an HTTP, HTTPS, or SOCKS proxy.
725
+ timeout:
726
+ Default request timeout in seconds.
727
+
728
+ This value can be overridden for individual requests.
729
+ verify:
730
+ If set to `False`, impit will not verify SSL certificates.
731
+
732
+ This can be used to ignore TLS errors (e.g., self-signed certificates).
733
+
734
+ True by default.
735
+ default_encoding:
736
+ Default encoding for response.text field (e.g., "utf-8", "cp1252").
737
+
738
+ Overrides `content-type` headers and bytestream prescan.
739
+ follow_redirects:
740
+
741
+ If set to `True` the client will automatically follow HTTP redirects (3xx responses).
742
+
743
+ `False` by default.
744
+ max_redirects:
745
+
746
+ Maximum number of redirects to follow if the `follow_redirects` option is enabled.
747
+
748
+ Default is 20.
749
+ cookie_jar:
750
+
751
+ Cookie jar to store cookies in.
752
+
753
+ This is a `http.cookiejar.CookieJar` instance.
754
+
755
+ By default, :class:`Client` doesn't store cookies between requests.
756
+ cookies: httpx-compatible cookies object.
757
+
758
+ These are the cookies to include in all requests (to the matching servers) made by this client.
759
+ headers: Default HTTP headers to include in requests.
760
+
761
+ These headers will be included in all requests made by this client.
762
+
763
+ Default is an empty dictionary.
764
+ local_address:
765
+
766
+ Local address to bind the client to.
767
+
768
+ Useful for testing purposes or when you want to bind the client to a specific network interface.
769
+ Can be an IP address in the format `xxx.xxx.xxx.xxx` (for IPv4) or `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff` (for IPv6).
770
+ """
771
+
772
+ async def __aenter__(self) -> AsyncClient:
773
+ """Enter the runtime context related to this object."""
774
+
775
+ async def __aexit__(self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: object | None) -> None:
776
+ """Exit the runtime context related to this object."""
254
777
 
255
778
  def __init__(
256
779
  self,
@@ -262,6 +785,10 @@ class AsyncClient:
262
785
  default_encoding: str | None = None,
263
786
  follow_redirects: bool | None = None,
264
787
  max_redirects: int | None = None,
788
+ cookie_jar: CookieJar | None = None,
789
+ cookies: Cookies | None = None,
790
+ headers: dict[str, str] | None = None,
791
+ local_address: str | None = None,
265
792
  ) -> None:
266
793
  """Initialize an asynchronous HTTP client.
267
794
 
@@ -275,6 +802,11 @@ class AsyncClient:
275
802
  header and bytestream prescan.
276
803
  follow_redirects: Whether to follow redirects (default: False)
277
804
  max_redirects: Maximum number of redirects to follow (default: 20)
805
+ cookie_jar: Cookie jar to store cookies in.
806
+ cookies: httpx-compatible cookies object.
807
+ headers: Default HTTP headers to include in requests.
808
+ local_address: Local address to bind the client to. Useful for testing purposes or when you want to bind the client to a specific network interface.
809
+ Can be an IP address in the format "xxx.xxx.xxx.xxx" (for IPv4) or "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" (for IPv6).
278
810
  """
279
811
 
280
812
  async def get(
@@ -315,6 +847,7 @@ class AsyncClient:
315
847
  headers: HTTP headers
316
848
  timeout: Request timeout in seconds (overrides default timeout)
317
849
  force_http3: Force HTTP/3 protocol
850
+
318
851
  """
319
852
 
320
853
  async def put(
@@ -446,9 +979,44 @@ class AsyncClient:
446
979
  headers: dict[str, str] | None = None,
447
980
  timeout: float | None = None,
448
981
  force_http3: bool | None = None,
982
+ stream: bool = False,
449
983
  ) -> Response:
450
984
  """Make an asynchronous HTTP request with the specified method.
451
985
 
986
+ Args:
987
+ method: HTTP method (e.g., "get", "post")
988
+ url: URL to request
989
+ content: Raw content to send
990
+ data: Form data to send in request body
991
+ headers: HTTP headers
992
+ timeout: Request timeout in seconds (overrides default timeout)
993
+ force_http3: Force HTTP/3 protocol
994
+ stream: Whether to return a streaming response (default: False)
995
+ """
996
+
997
+ def stream(
998
+ self,
999
+ method: str,
1000
+ url: str,
1001
+ content: bytes | bytearray | list[int] | None = None,
1002
+ data: dict[str, str] | None = None,
1003
+ headers: dict[str, str] | None = None,
1004
+ timeout: float | None = None,
1005
+ force_http3: bool | None = None,
1006
+ ) -> AbstractAsyncContextManager[Response]:
1007
+ """Make an asynchronous streaming request with the specified method.
1008
+
1009
+ This method returns a AsyncContextManager that yields a streaming :class:`Response` object.
1010
+
1011
+ See the following example for usage:
1012
+
1013
+ .. code-block:: python
1014
+
1015
+ with Client() as client:
1016
+ with client.stream("GET", "https://example.com/largefile") as response:
1017
+ for chunk in response.iter_bytes():
1018
+ process(chunk) # Process each chunk as it is received
1019
+
452
1020
  Args:
453
1021
  method: HTTP method (e.g., "get", "post")
454
1022
  url: URL to request
@@ -460,6 +1028,40 @@ class AsyncClient:
460
1028
  """
461
1029
 
462
1030
 
1031
+ def stream(
1032
+ method: str,
1033
+ url: str,
1034
+ content: bytes | bytearray | list[int] | None = None,
1035
+ data: dict[str, str] | None = None,
1036
+ headers: dict[str, str] | None = None,
1037
+ timeout: float | None = None,
1038
+ force_http3: bool | None = None,
1039
+ follow_redirects: bool | None = None,
1040
+ max_redirects: int | None = None,
1041
+ cookie_jar: CookieJar | None = None,
1042
+ cookies: Cookies | None = None,
1043
+ proxy: str | None = None,
1044
+ ) -> AbstractContextManager[Response]:
1045
+ """Make a streaming request without creating a client instance.
1046
+
1047
+ Args:
1048
+ method: HTTP method (e.g., "get", "post")
1049
+ url: URL to request
1050
+ content: Raw content to send
1051
+ data: Form data to send in request body
1052
+ headers: HTTP headers
1053
+ timeout: Request timeout in seconds
1054
+ force_http3: Force HTTP/3 protocol
1055
+ follow_redirects: Whether to follow redirects (default: False)
1056
+ max_redirects: Maximum number of redirects to follow (default: 20)
1057
+ cookie_jar: Cookie jar to store cookies in.
1058
+ cookies: httpx-compatible cookies object.
1059
+ proxy: Proxy URL to use to make the request.
1060
+
1061
+ Returns:
1062
+ Response object
1063
+ """
1064
+
463
1065
  def get(
464
1066
  url: str,
465
1067
  content: bytes | bytearray | list[int] | None = None,
@@ -467,6 +1069,11 @@ def get(
467
1069
  headers: dict[str, str] | None = None,
468
1070
  timeout: float | None = None,
469
1071
  force_http3: bool | None = None,
1072
+ follow_redirects: bool | None = None,
1073
+ max_redirects: int | None = None,
1074
+ cookie_jar: CookieJar | None = None,
1075
+ cookies: Cookies | None = None,
1076
+ proxy: str | None = None,
470
1077
  ) -> Response:
471
1078
  """Make a GET request without creating a client instance.
472
1079
 
@@ -477,6 +1084,11 @@ def get(
477
1084
  headers: HTTP headers
478
1085
  timeout: Request timeout in seconds
479
1086
  force_http3: Force HTTP/3 protocol
1087
+ follow_redirects: Whether to follow redirects (default: False)
1088
+ max_redirects: Maximum number of redirects to follow (default: 20)
1089
+ cookie_jar: Cookie jar to store cookies in.
1090
+ cookies: httpx-compatible cookies object.
1091
+ proxy: Proxy URL to use to make the request.
480
1092
 
481
1093
  Returns:
482
1094
  Response object
@@ -490,6 +1102,11 @@ def post(
490
1102
  headers: dict[str, str] | None = None,
491
1103
  timeout: float | None = None,
492
1104
  force_http3: bool | None = None,
1105
+ follow_redirects: bool | None = None,
1106
+ max_redirects: int | None = None,
1107
+ cookie_jar: CookieJar | None = None,
1108
+ cookies: Cookies | None = None,
1109
+ proxy: str | None = None,
493
1110
  ) -> Response:
494
1111
  """Make a POST request without creating a client instance.
495
1112
 
@@ -500,6 +1117,11 @@ def post(
500
1117
  headers: HTTP headers
501
1118
  timeout: Request timeout in seconds
502
1119
  force_http3: Force HTTP/3 protocol
1120
+ follow_redirects: Whether to follow redirects (default: False)
1121
+ max_redirects: Maximum number of redirects to follow (default: 20)
1122
+ cookie_jar: Cookie jar to store cookies in.
1123
+ cookies: httpx-compatible cookies object.
1124
+ proxy: Proxy URL to use to make the request.
503
1125
 
504
1126
  Returns:
505
1127
  Response object
@@ -513,6 +1135,11 @@ def put(
513
1135
  headers: dict[str, str] | None = None,
514
1136
  timeout: float | None = None,
515
1137
  force_http3: bool | None = None,
1138
+ follow_redirects: bool | None = None,
1139
+ max_redirects: int | None = None,
1140
+ cookie_jar: CookieJar | None = None,
1141
+ cookies: Cookies | None = None,
1142
+ proxy: str | None = None,
516
1143
  ) -> Response:
517
1144
  """Make a PUT request without creating a client instance.
518
1145
 
@@ -523,6 +1150,11 @@ def put(
523
1150
  headers: HTTP headers
524
1151
  timeout: Request timeout in seconds
525
1152
  force_http3: Force HTTP/3 protocol
1153
+ follow_redirects: Whether to follow redirects (default: False)
1154
+ max_redirects: Maximum number of redirects to follow (default: 20)
1155
+ cookie_jar: Cookie jar to store cookies in.
1156
+ cookies: httpx-compatible cookies object.
1157
+ proxy: Proxy URL to use to make the request.
526
1158
 
527
1159
  Returns:
528
1160
  Response object
@@ -536,6 +1168,11 @@ def patch(
536
1168
  headers: dict[str, str] | None = None,
537
1169
  timeout: float | None = None,
538
1170
  force_http3: bool | None = None,
1171
+ follow_redirects: bool | None = None,
1172
+ max_redirects: int | None = None,
1173
+ cookie_jar: CookieJar | None = None,
1174
+ cookies: Cookies | None = None,
1175
+ proxy: str | None = None,
539
1176
  ) -> Response:
540
1177
  """Make a PATCH request without creating a client instance.
541
1178
 
@@ -546,6 +1183,11 @@ def patch(
546
1183
  headers: HTTP headers
547
1184
  timeout: Request timeout in seconds
548
1185
  force_http3: Force HTTP/3 protocol
1186
+ follow_redirects: Whether to follow redirects (default: False)
1187
+ max_redirects: Maximum number of redirects to follow (default: 20)
1188
+ cookie_jar: Cookie jar to store cookies in.
1189
+ cookies: httpx-compatible cookies object.
1190
+ proxy: Proxy URL to use to make the request.
549
1191
 
550
1192
  Returns:
551
1193
  Response object
@@ -559,6 +1201,11 @@ def delete(
559
1201
  headers: dict[str, str] | None = None,
560
1202
  timeout: float | None = None,
561
1203
  force_http3: bool | None = None,
1204
+ follow_redirects: bool | None = None,
1205
+ max_redirects: int | None = None,
1206
+ cookie_jar: CookieJar | None = None,
1207
+ cookies: Cookies | None = None,
1208
+ proxy: str | None = None,
562
1209
  ) -> Response:
563
1210
  """Make a DELETE request without creating a client instance.
564
1211
 
@@ -569,6 +1216,11 @@ def delete(
569
1216
  headers: HTTP headers
570
1217
  timeout: Request timeout in seconds
571
1218
  force_http3: Force HTTP/3 protocol
1219
+ follow_redirects: Whether to follow redirects (default: False)
1220
+ max_redirects: Maximum number of redirects to follow (default: 20)
1221
+ cookie_jar: Cookie jar to store cookies in.
1222
+ cookies: httpx-compatible cookies object.
1223
+ proxy: Proxy URL to use to make the request.
572
1224
 
573
1225
  Returns:
574
1226
  Response object
@@ -582,6 +1234,11 @@ def head(
582
1234
  headers: dict[str, str] | None = None,
583
1235
  timeout: float | None = None,
584
1236
  force_http3: bool | None = None,
1237
+ follow_redirects: bool | None = None,
1238
+ max_redirects: int | None = None,
1239
+ cookie_jar: CookieJar | None = None,
1240
+ cookies: Cookies | None = None,
1241
+ proxy: str | None = None,
585
1242
  ) -> Response:
586
1243
  """Make a HEAD request without creating a client instance.
587
1244
 
@@ -592,6 +1249,11 @@ def head(
592
1249
  headers: HTTP headers
593
1250
  timeout: Request timeout in seconds
594
1251
  force_http3: Force HTTP/3 protocol
1252
+ follow_redirects: Whether to follow redirects (default: False)
1253
+ max_redirects: Maximum number of redirects to follow (default: 20)
1254
+ cookie_jar: Cookie jar to store cookies in.
1255
+ cookies: httpx-compatible cookies object.
1256
+ proxy: Proxy URL to use to make the request.
595
1257
 
596
1258
  Returns:
597
1259
  Response object
@@ -605,6 +1267,11 @@ def options(
605
1267
  headers: dict[str, str] | None = None,
606
1268
  timeout: float | None = None,
607
1269
  force_http3: bool | None = None,
1270
+ follow_redirects: bool | None = None,
1271
+ max_redirects: int | None = None,
1272
+ cookie_jar: CookieJar | None = None,
1273
+ cookies: Cookies | None = None,
1274
+ proxy: str | None = None,
608
1275
  ) -> Response:
609
1276
  """Make an OPTIONS request without creating a client instance.
610
1277
 
@@ -615,6 +1282,11 @@ def options(
615
1282
  headers: HTTP headers
616
1283
  timeout: Request timeout in seconds (overrides default timeout)
617
1284
  force_http3: Force HTTP/3 protocol
1285
+ follow_redirects: Whether to follow redirects (default: False)
1286
+ max_redirects: Maximum number of redirects to follow (default: 20)
1287
+ cookie_jar: Cookie jar to store cookies in.
1288
+ cookies: httpx-compatible cookies object.
1289
+ proxy: Proxy URL to use to make the request.
618
1290
  """
619
1291
 
620
1292
 
@@ -625,6 +1297,11 @@ def trace(
625
1297
  headers: dict[str, str] | None = None,
626
1298
  timeout: float | None = None,
627
1299
  force_http3: bool | None = None,
1300
+ follow_redirects: bool | None = None,
1301
+ max_redirects: int | None = None,
1302
+ cookie_jar: CookieJar | None = None,
1303
+ cookies: Cookies | None = None,
1304
+ proxy: str | None = None,
628
1305
  ) -> Response:
629
1306
  """Make a TRACE request without creating a client instance.
630
1307
 
@@ -635,4 +1312,9 @@ def trace(
635
1312
  headers: HTTP headers
636
1313
  timeout: Request timeout in seconds (overrides default timeout)
637
1314
  force_http3: Force HTTP/3 protocol
1315
+ follow_redirects: Whether to follow redirects (default: False)
1316
+ max_redirects: Maximum number of redirects to follow (default: 20)
1317
+ cookie_jar: Cookie jar to store cookies in.
1318
+ cookies: httpx-compatible cookies object.
1319
+ proxy: Proxy URL to use to make the request.
638
1320
  """
@@ -1,30 +1,35 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: impit
3
- Version: 0.2.1
3
+ Version: 0.9.0
4
4
  Classifier: Development Status :: 4 - Beta
5
+ Classifier: Environment :: Console
5
6
  Classifier: Intended Audience :: Developers
6
7
  Classifier: License :: OSI Approved :: Apache Software License
7
8
  Classifier: Operating System :: OS Independent
8
- Classifier: Programming Language :: Python :: 3.9
9
9
  Classifier: Programming Language :: Python :: 3.10
10
10
  Classifier: Programming Language :: Python :: 3.11
11
11
  Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
13
14
  Classifier: Topic :: Software Development :: Libraries
14
15
  Summary: A library for making HTTP requests through browser impersonation
15
16
  Keywords: apify,http,requests,browser,impersonation
16
17
  Author: Jindřich Bär
17
- Requires-Python: >=3.9
18
+ Requires-Python: >=3.10
18
19
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
19
- Project-URL: Homepage, https://apify.github.io/impit/
20
+ Project-URL: Homepage, https://apify.github.io/impit/python
20
21
  Project-URL: Apify homepage, https://apify.com
21
- Project-URL: Changelog, https://github.com/apify/impit/blob/master/CHANGELOG.md
22
- Project-URL: Documentation, https://apify.github.io/impit/
22
+ Project-URL: Changelog, https://github.com/apify/impit/blob/master/impit-python/CHANGELOG.md
23
+ Project-URL: Documentation, https://apify.github.io/impit/python
23
24
  Project-URL: Issue tracker, https://github.com/apify/impit/issues
24
25
  Project-URL: Repository, https://github.com/apify/impit
25
26
 
26
27
  # `impit` for Python
27
28
 
29
+ > This documents the `impit` Python package, which provides bindings for the `impit` library.
30
+ >
31
+ > See documentation for the JavaScript/TypeScript version of `impit` [here](https://apify.github.io/impit/js/).
32
+
28
33
  `impit` is a Python package that provides bindings for the [`impit`](https://github.com/apify/impit) library.
29
34
 
30
35
  It allows you to switch the TLS fingerprints and the HTTP headers of your requests, while still using the same API as `httpx` or `requests`.
@@ -0,0 +1,9 @@
1
+ impit-0.9.0.dist-info/METADATA,sha256=rUem-pBLg6yAp-5ARgqksLUOut5yrVmou21VKAv3Qy0,2833
2
+ impit-0.9.0.dist-info/WHEEL,sha256=k_XiDtlLA7dbYGdvJSsIA4CTI9KZm58eaZX7iYUYBGQ,108
3
+ impit.libs/libgcc_s-98a1ef30.so.1,sha256=XOVRhHznCIpbSdhFoozhla-OfRqBtXftKPQ4cSMKjrs,433441
4
+ impit/__init__.py,sha256=uZsD0XMFOt7QwVUgNcnVQkDF85y9n5slxoxR0wim7ug,1664
5
+ impit/cookies.py,sha256=bfNPkMk2Y0bG-RqqMMJxIfvCMZp_fDclYAnSlsCwkIg,7009
6
+ impit/impit.cpython-313-x86_64-linux-musl.so,sha256=UlbcfPotbcezKVMCglRUniWlPDVXd8go_hfY75MGhBU,14832353
7
+ impit/impit.pyi,sha256=IxLCuSkx3mHQcwcSS65lveoM5QYvMeJcU-Mhhdelyng,45730
8
+ impit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ impit-0.9.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.8.3)
2
+ Generator: maturin (1.10.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313-musllinux_1_2_x86_64
@@ -1,8 +0,0 @@
1
- impit-0.2.1.dist-info/METADATA,sha256=FhD2ueaHS0b6c14Vi2Z_IGwTpgKz7aJrggdmg98VBoQ,2562
2
- impit-0.2.1.dist-info/WHEEL,sha256=FhhfBGxpw7lUS1vCU0xMKSc8xZ-17phLfvnaRtwemzY,107
3
- impit.libs/libgcc_s-1e52349c.so.1,sha256=Ani0u_W_tB8ESsFePO4D2mn2lrgWLhyFdw6RETxBTYM,437537
4
- impit/__init__.py,sha256=112MOaJD7xu13dnU1b1GHQO41EI61pQE6LdTeojqWqg,400
5
- impit/impit.pyi,sha256=wPx29TNIyK9xTazeb-KPnXeYrNc0wMZnqSXzYCPPfRc,19775
6
- impit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- impit/impit.cpython-313-x86_64-linux-musl.so,sha256=NpYA1D-bxIVeNNZ5ruehi1S9Fb_mrUckvjxXMkrYnH0,14520689
8
- impit-0.2.1.dist-info/RECORD,,