Initial public upload from Projekte/CodingAgent (part 3)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/METADATA +203 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/RECORD +32 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/WHEEL +4 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/entry_points.txt +2 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md +12 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_urlparse.py +527 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_urls.py +641 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_utils.py +242 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/py.typed +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/METADATA +351 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/RECORD +13 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/WHEEL +5 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/licenses/LICENSE +21 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/top_level.txt +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/__init__.py +14 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_api.py +93 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_decoders.py +139 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_exceptions.py +5 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_models.py +54 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/py.typed +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/METADATA +155 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/RECORD +19 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/WHEEL +4 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/entry_points.txt +3 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/licenses/LICENSE.md +31 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/__init__.py +45 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/__main__.py +6 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/cli.py +128 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/codec.py +159 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/compat.py +41 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/core.py +648 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/idnadata.py +1897 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/intranges.py +55 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/package_data.py +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/py.typed +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/uts46data.py +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/INSTALLER +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/METADATA +79 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/RECORD +12 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/REQUESTED +0 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/WHEEL +5 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE +21 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/top_level.txt +1 -0
- hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig/__init__.py +249 -0
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/METADATA
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.3
|
| 2 |
+
Name: httpx
|
| 3 |
+
Version: 0.28.1
|
| 4 |
+
Summary: The next generation HTTP client.
|
| 5 |
+
Project-URL: Changelog, https://github.com/encode/httpx/blob/master/CHANGELOG.md
|
| 6 |
+
Project-URL: Documentation, https://www.python-httpx.org
|
| 7 |
+
Project-URL: Homepage, https://github.com/encode/httpx
|
| 8 |
+
Project-URL: Source, https://github.com/encode/httpx
|
| 9 |
+
Author-email: Tom Christie <tom@tomchristie.com>
|
| 10 |
+
License: BSD-3-Clause
|
| 11 |
+
Classifier: Development Status :: 4 - Beta
|
| 12 |
+
Classifier: Environment :: Web Environment
|
| 13 |
+
Classifier: Framework :: AsyncIO
|
| 14 |
+
Classifier: Framework :: Trio
|
| 15 |
+
Classifier: Intended Audience :: Developers
|
| 16 |
+
Classifier: License :: OSI Approved :: BSD License
|
| 17 |
+
Classifier: Operating System :: OS Independent
|
| 18 |
+
Classifier: Programming Language :: Python :: 3
|
| 19 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 24 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 25 |
+
Classifier: Topic :: Internet :: WWW/HTTP
|
| 26 |
+
Requires-Python: >=3.8
|
| 27 |
+
Requires-Dist: anyio
|
| 28 |
+
Requires-Dist: certifi
|
| 29 |
+
Requires-Dist: httpcore==1.*
|
| 30 |
+
Requires-Dist: idna
|
| 31 |
+
Provides-Extra: brotli
|
| 32 |
+
Requires-Dist: brotli; (platform_python_implementation == 'CPython') and extra == 'brotli'
|
| 33 |
+
Requires-Dist: brotlicffi; (platform_python_implementation != 'CPython') and extra == 'brotli'
|
| 34 |
+
Provides-Extra: cli
|
| 35 |
+
Requires-Dist: click==8.*; extra == 'cli'
|
| 36 |
+
Requires-Dist: pygments==2.*; extra == 'cli'
|
| 37 |
+
Requires-Dist: rich<14,>=10; extra == 'cli'
|
| 38 |
+
Provides-Extra: http2
|
| 39 |
+
Requires-Dist: h2<5,>=3; extra == 'http2'
|
| 40 |
+
Provides-Extra: socks
|
| 41 |
+
Requires-Dist: socksio==1.*; extra == 'socks'
|
| 42 |
+
Provides-Extra: zstd
|
| 43 |
+
Requires-Dist: zstandard>=0.18.0; extra == 'zstd'
|
| 44 |
+
Description-Content-Type: text/markdown
|
| 45 |
+
|
| 46 |
+
<p align="center">
|
| 47 |
+
<a href="https://www.python-httpx.org/"><img width="350" height="208" src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/butterfly.png" alt='HTTPX'></a>
|
| 48 |
+
</p>
|
| 49 |
+
|
| 50 |
+
<p align="center"><strong>HTTPX</strong> <em>- A next-generation HTTP client for Python.</em></p>
|
| 51 |
+
|
| 52 |
+
<p align="center">
|
| 53 |
+
<a href="https://github.com/encode/httpx/actions">
|
| 54 |
+
<img src="https://github.com/encode/httpx/workflows/Test%20Suite/badge.svg" alt="Test Suite">
|
| 55 |
+
</a>
|
| 56 |
+
<a href="https://pypi.org/project/httpx/">
|
| 57 |
+
<img src="https://badge.fury.io/py/httpx.svg" alt="Package version">
|
| 58 |
+
</a>
|
| 59 |
+
</p>
|
| 60 |
+
|
| 61 |
+
HTTPX is a fully featured HTTP client library for Python 3. It includes **an integrated command line client**, has support for both **HTTP/1.1 and HTTP/2**, and provides both **sync and async APIs**.
|
| 62 |
+
|
| 63 |
+
---
|
| 64 |
+
|
| 65 |
+
Install HTTPX using pip:
|
| 66 |
+
|
| 67 |
+
```shell
|
| 68 |
+
$ pip install httpx
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
Now, let's get started:
|
| 72 |
+
|
| 73 |
+
```pycon
|
| 74 |
+
>>> import httpx
|
| 75 |
+
>>> r = httpx.get('https://www.example.org/')
|
| 76 |
+
>>> r
|
| 77 |
+
<Response [200 OK]>
|
| 78 |
+
>>> r.status_code
|
| 79 |
+
200
|
| 80 |
+
>>> r.headers['content-type']
|
| 81 |
+
'text/html; charset=UTF-8'
|
| 82 |
+
>>> r.text
|
| 83 |
+
'<!doctype html>\n<html>\n<head>\n<title>Example Domain</title>...'
|
| 84 |
+
```
|
| 85 |
+
|
| 86 |
+
Or, using the command-line client.
|
| 87 |
+
|
| 88 |
+
```shell
|
| 89 |
+
$ pip install 'httpx[cli]' # The command line client is an optional dependency.
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
Which now allows us to use HTTPX directly from the command-line...
|
| 93 |
+
|
| 94 |
+
<p align="center">
|
| 95 |
+
<img width="700" src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/httpx-help.png" alt='httpx --help'>
|
| 96 |
+
</p>
|
| 97 |
+
|
| 98 |
+
Sending a request...
|
| 99 |
+
|
| 100 |
+
<p align="center">
|
| 101 |
+
<img width="700" src="https://raw.githubusercontent.com/encode/httpx/master/docs/img/httpx-request.png" alt='httpx http://httpbin.org/json'>
|
| 102 |
+
</p>
|
| 103 |
+
|
| 104 |
+
## Features
|
| 105 |
+
|
| 106 |
+
HTTPX builds on the well-established usability of `requests`, and gives you:
|
| 107 |
+
|
| 108 |
+
* A broadly [requests-compatible API](https://www.python-httpx.org/compatibility/).
|
| 109 |
+
* An integrated command-line client.
|
| 110 |
+
* HTTP/1.1 [and HTTP/2 support](https://www.python-httpx.org/http2/).
|
| 111 |
+
* Standard synchronous interface, but with [async support if you need it](https://www.python-httpx.org/async/).
|
| 112 |
+
* Ability to make requests directly to [WSGI applications](https://www.python-httpx.org/advanced/transports/#wsgi-transport) or [ASGI applications](https://www.python-httpx.org/advanced/transports/#asgi-transport).
|
| 113 |
+
* Strict timeouts everywhere.
|
| 114 |
+
* Fully type annotated.
|
| 115 |
+
* 100% test coverage.
|
| 116 |
+
|
| 117 |
+
Plus all the standard features of `requests`...
|
| 118 |
+
|
| 119 |
+
* International Domains and URLs
|
| 120 |
+
* Keep-Alive & Connection Pooling
|
| 121 |
+
* Sessions with Cookie Persistence
|
| 122 |
+
* Browser-style SSL Verification
|
| 123 |
+
* Basic/Digest Authentication
|
| 124 |
+
* Elegant Key/Value Cookies
|
| 125 |
+
* Automatic Decompression
|
| 126 |
+
* Automatic Content Decoding
|
| 127 |
+
* Unicode Response Bodies
|
| 128 |
+
* Multipart File Uploads
|
| 129 |
+
* HTTP(S) Proxy Support
|
| 130 |
+
* Connection Timeouts
|
| 131 |
+
* Streaming Downloads
|
| 132 |
+
* .netrc Support
|
| 133 |
+
* Chunked Requests
|
| 134 |
+
|
| 135 |
+
## Installation
|
| 136 |
+
|
| 137 |
+
Install with pip:
|
| 138 |
+
|
| 139 |
+
```shell
|
| 140 |
+
$ pip install httpx
|
| 141 |
+
```
|
| 142 |
+
|
| 143 |
+
Or, to include the optional HTTP/2 support, use:
|
| 144 |
+
|
| 145 |
+
```shell
|
| 146 |
+
$ pip install httpx[http2]
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
HTTPX requires Python 3.8+.
|
| 150 |
+
|
| 151 |
+
## Documentation
|
| 152 |
+
|
| 153 |
+
Project documentation is available at [https://www.python-httpx.org/](https://www.python-httpx.org/).
|
| 154 |
+
|
| 155 |
+
For a run-through of all the basics, head over to the [QuickStart](https://www.python-httpx.org/quickstart/).
|
| 156 |
+
|
| 157 |
+
For more advanced topics, see the [Advanced Usage](https://www.python-httpx.org/advanced/) section, the [async support](https://www.python-httpx.org/async/) section, or the [HTTP/2](https://www.python-httpx.org/http2/) section.
|
| 158 |
+
|
| 159 |
+
The [Developer Interface](https://www.python-httpx.org/api/) provides a comprehensive API reference.
|
| 160 |
+
|
| 161 |
+
To find out about tools that integrate with HTTPX, see [Third Party Packages](https://www.python-httpx.org/third_party_packages/).
|
| 162 |
+
|
| 163 |
+
## Contribute
|
| 164 |
+
|
| 165 |
+
If you want to contribute with HTTPX check out the [Contributing Guide](https://www.python-httpx.org/contributing/) to learn how to start.
|
| 166 |
+
|
| 167 |
+
## Dependencies
|
| 168 |
+
|
| 169 |
+
The HTTPX project relies on these excellent libraries:
|
| 170 |
+
|
| 171 |
+
* `httpcore` - The underlying transport implementation for `httpx`.
|
| 172 |
+
* `h11` - HTTP/1.1 support.
|
| 173 |
+
* `certifi` - SSL certificates.
|
| 174 |
+
* `idna` - Internationalized domain name support.
|
| 175 |
+
* `sniffio` - Async library autodetection.
|
| 176 |
+
|
| 177 |
+
As well as these optional installs:
|
| 178 |
+
|
| 179 |
+
* `h2` - HTTP/2 support. *(Optional, with `httpx[http2]`)*
|
| 180 |
+
* `socksio` - SOCKS proxy support. *(Optional, with `httpx[socks]`)*
|
| 181 |
+
* `rich` - Rich terminal support. *(Optional, with `httpx[cli]`)*
|
| 182 |
+
* `click` - Command line client support. *(Optional, with `httpx[cli]`)*
|
| 183 |
+
* `brotli` or `brotlicffi` - Decoding for "brotli" compressed responses. *(Optional, with `httpx[brotli]`)*
|
| 184 |
+
* `zstandard` - Decoding for "zstd" compressed responses. *(Optional, with `httpx[zstd]`)*
|
| 185 |
+
|
| 186 |
+
A huge amount of credit is due to `requests` for the API layout that
|
| 187 |
+
much of this work follows, as well as to `urllib3` for plenty of design
|
| 188 |
+
inspiration around the lower-level networking details.
|
| 189 |
+
|
| 190 |
+
---
|
| 191 |
+
|
| 192 |
+
<p align="center"><i>HTTPX is <a href="https://github.com/encode/httpx/blob/master/LICENSE.md">BSD licensed</a> code.<br/>Designed & crafted with care.</i><br/>— 🦋 —</p>
|
| 193 |
+
|
| 194 |
+
## Release Information
|
| 195 |
+
|
| 196 |
+
### Fixed
|
| 197 |
+
|
| 198 |
+
* Reintroduced supposedly-private `URLTypes` shortcut. (#2673)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
---
|
| 202 |
+
|
| 203 |
+
[Full changelog](https://github.com/encode/httpx/blob/master/CHANGELOG.md)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/RECORD
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/httpx,sha256=gRFPb2PaCCSO98wCExsi-S88-5nu5up_dYRMrTqXxpA,334
|
| 2 |
+
httpx-0.28.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 3 |
+
httpx-0.28.1.dist-info/METADATA,sha256=_rubD48-gNV8gZnDBPNcQzboWB0dGNeYPJJ2a4J5OyU,7052
|
| 4 |
+
httpx-0.28.1.dist-info/RECORD,,
|
| 5 |
+
httpx-0.28.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 6 |
+
httpx-0.28.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
| 7 |
+
httpx-0.28.1.dist-info/entry_points.txt,sha256=2lVkdQmxLA1pNMgSN2eV89o90HCZezhmNwsy6ryKDSA,37
|
| 8 |
+
httpx-0.28.1.dist-info/licenses/LICENSE.md,sha256=TsWdVE8StfU5o6cW_TIaxYzNgDC0ZSIfLIgCAM3yjY0,1508
|
| 9 |
+
httpx/__init__.py,sha256=CsaZe6yZj0rHg6322AWKWHGTMVr9txgEfD5P3_Rrz60,2171
|
| 10 |
+
httpx/__version__.py,sha256=LoUyYeOXTieGzuP_64UL0wxdtxjuu_QbOvE7NOg-IqU,108
|
| 11 |
+
httpx/_api.py,sha256=r_Zgs4jIpcPJLqK5dbbSayqo_iVMKFaxZCd-oOHxLEs,11743
|
| 12 |
+
httpx/_auth.py,sha256=Yr3QwaUSK17rGYx-7j-FdicFIzz4Y9FFV-1F4-7RXX4,11891
|
| 13 |
+
httpx/_client.py,sha256=xD-UG67-WMkeltAAOeGGj-cZ2RRTAm19sWRxlFY7_40,65714
|
| 14 |
+
httpx/_config.py,sha256=pPp2U-wicfcKsF-KYRE1LYdt3e6ERGeIoXZ8Gjo3LWc,8547
|
| 15 |
+
httpx/_content.py,sha256=LGGzrJTR3OvN4Mb1GVVNLXkXJH-6oKlwAttO9p5w_yg,8161
|
| 16 |
+
httpx/_decoders.py,sha256=p0dX8I0NEHexs3UGp4SsZutiMhsXrrWl6-GnqVb0iKM,12041
|
| 17 |
+
httpx/_exceptions.py,sha256=bxW7fxzgVMAdNTbwT0Vnq04gJDW1_gI_GFiQPuMyjL0,8527
|
| 18 |
+
httpx/_main.py,sha256=Cg9GMabiTT_swaDfUgIRitSwxLRMSwUDOm7LdSGqlA4,15626
|
| 19 |
+
httpx/_models.py,sha256=4__Guyv1gLxuZChwim8kfQNiIOcJ9acreFOSurvZfms,44700
|
| 20 |
+
httpx/_multipart.py,sha256=KOHEZZl6oohg9mPaKyyu345qq1rJLg35TUG3YAzXB3Y,9843
|
| 21 |
+
httpx/_status_codes.py,sha256=DYn-2ufBgMeXy5s8x3_TB7wjAuAAMewTakPrm5rXEsc,5639
|
| 22 |
+
httpx/_transports/__init__.py,sha256=GbUoBSAOp7z-l-9j5YhMhR3DMIcn6FVLhj072O3Nnno,275
|
| 23 |
+
httpx/_transports/asgi.py,sha256=HRfiDYMPt4wQH2gFgHZg4c-i3sblo6bL5GTqcET-xz8,5501
|
| 24 |
+
httpx/_transports/base.py,sha256=kZS_VMbViYfF570pogUCJ1bulz-ybfL51Pqs9yktebU,2523
|
| 25 |
+
httpx/_transports/default.py,sha256=AzeaRUyVwCccTyyNJexDf0n1dFfzzydpdIQgvw7PLnk,13983
|
| 26 |
+
httpx/_transports/mock.py,sha256=PTo0d567RITXxGrki6kN7_67wwAxfwiMDcuXJiZCjEo,1232
|
| 27 |
+
httpx/_transports/wsgi.py,sha256=NcPX3Xap_EwCFZWO_OaSyQNuInCYx1QMNbO8GAei6jY,4825
|
| 28 |
+
httpx/_types.py,sha256=Jyh41GQq7AOev8IOWKDAg7zCbvHAfufmW5g_PiTtErY,2965
|
| 29 |
+
httpx/_urlparse.py,sha256=ZAmH47ONfkxrrj-PPYhGeiHjb6AjKCS-ANWIN4OL_KY,18546
|
| 30 |
+
httpx/_urls.py,sha256=dX99VR1DSOHpgo9Aq7PzYO4FKdxqKjwyNp8grf8dHN0,21550
|
| 31 |
+
httpx/_utils.py,sha256=_TVeqAKvxJkKHdz7dFeb4s0LZqQXgeFkXSgfiHBK_1o,8285
|
| 32 |
+
httpx/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: hatchling 1.26.3
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
httpx = httpx:main
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright © 2019, [Encode OSS Ltd](https://www.encode.io/).
|
| 2 |
+
All rights reserved.
|
| 3 |
+
|
| 4 |
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
| 5 |
+
|
| 6 |
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
| 7 |
+
|
| 8 |
+
* 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.
|
| 9 |
+
|
| 10 |
+
* 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.
|
| 11 |
+
|
| 12 |
+
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.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_urlparse.py
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
An implementation of `urlparse` that provides URL validation and normalization
|
| 3 |
+
as described by RFC3986.
|
| 4 |
+
|
| 5 |
+
We rely on this implementation rather than the one in Python's stdlib, because:
|
| 6 |
+
|
| 7 |
+
* It provides more complete URL validation.
|
| 8 |
+
* It properly differentiates between an empty querystring and an absent querystring,
|
| 9 |
+
to distinguish URLs with a trailing '?'.
|
| 10 |
+
* It handles scheme, hostname, port, and path normalization.
|
| 11 |
+
* It supports IDNA hostnames, normalizing them to their encoded form.
|
| 12 |
+
* The API supports passing individual components, as well as the complete URL string.
|
| 13 |
+
|
| 14 |
+
Previously we relied on the excellent `rfc3986` package to handle URL parsing and
|
| 15 |
+
validation, but this module provides a simpler alternative, with less indirection
|
| 16 |
+
required.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
|
| 21 |
+
import ipaddress
|
| 22 |
+
import re
|
| 23 |
+
import typing
|
| 24 |
+
|
| 25 |
+
import idna
|
| 26 |
+
|
| 27 |
+
from ._exceptions import InvalidURL
|
| 28 |
+
|
| 29 |
+
MAX_URL_LENGTH = 65536
|
| 30 |
+
|
| 31 |
+
# https://datatracker.ietf.org/doc/html/rfc3986.html#section-2.3
|
| 32 |
+
UNRESERVED_CHARACTERS = (
|
| 33 |
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~"
|
| 34 |
+
)
|
| 35 |
+
SUB_DELIMS = "!$&'()*+,;="
|
| 36 |
+
|
| 37 |
+
PERCENT_ENCODED_REGEX = re.compile("%[A-Fa-f0-9]{2}")
|
| 38 |
+
|
| 39 |
+
# https://url.spec.whatwg.org/#percent-encoded-bytes
|
| 40 |
+
|
| 41 |
+
# The fragment percent-encode set is the C0 control percent-encode set
|
| 42 |
+
# and U+0020 SPACE, U+0022 ("), U+003C (<), U+003E (>), and U+0060 (`).
|
| 43 |
+
FRAG_SAFE = "".join(
|
| 44 |
+
[chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x3C, 0x3E, 0x60)]
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
# The query percent-encode set is the C0 control percent-encode set
|
| 48 |
+
# and U+0020 SPACE, U+0022 ("), U+0023 (#), U+003C (<), and U+003E (>).
|
| 49 |
+
QUERY_SAFE = "".join(
|
| 50 |
+
[chr(i) for i in range(0x20, 0x7F) if i not in (0x20, 0x22, 0x23, 0x3C, 0x3E)]
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
# The path percent-encode set is the query percent-encode set
|
| 54 |
+
# and U+003F (?), U+0060 (`), U+007B ({), and U+007D (}).
|
| 55 |
+
PATH_SAFE = "".join(
|
| 56 |
+
[
|
| 57 |
+
chr(i)
|
| 58 |
+
for i in range(0x20, 0x7F)
|
| 59 |
+
if i not in (0x20, 0x22, 0x23, 0x3C, 0x3E) + (0x3F, 0x60, 0x7B, 0x7D)
|
| 60 |
+
]
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
# The userinfo percent-encode set is the path percent-encode set
|
| 64 |
+
# and U+002F (/), U+003A (:), U+003B (;), U+003D (=), U+0040 (@),
|
| 65 |
+
# U+005B ([) to U+005E (^), inclusive, and U+007C (|).
|
| 66 |
+
USERNAME_SAFE = "".join(
|
| 67 |
+
[
|
| 68 |
+
chr(i)
|
| 69 |
+
for i in range(0x20, 0x7F)
|
| 70 |
+
if i
|
| 71 |
+
not in (0x20, 0x22, 0x23, 0x3C, 0x3E)
|
| 72 |
+
+ (0x3F, 0x60, 0x7B, 0x7D)
|
| 73 |
+
+ (0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C)
|
| 74 |
+
]
|
| 75 |
+
)
|
| 76 |
+
PASSWORD_SAFE = "".join(
|
| 77 |
+
[
|
| 78 |
+
chr(i)
|
| 79 |
+
for i in range(0x20, 0x7F)
|
| 80 |
+
if i
|
| 81 |
+
not in (0x20, 0x22, 0x23, 0x3C, 0x3E)
|
| 82 |
+
+ (0x3F, 0x60, 0x7B, 0x7D)
|
| 83 |
+
+ (0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C)
|
| 84 |
+
]
|
| 85 |
+
)
|
| 86 |
+
# Note... The terminology 'userinfo' percent-encode set in the WHATWG document
|
| 87 |
+
# is used for the username and password quoting. For the joint userinfo component
|
| 88 |
+
# we remove U+003A (:) from the safe set.
|
| 89 |
+
USERINFO_SAFE = "".join(
|
| 90 |
+
[
|
| 91 |
+
chr(i)
|
| 92 |
+
for i in range(0x20, 0x7F)
|
| 93 |
+
if i
|
| 94 |
+
not in (0x20, 0x22, 0x23, 0x3C, 0x3E)
|
| 95 |
+
+ (0x3F, 0x60, 0x7B, 0x7D)
|
| 96 |
+
+ (0x2F, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x5E, 0x7C)
|
| 97 |
+
]
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
# {scheme}: (optional)
|
| 102 |
+
# //{authority} (optional)
|
| 103 |
+
# {path}
|
| 104 |
+
# ?{query} (optional)
|
| 105 |
+
# #{fragment} (optional)
|
| 106 |
+
URL_REGEX = re.compile(
|
| 107 |
+
(
|
| 108 |
+
r"(?:(?P<scheme>{scheme}):)?"
|
| 109 |
+
r"(?://(?P<authority>{authority}))?"
|
| 110 |
+
r"(?P<path>{path})"
|
| 111 |
+
r"(?:\?(?P<query>{query}))?"
|
| 112 |
+
r"(?:#(?P<fragment>{fragment}))?"
|
| 113 |
+
).format(
|
| 114 |
+
scheme="([a-zA-Z][a-zA-Z0-9+.-]*)?",
|
| 115 |
+
authority="[^/?#]*",
|
| 116 |
+
path="[^?#]*",
|
| 117 |
+
query="[^#]*",
|
| 118 |
+
fragment=".*",
|
| 119 |
+
)
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
# {userinfo}@ (optional)
|
| 123 |
+
# {host}
|
| 124 |
+
# :{port} (optional)
|
| 125 |
+
AUTHORITY_REGEX = re.compile(
|
| 126 |
+
(
|
| 127 |
+
r"(?:(?P<userinfo>{userinfo})@)?" r"(?P<host>{host})" r":?(?P<port>{port})?"
|
| 128 |
+
).format(
|
| 129 |
+
userinfo=".*", # Any character sequence.
|
| 130 |
+
host="(\\[.*\\]|[^:@]*)", # Either any character sequence excluding ':' or '@',
|
| 131 |
+
# or an IPv6 address enclosed within square brackets.
|
| 132 |
+
port=".*", # Any character sequence.
|
| 133 |
+
)
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
# If we call urlparse with an individual component, then we need to regex
|
| 138 |
+
# validate that component individually.
|
| 139 |
+
# Note that we're duplicating the same strings as above. Shock! Horror!!
|
| 140 |
+
COMPONENT_REGEX = {
|
| 141 |
+
"scheme": re.compile("([a-zA-Z][a-zA-Z0-9+.-]*)?"),
|
| 142 |
+
"authority": re.compile("[^/?#]*"),
|
| 143 |
+
"path": re.compile("[^?#]*"),
|
| 144 |
+
"query": re.compile("[^#]*"),
|
| 145 |
+
"fragment": re.compile(".*"),
|
| 146 |
+
"userinfo": re.compile("[^@]*"),
|
| 147 |
+
"host": re.compile("(\\[.*\\]|[^:]*)"),
|
| 148 |
+
"port": re.compile(".*"),
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
# We use these simple regexs as a first pass before handing off to
|
| 153 |
+
# the stdlib 'ipaddress' module for IP address validation.
|
| 154 |
+
IPv4_STYLE_HOSTNAME = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$")
|
| 155 |
+
IPv6_STYLE_HOSTNAME = re.compile(r"^\[.*\]$")
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
class ParseResult(typing.NamedTuple):
|
| 159 |
+
scheme: str
|
| 160 |
+
userinfo: str
|
| 161 |
+
host: str
|
| 162 |
+
port: int | None
|
| 163 |
+
path: str
|
| 164 |
+
query: str | None
|
| 165 |
+
fragment: str | None
|
| 166 |
+
|
| 167 |
+
@property
|
| 168 |
+
def authority(self) -> str:
|
| 169 |
+
return "".join(
|
| 170 |
+
[
|
| 171 |
+
f"{self.userinfo}@" if self.userinfo else "",
|
| 172 |
+
f"[{self.host}]" if ":" in self.host else self.host,
|
| 173 |
+
f":{self.port}" if self.port is not None else "",
|
| 174 |
+
]
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
@property
|
| 178 |
+
def netloc(self) -> str:
|
| 179 |
+
return "".join(
|
| 180 |
+
[
|
| 181 |
+
f"[{self.host}]" if ":" in self.host else self.host,
|
| 182 |
+
f":{self.port}" if self.port is not None else "",
|
| 183 |
+
]
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
def copy_with(self, **kwargs: str | None) -> ParseResult:
|
| 187 |
+
if not kwargs:
|
| 188 |
+
return self
|
| 189 |
+
|
| 190 |
+
defaults = {
|
| 191 |
+
"scheme": self.scheme,
|
| 192 |
+
"authority": self.authority,
|
| 193 |
+
"path": self.path,
|
| 194 |
+
"query": self.query,
|
| 195 |
+
"fragment": self.fragment,
|
| 196 |
+
}
|
| 197 |
+
defaults.update(kwargs)
|
| 198 |
+
return urlparse("", **defaults)
|
| 199 |
+
|
| 200 |
+
def __str__(self) -> str:
|
| 201 |
+
authority = self.authority
|
| 202 |
+
return "".join(
|
| 203 |
+
[
|
| 204 |
+
f"{self.scheme}:" if self.scheme else "",
|
| 205 |
+
f"//{authority}" if authority else "",
|
| 206 |
+
self.path,
|
| 207 |
+
f"?{self.query}" if self.query is not None else "",
|
| 208 |
+
f"#{self.fragment}" if self.fragment is not None else "",
|
| 209 |
+
]
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def urlparse(url: str = "", **kwargs: str | None) -> ParseResult:
|
| 214 |
+
# Initial basic checks on allowable URLs.
|
| 215 |
+
# ---------------------------------------
|
| 216 |
+
|
| 217 |
+
# Hard limit the maximum allowable URL length.
|
| 218 |
+
if len(url) > MAX_URL_LENGTH:
|
| 219 |
+
raise InvalidURL("URL too long")
|
| 220 |
+
|
| 221 |
+
# If a URL includes any ASCII control characters including \t, \r, \n,
|
| 222 |
+
# then treat it as invalid.
|
| 223 |
+
if any(char.isascii() and not char.isprintable() for char in url):
|
| 224 |
+
char = next(char for char in url if char.isascii() and not char.isprintable())
|
| 225 |
+
idx = url.find(char)
|
| 226 |
+
error = (
|
| 227 |
+
f"Invalid non-printable ASCII character in URL, {char!r} at position {idx}."
|
| 228 |
+
)
|
| 229 |
+
raise InvalidURL(error)
|
| 230 |
+
|
| 231 |
+
# Some keyword arguments require special handling.
|
| 232 |
+
# ------------------------------------------------
|
| 233 |
+
|
| 234 |
+
# Coerce "port" to a string, if it is provided as an integer.
|
| 235 |
+
if "port" in kwargs:
|
| 236 |
+
port = kwargs["port"]
|
| 237 |
+
kwargs["port"] = str(port) if isinstance(port, int) else port
|
| 238 |
+
|
| 239 |
+
# Replace "netloc" with "host and "port".
|
| 240 |
+
if "netloc" in kwargs:
|
| 241 |
+
netloc = kwargs.pop("netloc") or ""
|
| 242 |
+
kwargs["host"], _, kwargs["port"] = netloc.partition(":")
|
| 243 |
+
|
| 244 |
+
# Replace "username" and/or "password" with "userinfo".
|
| 245 |
+
if "username" in kwargs or "password" in kwargs:
|
| 246 |
+
username = quote(kwargs.pop("username", "") or "", safe=USERNAME_SAFE)
|
| 247 |
+
password = quote(kwargs.pop("password", "") or "", safe=PASSWORD_SAFE)
|
| 248 |
+
kwargs["userinfo"] = f"{username}:{password}" if password else username
|
| 249 |
+
|
| 250 |
+
# Replace "raw_path" with "path" and "query".
|
| 251 |
+
if "raw_path" in kwargs:
|
| 252 |
+
raw_path = kwargs.pop("raw_path") or ""
|
| 253 |
+
kwargs["path"], seperator, kwargs["query"] = raw_path.partition("?")
|
| 254 |
+
if not seperator:
|
| 255 |
+
kwargs["query"] = None
|
| 256 |
+
|
| 257 |
+
# Ensure that IPv6 "host" addresses are always escaped with "[...]".
|
| 258 |
+
if "host" in kwargs:
|
| 259 |
+
host = kwargs.get("host") or ""
|
| 260 |
+
if ":" in host and not (host.startswith("[") and host.endswith("]")):
|
| 261 |
+
kwargs["host"] = f"[{host}]"
|
| 262 |
+
|
| 263 |
+
# If any keyword arguments are provided, ensure they are valid.
|
| 264 |
+
# -------------------------------------------------------------
|
| 265 |
+
|
| 266 |
+
for key, value in kwargs.items():
|
| 267 |
+
if value is not None:
|
| 268 |
+
if len(value) > MAX_URL_LENGTH:
|
| 269 |
+
raise InvalidURL(f"URL component '{key}' too long")
|
| 270 |
+
|
| 271 |
+
# If a component includes any ASCII control characters including \t, \r, \n,
|
| 272 |
+
# then treat it as invalid.
|
| 273 |
+
if any(char.isascii() and not char.isprintable() for char in value):
|
| 274 |
+
char = next(
|
| 275 |
+
char for char in value if char.isascii() and not char.isprintable()
|
| 276 |
+
)
|
| 277 |
+
idx = value.find(char)
|
| 278 |
+
error = (
|
| 279 |
+
f"Invalid non-printable ASCII character in URL {key} component, "
|
| 280 |
+
f"{char!r} at position {idx}."
|
| 281 |
+
)
|
| 282 |
+
raise InvalidURL(error)
|
| 283 |
+
|
| 284 |
+
# Ensure that keyword arguments match as a valid regex.
|
| 285 |
+
if not COMPONENT_REGEX[key].fullmatch(value):
|
| 286 |
+
raise InvalidURL(f"Invalid URL component '{key}'")
|
| 287 |
+
|
| 288 |
+
# The URL_REGEX will always match, but may have empty components.
|
| 289 |
+
url_match = URL_REGEX.match(url)
|
| 290 |
+
assert url_match is not None
|
| 291 |
+
url_dict = url_match.groupdict()
|
| 292 |
+
|
| 293 |
+
# * 'scheme', 'authority', and 'path' may be empty strings.
|
| 294 |
+
# * 'query' may be 'None', indicating no trailing "?" portion.
|
| 295 |
+
# Any string including the empty string, indicates a trailing "?".
|
| 296 |
+
# * 'fragment' may be 'None', indicating no trailing "#" portion.
|
| 297 |
+
# Any string including the empty string, indicates a trailing "#".
|
| 298 |
+
scheme = kwargs.get("scheme", url_dict["scheme"]) or ""
|
| 299 |
+
authority = kwargs.get("authority", url_dict["authority"]) or ""
|
| 300 |
+
path = kwargs.get("path", url_dict["path"]) or ""
|
| 301 |
+
query = kwargs.get("query", url_dict["query"])
|
| 302 |
+
frag = kwargs.get("fragment", url_dict["fragment"])
|
| 303 |
+
|
| 304 |
+
# The AUTHORITY_REGEX will always match, but may have empty components.
|
| 305 |
+
authority_match = AUTHORITY_REGEX.match(authority)
|
| 306 |
+
assert authority_match is not None
|
| 307 |
+
authority_dict = authority_match.groupdict()
|
| 308 |
+
|
| 309 |
+
# * 'userinfo' and 'host' may be empty strings.
|
| 310 |
+
# * 'port' may be 'None'.
|
| 311 |
+
userinfo = kwargs.get("userinfo", authority_dict["userinfo"]) or ""
|
| 312 |
+
host = kwargs.get("host", authority_dict["host"]) or ""
|
| 313 |
+
port = kwargs.get("port", authority_dict["port"])
|
| 314 |
+
|
| 315 |
+
# Normalize and validate each component.
|
| 316 |
+
# We end up with a parsed representation of the URL,
|
| 317 |
+
# with components that are plain ASCII bytestrings.
|
| 318 |
+
parsed_scheme: str = scheme.lower()
|
| 319 |
+
parsed_userinfo: str = quote(userinfo, safe=USERINFO_SAFE)
|
| 320 |
+
parsed_host: str = encode_host(host)
|
| 321 |
+
parsed_port: int | None = normalize_port(port, scheme)
|
| 322 |
+
|
| 323 |
+
has_scheme = parsed_scheme != ""
|
| 324 |
+
has_authority = (
|
| 325 |
+
parsed_userinfo != "" or parsed_host != "" or parsed_port is not None
|
| 326 |
+
)
|
| 327 |
+
validate_path(path, has_scheme=has_scheme, has_authority=has_authority)
|
| 328 |
+
if has_scheme or has_authority:
|
| 329 |
+
path = normalize_path(path)
|
| 330 |
+
|
| 331 |
+
parsed_path: str = quote(path, safe=PATH_SAFE)
|
| 332 |
+
parsed_query: str | None = None if query is None else quote(query, safe=QUERY_SAFE)
|
| 333 |
+
parsed_frag: str | None = None if frag is None else quote(frag, safe=FRAG_SAFE)
|
| 334 |
+
|
| 335 |
+
# The parsed ASCII bytestrings are our canonical form.
|
| 336 |
+
# All properties of the URL are derived from these.
|
| 337 |
+
return ParseResult(
|
| 338 |
+
parsed_scheme,
|
| 339 |
+
parsed_userinfo,
|
| 340 |
+
parsed_host,
|
| 341 |
+
parsed_port,
|
| 342 |
+
parsed_path,
|
| 343 |
+
parsed_query,
|
| 344 |
+
parsed_frag,
|
| 345 |
+
)
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
def encode_host(host: str) -> str:
|
| 349 |
+
if not host:
|
| 350 |
+
return ""
|
| 351 |
+
|
| 352 |
+
elif IPv4_STYLE_HOSTNAME.match(host):
|
| 353 |
+
# Validate IPv4 hostnames like #.#.#.#
|
| 354 |
+
#
|
| 355 |
+
# From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2
|
| 356 |
+
#
|
| 357 |
+
# IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
|
| 358 |
+
try:
|
| 359 |
+
ipaddress.IPv4Address(host)
|
| 360 |
+
except ipaddress.AddressValueError:
|
| 361 |
+
raise InvalidURL(f"Invalid IPv4 address: {host!r}")
|
| 362 |
+
return host
|
| 363 |
+
|
| 364 |
+
elif IPv6_STYLE_HOSTNAME.match(host):
|
| 365 |
+
# Validate IPv6 hostnames like [...]
|
| 366 |
+
#
|
| 367 |
+
# From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2
|
| 368 |
+
#
|
| 369 |
+
# "A host identified by an Internet Protocol literal address, version 6
|
| 370 |
+
# [RFC3513] or later, is distinguished by enclosing the IP literal
|
| 371 |
+
# within square brackets ("[" and "]"). This is the only place where
|
| 372 |
+
# square bracket characters are allowed in the URI syntax."
|
| 373 |
+
try:
|
| 374 |
+
ipaddress.IPv6Address(host[1:-1])
|
| 375 |
+
except ipaddress.AddressValueError:
|
| 376 |
+
raise InvalidURL(f"Invalid IPv6 address: {host!r}")
|
| 377 |
+
return host[1:-1]
|
| 378 |
+
|
| 379 |
+
elif host.isascii():
|
| 380 |
+
# Regular ASCII hostnames
|
| 381 |
+
#
|
| 382 |
+
# From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2
|
| 383 |
+
#
|
| 384 |
+
# reg-name = *( unreserved / pct-encoded / sub-delims )
|
| 385 |
+
WHATWG_SAFE = '"`{}%|\\'
|
| 386 |
+
return quote(host.lower(), safe=SUB_DELIMS + WHATWG_SAFE)
|
| 387 |
+
|
| 388 |
+
# IDNA hostnames
|
| 389 |
+
try:
|
| 390 |
+
return idna.encode(host.lower()).decode("ascii")
|
| 391 |
+
except idna.IDNAError:
|
| 392 |
+
raise InvalidURL(f"Invalid IDNA hostname: {host!r}")
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
def normalize_port(port: str | int | None, scheme: str) -> int | None:
|
| 396 |
+
# From https://tools.ietf.org/html/rfc3986#section-3.2.3
|
| 397 |
+
#
|
| 398 |
+
# "A scheme may define a default port. For example, the "http" scheme
|
| 399 |
+
# defines a default port of "80", corresponding to its reserved TCP
|
| 400 |
+
# port number. The type of port designated by the port number (e.g.,
|
| 401 |
+
# TCP, UDP, SCTP) is defined by the URI scheme. URI producers and
|
| 402 |
+
# normalizers should omit the port component and its ":" delimiter if
|
| 403 |
+
# port is empty or if its value would be the same as that of the
|
| 404 |
+
# scheme's default."
|
| 405 |
+
if port is None or port == "":
|
| 406 |
+
return None
|
| 407 |
+
|
| 408 |
+
try:
|
| 409 |
+
port_as_int = int(port)
|
| 410 |
+
except ValueError:
|
| 411 |
+
raise InvalidURL(f"Invalid port: {port!r}")
|
| 412 |
+
|
| 413 |
+
# See https://url.spec.whatwg.org/#url-miscellaneous
|
| 414 |
+
default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get(
|
| 415 |
+
scheme
|
| 416 |
+
)
|
| 417 |
+
if port_as_int == default_port:
|
| 418 |
+
return None
|
| 419 |
+
return port_as_int
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None:
|
| 423 |
+
"""
|
| 424 |
+
Path validation rules that depend on if the URL contains
|
| 425 |
+
a scheme or authority component.
|
| 426 |
+
|
| 427 |
+
See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3
|
| 428 |
+
"""
|
| 429 |
+
if has_authority:
|
| 430 |
+
# If a URI contains an authority component, then the path component
|
| 431 |
+
# must either be empty or begin with a slash ("/") character."
|
| 432 |
+
if path and not path.startswith("/"):
|
| 433 |
+
raise InvalidURL("For absolute URLs, path must be empty or begin with '/'")
|
| 434 |
+
|
| 435 |
+
if not has_scheme and not has_authority:
|
| 436 |
+
# If a URI does not contain an authority component, then the path cannot begin
|
| 437 |
+
# with two slash characters ("//").
|
| 438 |
+
if path.startswith("//"):
|
| 439 |
+
raise InvalidURL("Relative URLs cannot have a path starting with '//'")
|
| 440 |
+
|
| 441 |
+
# In addition, a URI reference (Section 4.1) may be a relative-path reference,
|
| 442 |
+
# in which case the first path segment cannot contain a colon (":") character.
|
| 443 |
+
if path.startswith(":"):
|
| 444 |
+
raise InvalidURL("Relative URLs cannot have a path starting with ':'")
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
def normalize_path(path: str) -> str:
|
| 448 |
+
"""
|
| 449 |
+
Drop "." and ".." segments from a URL path.
|
| 450 |
+
|
| 451 |
+
For example:
|
| 452 |
+
|
| 453 |
+
normalize_path("/path/./to/somewhere/..") == "/path/to"
|
| 454 |
+
"""
|
| 455 |
+
# Fast return when no '.' characters in the path.
|
| 456 |
+
if "." not in path:
|
| 457 |
+
return path
|
| 458 |
+
|
| 459 |
+
components = path.split("/")
|
| 460 |
+
|
| 461 |
+
# Fast return when no '.' or '..' components in the path.
|
| 462 |
+
if "." not in components and ".." not in components:
|
| 463 |
+
return path
|
| 464 |
+
|
| 465 |
+
# https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
|
| 466 |
+
output: list[str] = []
|
| 467 |
+
for component in components:
|
| 468 |
+
if component == ".":
|
| 469 |
+
pass
|
| 470 |
+
elif component == "..":
|
| 471 |
+
if output and output != [""]:
|
| 472 |
+
output.pop()
|
| 473 |
+
else:
|
| 474 |
+
output.append(component)
|
| 475 |
+
return "/".join(output)
|
| 476 |
+
|
| 477 |
+
|
| 478 |
+
def PERCENT(string: str) -> str:
|
| 479 |
+
return "".join([f"%{byte:02X}" for byte in string.encode("utf-8")])
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
def percent_encoded(string: str, safe: str) -> str:
|
| 483 |
+
"""
|
| 484 |
+
Use percent-encoding to quote a string.
|
| 485 |
+
"""
|
| 486 |
+
NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe
|
| 487 |
+
|
| 488 |
+
# Fast path for strings that don't need escaping.
|
| 489 |
+
if not string.rstrip(NON_ESCAPED_CHARS):
|
| 490 |
+
return string
|
| 491 |
+
|
| 492 |
+
return "".join(
|
| 493 |
+
[char if char in NON_ESCAPED_CHARS else PERCENT(char) for char in string]
|
| 494 |
+
)
|
| 495 |
+
|
| 496 |
+
|
| 497 |
+
def quote(string: str, safe: str) -> str:
|
| 498 |
+
"""
|
| 499 |
+
Use percent-encoding to quote a string, omitting existing '%xx' escape sequences.
|
| 500 |
+
|
| 501 |
+
See: https://www.rfc-editor.org/rfc/rfc3986#section-2.1
|
| 502 |
+
|
| 503 |
+
* `string`: The string to be percent-escaped.
|
| 504 |
+
* `safe`: A string containing characters that may be treated as safe, and do not
|
| 505 |
+
need to be escaped. Unreserved characters are always treated as safe.
|
| 506 |
+
See: https://www.rfc-editor.org/rfc/rfc3986#section-2.3
|
| 507 |
+
"""
|
| 508 |
+
parts = []
|
| 509 |
+
current_position = 0
|
| 510 |
+
for match in re.finditer(PERCENT_ENCODED_REGEX, string):
|
| 511 |
+
start_position, end_position = match.start(), match.end()
|
| 512 |
+
matched_text = match.group(0)
|
| 513 |
+
# Add any text up to the '%xx' escape sequence.
|
| 514 |
+
if start_position != current_position:
|
| 515 |
+
leading_text = string[current_position:start_position]
|
| 516 |
+
parts.append(percent_encoded(leading_text, safe=safe))
|
| 517 |
+
|
| 518 |
+
# Add the '%xx' escape sequence.
|
| 519 |
+
parts.append(matched_text)
|
| 520 |
+
current_position = end_position
|
| 521 |
+
|
| 522 |
+
# Add any text after the final '%xx' escape sequence.
|
| 523 |
+
if current_position != len(string):
|
| 524 |
+
trailing_text = string[current_position:]
|
| 525 |
+
parts.append(percent_encoded(trailing_text, safe=safe))
|
| 526 |
+
|
| 527 |
+
return "".join(parts)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_urls.py
ADDED
|
@@ -0,0 +1,641 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import typing
|
| 4 |
+
from urllib.parse import parse_qs, unquote, urlencode
|
| 5 |
+
|
| 6 |
+
import idna
|
| 7 |
+
|
| 8 |
+
from ._types import QueryParamTypes
|
| 9 |
+
from ._urlparse import urlparse
|
| 10 |
+
from ._utils import primitive_value_to_str
|
| 11 |
+
|
| 12 |
+
__all__ = ["URL", "QueryParams"]
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class URL:
|
| 16 |
+
"""
|
| 17 |
+
url = httpx.URL("HTTPS://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink")
|
| 18 |
+
|
| 19 |
+
assert url.scheme == "https"
|
| 20 |
+
assert url.username == "jo@email.com"
|
| 21 |
+
assert url.password == "a secret"
|
| 22 |
+
assert url.userinfo == b"jo%40email.com:a%20secret"
|
| 23 |
+
assert url.host == "müller.de"
|
| 24 |
+
assert url.raw_host == b"xn--mller-kva.de"
|
| 25 |
+
assert url.port == 1234
|
| 26 |
+
assert url.netloc == b"xn--mller-kva.de:1234"
|
| 27 |
+
assert url.path == "/pa th"
|
| 28 |
+
assert url.query == b"?search=ab"
|
| 29 |
+
assert url.raw_path == b"/pa%20th?search=ab"
|
| 30 |
+
assert url.fragment == "anchorlink"
|
| 31 |
+
|
| 32 |
+
The components of a URL are broken down like this:
|
| 33 |
+
|
| 34 |
+
https://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink
|
| 35 |
+
[scheme] [ username ] [password] [ host ][port][ path ] [ query ] [fragment]
|
| 36 |
+
[ userinfo ] [ netloc ][ raw_path ]
|
| 37 |
+
|
| 38 |
+
Note that:
|
| 39 |
+
|
| 40 |
+
* `url.scheme` is normalized to always be lowercased.
|
| 41 |
+
|
| 42 |
+
* `url.host` is normalized to always be lowercased. Internationalized domain
|
| 43 |
+
names are represented in unicode, without IDNA encoding applied. For instance:
|
| 44 |
+
|
| 45 |
+
url = httpx.URL("http://中国.icom.museum")
|
| 46 |
+
assert url.host == "中国.icom.museum"
|
| 47 |
+
url = httpx.URL("http://xn--fiqs8s.icom.museum")
|
| 48 |
+
assert url.host == "中国.icom.museum"
|
| 49 |
+
|
| 50 |
+
* `url.raw_host` is normalized to always be lowercased, and is IDNA encoded.
|
| 51 |
+
|
| 52 |
+
url = httpx.URL("http://中国.icom.museum")
|
| 53 |
+
assert url.raw_host == b"xn--fiqs8s.icom.museum"
|
| 54 |
+
url = httpx.URL("http://xn--fiqs8s.icom.museum")
|
| 55 |
+
assert url.raw_host == b"xn--fiqs8s.icom.museum"
|
| 56 |
+
|
| 57 |
+
* `url.port` is either None or an integer. URLs that include the default port for
|
| 58 |
+
"http", "https", "ws", "wss", and "ftp" schemes have their port
|
| 59 |
+
normalized to `None`.
|
| 60 |
+
|
| 61 |
+
assert httpx.URL("http://example.com") == httpx.URL("http://example.com:80")
|
| 62 |
+
assert httpx.URL("http://example.com").port is None
|
| 63 |
+
assert httpx.URL("http://example.com:80").port is None
|
| 64 |
+
|
| 65 |
+
* `url.userinfo` is raw bytes, without URL escaping. Usually you'll want to work
|
| 66 |
+
with `url.username` and `url.password` instead, which handle the URL escaping.
|
| 67 |
+
|
| 68 |
+
* `url.raw_path` is raw bytes of both the path and query, without URL escaping.
|
| 69 |
+
This portion is used as the target when constructing HTTP requests. Usually you'll
|
| 70 |
+
want to work with `url.path` instead.
|
| 71 |
+
|
| 72 |
+
* `url.query` is raw bytes, without URL escaping. A URL query string portion can
|
| 73 |
+
only be properly URL escaped when decoding the parameter names and values
|
| 74 |
+
themselves.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None:
|
| 78 |
+
if kwargs:
|
| 79 |
+
allowed = {
|
| 80 |
+
"scheme": str,
|
| 81 |
+
"username": str,
|
| 82 |
+
"password": str,
|
| 83 |
+
"userinfo": bytes,
|
| 84 |
+
"host": str,
|
| 85 |
+
"port": int,
|
| 86 |
+
"netloc": bytes,
|
| 87 |
+
"path": str,
|
| 88 |
+
"query": bytes,
|
| 89 |
+
"raw_path": bytes,
|
| 90 |
+
"fragment": str,
|
| 91 |
+
"params": object,
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
# Perform type checking for all supported keyword arguments.
|
| 95 |
+
for key, value in kwargs.items():
|
| 96 |
+
if key not in allowed:
|
| 97 |
+
message = f"{key!r} is an invalid keyword argument for URL()"
|
| 98 |
+
raise TypeError(message)
|
| 99 |
+
if value is not None and not isinstance(value, allowed[key]):
|
| 100 |
+
expected = allowed[key].__name__
|
| 101 |
+
seen = type(value).__name__
|
| 102 |
+
message = f"Argument {key!r} must be {expected} but got {seen}"
|
| 103 |
+
raise TypeError(message)
|
| 104 |
+
if isinstance(value, bytes):
|
| 105 |
+
kwargs[key] = value.decode("ascii")
|
| 106 |
+
|
| 107 |
+
if "params" in kwargs:
|
| 108 |
+
# Replace any "params" keyword with the raw "query" instead.
|
| 109 |
+
#
|
| 110 |
+
# Ensure that empty params use `kwargs["query"] = None` rather
|
| 111 |
+
# than `kwargs["query"] = ""`, so that generated URLs do not
|
| 112 |
+
# include an empty trailing "?".
|
| 113 |
+
params = kwargs.pop("params")
|
| 114 |
+
kwargs["query"] = None if not params else str(QueryParams(params))
|
| 115 |
+
|
| 116 |
+
if isinstance(url, str):
|
| 117 |
+
self._uri_reference = urlparse(url, **kwargs)
|
| 118 |
+
elif isinstance(url, URL):
|
| 119 |
+
self._uri_reference = url._uri_reference.copy_with(**kwargs)
|
| 120 |
+
else:
|
| 121 |
+
raise TypeError(
|
| 122 |
+
"Invalid type for url. Expected str or httpx.URL,"
|
| 123 |
+
f" got {type(url)}: {url!r}"
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
@property
|
| 127 |
+
def scheme(self) -> str:
|
| 128 |
+
"""
|
| 129 |
+
The URL scheme, such as "http", "https".
|
| 130 |
+
Always normalised to lowercase.
|
| 131 |
+
"""
|
| 132 |
+
return self._uri_reference.scheme
|
| 133 |
+
|
| 134 |
+
@property
|
| 135 |
+
def raw_scheme(self) -> bytes:
|
| 136 |
+
"""
|
| 137 |
+
The raw bytes representation of the URL scheme, such as b"http", b"https".
|
| 138 |
+
Always normalised to lowercase.
|
| 139 |
+
"""
|
| 140 |
+
return self._uri_reference.scheme.encode("ascii")
|
| 141 |
+
|
| 142 |
+
@property
|
| 143 |
+
def userinfo(self) -> bytes:
|
| 144 |
+
"""
|
| 145 |
+
The URL userinfo as a raw bytestring.
|
| 146 |
+
For example: b"jo%40email.com:a%20secret".
|
| 147 |
+
"""
|
| 148 |
+
return self._uri_reference.userinfo.encode("ascii")
|
| 149 |
+
|
| 150 |
+
@property
|
| 151 |
+
def username(self) -> str:
|
| 152 |
+
"""
|
| 153 |
+
The URL username as a string, with URL decoding applied.
|
| 154 |
+
For example: "jo@email.com"
|
| 155 |
+
"""
|
| 156 |
+
userinfo = self._uri_reference.userinfo
|
| 157 |
+
return unquote(userinfo.partition(":")[0])
|
| 158 |
+
|
| 159 |
+
@property
|
| 160 |
+
def password(self) -> str:
|
| 161 |
+
"""
|
| 162 |
+
The URL password as a string, with URL decoding applied.
|
| 163 |
+
For example: "a secret"
|
| 164 |
+
"""
|
| 165 |
+
userinfo = self._uri_reference.userinfo
|
| 166 |
+
return unquote(userinfo.partition(":")[2])
|
| 167 |
+
|
| 168 |
+
@property
|
| 169 |
+
def host(self) -> str:
|
| 170 |
+
"""
|
| 171 |
+
The URL host as a string.
|
| 172 |
+
Always normalized to lowercase, with IDNA hosts decoded into unicode.
|
| 173 |
+
|
| 174 |
+
Examples:
|
| 175 |
+
|
| 176 |
+
url = httpx.URL("http://www.EXAMPLE.org")
|
| 177 |
+
assert url.host == "www.example.org"
|
| 178 |
+
|
| 179 |
+
url = httpx.URL("http://中国.icom.museum")
|
| 180 |
+
assert url.host == "中国.icom.museum"
|
| 181 |
+
|
| 182 |
+
url = httpx.URL("http://xn--fiqs8s.icom.museum")
|
| 183 |
+
assert url.host == "中国.icom.museum"
|
| 184 |
+
|
| 185 |
+
url = httpx.URL("https://[::ffff:192.168.0.1]")
|
| 186 |
+
assert url.host == "::ffff:192.168.0.1"
|
| 187 |
+
"""
|
| 188 |
+
host: str = self._uri_reference.host
|
| 189 |
+
|
| 190 |
+
if host.startswith("xn--"):
|
| 191 |
+
host = idna.decode(host)
|
| 192 |
+
|
| 193 |
+
return host
|
| 194 |
+
|
| 195 |
+
@property
|
| 196 |
+
def raw_host(self) -> bytes:
|
| 197 |
+
"""
|
| 198 |
+
The raw bytes representation of the URL host.
|
| 199 |
+
Always normalized to lowercase, and IDNA encoded.
|
| 200 |
+
|
| 201 |
+
Examples:
|
| 202 |
+
|
| 203 |
+
url = httpx.URL("http://www.EXAMPLE.org")
|
| 204 |
+
assert url.raw_host == b"www.example.org"
|
| 205 |
+
|
| 206 |
+
url = httpx.URL("http://中国.icom.museum")
|
| 207 |
+
assert url.raw_host == b"xn--fiqs8s.icom.museum"
|
| 208 |
+
|
| 209 |
+
url = httpx.URL("http://xn--fiqs8s.icom.museum")
|
| 210 |
+
assert url.raw_host == b"xn--fiqs8s.icom.museum"
|
| 211 |
+
|
| 212 |
+
url = httpx.URL("https://[::ffff:192.168.0.1]")
|
| 213 |
+
assert url.raw_host == b"::ffff:192.168.0.1"
|
| 214 |
+
"""
|
| 215 |
+
return self._uri_reference.host.encode("ascii")
|
| 216 |
+
|
| 217 |
+
@property
|
| 218 |
+
def port(self) -> int | None:
|
| 219 |
+
"""
|
| 220 |
+
The URL port as an integer.
|
| 221 |
+
|
| 222 |
+
Note that the URL class performs port normalization as per the WHATWG spec.
|
| 223 |
+
Default ports for "http", "https", "ws", "wss", and "ftp" schemes are always
|
| 224 |
+
treated as `None`.
|
| 225 |
+
|
| 226 |
+
For example:
|
| 227 |
+
|
| 228 |
+
assert httpx.URL("http://www.example.com") == httpx.URL("http://www.example.com:80")
|
| 229 |
+
assert httpx.URL("http://www.example.com:80").port is None
|
| 230 |
+
"""
|
| 231 |
+
return self._uri_reference.port
|
| 232 |
+
|
| 233 |
+
@property
|
| 234 |
+
def netloc(self) -> bytes:
|
| 235 |
+
"""
|
| 236 |
+
Either `<host>` or `<host>:<port>` as bytes.
|
| 237 |
+
Always normalized to lowercase, and IDNA encoded.
|
| 238 |
+
|
| 239 |
+
This property may be used for generating the value of a request
|
| 240 |
+
"Host" header.
|
| 241 |
+
"""
|
| 242 |
+
return self._uri_reference.netloc.encode("ascii")
|
| 243 |
+
|
| 244 |
+
@property
|
| 245 |
+
def path(self) -> str:
|
| 246 |
+
"""
|
| 247 |
+
The URL path as a string. Excluding the query string, and URL decoded.
|
| 248 |
+
|
| 249 |
+
For example:
|
| 250 |
+
|
| 251 |
+
url = httpx.URL("https://example.com/pa%20th")
|
| 252 |
+
assert url.path == "/pa th"
|
| 253 |
+
"""
|
| 254 |
+
path = self._uri_reference.path or "/"
|
| 255 |
+
return unquote(path)
|
| 256 |
+
|
| 257 |
+
@property
|
| 258 |
+
def query(self) -> bytes:
|
| 259 |
+
"""
|
| 260 |
+
The URL query string, as raw bytes, excluding the leading b"?".
|
| 261 |
+
|
| 262 |
+
This is necessarily a bytewise interface, because we cannot
|
| 263 |
+
perform URL decoding of this representation until we've parsed
|
| 264 |
+
the keys and values into a QueryParams instance.
|
| 265 |
+
|
| 266 |
+
For example:
|
| 267 |
+
|
| 268 |
+
url = httpx.URL("https://example.com/?filter=some%20search%20terms")
|
| 269 |
+
assert url.query == b"filter=some%20search%20terms"
|
| 270 |
+
"""
|
| 271 |
+
query = self._uri_reference.query or ""
|
| 272 |
+
return query.encode("ascii")
|
| 273 |
+
|
| 274 |
+
@property
|
| 275 |
+
def params(self) -> QueryParams:
|
| 276 |
+
"""
|
| 277 |
+
The URL query parameters, neatly parsed and packaged into an immutable
|
| 278 |
+
multidict representation.
|
| 279 |
+
"""
|
| 280 |
+
return QueryParams(self._uri_reference.query)
|
| 281 |
+
|
| 282 |
+
@property
|
| 283 |
+
def raw_path(self) -> bytes:
|
| 284 |
+
"""
|
| 285 |
+
The complete URL path and query string as raw bytes.
|
| 286 |
+
Used as the target when constructing HTTP requests.
|
| 287 |
+
|
| 288 |
+
For example:
|
| 289 |
+
|
| 290 |
+
GET /users?search=some%20text HTTP/1.1
|
| 291 |
+
Host: www.example.org
|
| 292 |
+
Connection: close
|
| 293 |
+
"""
|
| 294 |
+
path = self._uri_reference.path or "/"
|
| 295 |
+
if self._uri_reference.query is not None:
|
| 296 |
+
path += "?" + self._uri_reference.query
|
| 297 |
+
return path.encode("ascii")
|
| 298 |
+
|
| 299 |
+
@property
|
| 300 |
+
def fragment(self) -> str:
|
| 301 |
+
"""
|
| 302 |
+
The URL fragments, as used in HTML anchors.
|
| 303 |
+
As a string, without the leading '#'.
|
| 304 |
+
"""
|
| 305 |
+
return unquote(self._uri_reference.fragment or "")
|
| 306 |
+
|
| 307 |
+
@property
|
| 308 |
+
def is_absolute_url(self) -> bool:
|
| 309 |
+
"""
|
| 310 |
+
Return `True` for absolute URLs such as 'http://example.com/path',
|
| 311 |
+
and `False` for relative URLs such as '/path'.
|
| 312 |
+
"""
|
| 313 |
+
# We don't use `.is_absolute` from `rfc3986` because it treats
|
| 314 |
+
# URLs with a fragment portion as not absolute.
|
| 315 |
+
# What we actually care about is if the URL provides
|
| 316 |
+
# a scheme and hostname to which connections should be made.
|
| 317 |
+
return bool(self._uri_reference.scheme and self._uri_reference.host)
|
| 318 |
+
|
| 319 |
+
@property
|
| 320 |
+
def is_relative_url(self) -> bool:
|
| 321 |
+
"""
|
| 322 |
+
Return `False` for absolute URLs such as 'http://example.com/path',
|
| 323 |
+
and `True` for relative URLs such as '/path'.
|
| 324 |
+
"""
|
| 325 |
+
return not self.is_absolute_url
|
| 326 |
+
|
| 327 |
+
def copy_with(self, **kwargs: typing.Any) -> URL:
|
| 328 |
+
"""
|
| 329 |
+
Copy this URL, returning a new URL with some components altered.
|
| 330 |
+
Accepts the same set of parameters as the components that are made
|
| 331 |
+
available via properties on the `URL` class.
|
| 332 |
+
|
| 333 |
+
For example:
|
| 334 |
+
|
| 335 |
+
url = httpx.URL("https://www.example.com").copy_with(
|
| 336 |
+
username="jo@gmail.com", password="a secret"
|
| 337 |
+
)
|
| 338 |
+
assert url == "https://jo%40email.com:a%20secret@www.example.com"
|
| 339 |
+
"""
|
| 340 |
+
return URL(self, **kwargs)
|
| 341 |
+
|
| 342 |
+
def copy_set_param(self, key: str, value: typing.Any = None) -> URL:
|
| 343 |
+
return self.copy_with(params=self.params.set(key, value))
|
| 344 |
+
|
| 345 |
+
def copy_add_param(self, key: str, value: typing.Any = None) -> URL:
|
| 346 |
+
return self.copy_with(params=self.params.add(key, value))
|
| 347 |
+
|
| 348 |
+
def copy_remove_param(self, key: str) -> URL:
|
| 349 |
+
return self.copy_with(params=self.params.remove(key))
|
| 350 |
+
|
| 351 |
+
def copy_merge_params(self, params: QueryParamTypes) -> URL:
|
| 352 |
+
return self.copy_with(params=self.params.merge(params))
|
| 353 |
+
|
| 354 |
+
def join(self, url: URL | str) -> URL:
|
| 355 |
+
"""
|
| 356 |
+
Return an absolute URL, using this URL as the base.
|
| 357 |
+
|
| 358 |
+
Eg.
|
| 359 |
+
|
| 360 |
+
url = httpx.URL("https://www.example.com/test")
|
| 361 |
+
url = url.join("/new/path")
|
| 362 |
+
assert url == "https://www.example.com/new/path"
|
| 363 |
+
"""
|
| 364 |
+
from urllib.parse import urljoin
|
| 365 |
+
|
| 366 |
+
return URL(urljoin(str(self), str(URL(url))))
|
| 367 |
+
|
| 368 |
+
def __hash__(self) -> int:
|
| 369 |
+
return hash(str(self))
|
| 370 |
+
|
| 371 |
+
def __eq__(self, other: typing.Any) -> bool:
|
| 372 |
+
return isinstance(other, (URL, str)) and str(self) == str(URL(other))
|
| 373 |
+
|
| 374 |
+
def __str__(self) -> str:
|
| 375 |
+
return str(self._uri_reference)
|
| 376 |
+
|
| 377 |
+
def __repr__(self) -> str:
|
| 378 |
+
scheme, userinfo, host, port, path, query, fragment = self._uri_reference
|
| 379 |
+
|
| 380 |
+
if ":" in userinfo:
|
| 381 |
+
# Mask any password component.
|
| 382 |
+
userinfo = f'{userinfo.split(":")[0]}:[secure]'
|
| 383 |
+
|
| 384 |
+
authority = "".join(
|
| 385 |
+
[
|
| 386 |
+
f"{userinfo}@" if userinfo else "",
|
| 387 |
+
f"[{host}]" if ":" in host else host,
|
| 388 |
+
f":{port}" if port is not None else "",
|
| 389 |
+
]
|
| 390 |
+
)
|
| 391 |
+
url = "".join(
|
| 392 |
+
[
|
| 393 |
+
f"{self.scheme}:" if scheme else "",
|
| 394 |
+
f"//{authority}" if authority else "",
|
| 395 |
+
path,
|
| 396 |
+
f"?{query}" if query is not None else "",
|
| 397 |
+
f"#{fragment}" if fragment is not None else "",
|
| 398 |
+
]
|
| 399 |
+
)
|
| 400 |
+
|
| 401 |
+
return f"{self.__class__.__name__}({url!r})"
|
| 402 |
+
|
| 403 |
+
@property
|
| 404 |
+
def raw(self) -> tuple[bytes, bytes, int, bytes]: # pragma: nocover
|
| 405 |
+
import collections
|
| 406 |
+
import warnings
|
| 407 |
+
|
| 408 |
+
warnings.warn("URL.raw is deprecated.")
|
| 409 |
+
RawURL = collections.namedtuple(
|
| 410 |
+
"RawURL", ["raw_scheme", "raw_host", "port", "raw_path"]
|
| 411 |
+
)
|
| 412 |
+
return RawURL(
|
| 413 |
+
raw_scheme=self.raw_scheme,
|
| 414 |
+
raw_host=self.raw_host,
|
| 415 |
+
port=self.port,
|
| 416 |
+
raw_path=self.raw_path,
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
class QueryParams(typing.Mapping[str, str]):
|
| 421 |
+
"""
|
| 422 |
+
URL query parameters, as a multi-dict.
|
| 423 |
+
"""
|
| 424 |
+
|
| 425 |
+
def __init__(self, *args: QueryParamTypes | None, **kwargs: typing.Any) -> None:
|
| 426 |
+
assert len(args) < 2, "Too many arguments."
|
| 427 |
+
assert not (args and kwargs), "Cannot mix named and unnamed arguments."
|
| 428 |
+
|
| 429 |
+
value = args[0] if args else kwargs
|
| 430 |
+
|
| 431 |
+
if value is None or isinstance(value, (str, bytes)):
|
| 432 |
+
value = value.decode("ascii") if isinstance(value, bytes) else value
|
| 433 |
+
self._dict = parse_qs(value, keep_blank_values=True)
|
| 434 |
+
elif isinstance(value, QueryParams):
|
| 435 |
+
self._dict = {k: list(v) for k, v in value._dict.items()}
|
| 436 |
+
else:
|
| 437 |
+
dict_value: dict[typing.Any, list[typing.Any]] = {}
|
| 438 |
+
if isinstance(value, (list, tuple)):
|
| 439 |
+
# Convert list inputs like:
|
| 440 |
+
# [("a", "123"), ("a", "456"), ("b", "789")]
|
| 441 |
+
# To a dict representation, like:
|
| 442 |
+
# {"a": ["123", "456"], "b": ["789"]}
|
| 443 |
+
for item in value:
|
| 444 |
+
dict_value.setdefault(item[0], []).append(item[1])
|
| 445 |
+
else:
|
| 446 |
+
# Convert dict inputs like:
|
| 447 |
+
# {"a": "123", "b": ["456", "789"]}
|
| 448 |
+
# To dict inputs where values are always lists, like:
|
| 449 |
+
# {"a": ["123"], "b": ["456", "789"]}
|
| 450 |
+
dict_value = {
|
| 451 |
+
k: list(v) if isinstance(v, (list, tuple)) else [v]
|
| 452 |
+
for k, v in value.items()
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
# Ensure that keys and values are neatly coerced to strings.
|
| 456 |
+
# We coerce values `True` and `False` to JSON-like "true" and "false"
|
| 457 |
+
# representations, and coerce `None` values to the empty string.
|
| 458 |
+
self._dict = {
|
| 459 |
+
str(k): [primitive_value_to_str(item) for item in v]
|
| 460 |
+
for k, v in dict_value.items()
|
| 461 |
+
}
|
| 462 |
+
|
| 463 |
+
def keys(self) -> typing.KeysView[str]:
|
| 464 |
+
"""
|
| 465 |
+
Return all the keys in the query params.
|
| 466 |
+
|
| 467 |
+
Usage:
|
| 468 |
+
|
| 469 |
+
q = httpx.QueryParams("a=123&a=456&b=789")
|
| 470 |
+
assert list(q.keys()) == ["a", "b"]
|
| 471 |
+
"""
|
| 472 |
+
return self._dict.keys()
|
| 473 |
+
|
| 474 |
+
def values(self) -> typing.ValuesView[str]:
|
| 475 |
+
"""
|
| 476 |
+
Return all the values in the query params. If a key occurs more than once
|
| 477 |
+
only the first item for that key is returned.
|
| 478 |
+
|
| 479 |
+
Usage:
|
| 480 |
+
|
| 481 |
+
q = httpx.QueryParams("a=123&a=456&b=789")
|
| 482 |
+
assert list(q.values()) == ["123", "789"]
|
| 483 |
+
"""
|
| 484 |
+
return {k: v[0] for k, v in self._dict.items()}.values()
|
| 485 |
+
|
| 486 |
+
def items(self) -> typing.ItemsView[str, str]:
|
| 487 |
+
"""
|
| 488 |
+
Return all items in the query params. If a key occurs more than once
|
| 489 |
+
only the first item for that key is returned.
|
| 490 |
+
|
| 491 |
+
Usage:
|
| 492 |
+
|
| 493 |
+
q = httpx.QueryParams("a=123&a=456&b=789")
|
| 494 |
+
assert list(q.items()) == [("a", "123"), ("b", "789")]
|
| 495 |
+
"""
|
| 496 |
+
return {k: v[0] for k, v in self._dict.items()}.items()
|
| 497 |
+
|
| 498 |
+
def multi_items(self) -> list[tuple[str, str]]:
|
| 499 |
+
"""
|
| 500 |
+
Return all items in the query params. Allow duplicate keys to occur.
|
| 501 |
+
|
| 502 |
+
Usage:
|
| 503 |
+
|
| 504 |
+
q = httpx.QueryParams("a=123&a=456&b=789")
|
| 505 |
+
assert list(q.multi_items()) == [("a", "123"), ("a", "456"), ("b", "789")]
|
| 506 |
+
"""
|
| 507 |
+
multi_items: list[tuple[str, str]] = []
|
| 508 |
+
for k, v in self._dict.items():
|
| 509 |
+
multi_items.extend([(k, i) for i in v])
|
| 510 |
+
return multi_items
|
| 511 |
+
|
| 512 |
+
def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any:
|
| 513 |
+
"""
|
| 514 |
+
Get a value from the query param for a given key. If the key occurs
|
| 515 |
+
more than once, then only the first value is returned.
|
| 516 |
+
|
| 517 |
+
Usage:
|
| 518 |
+
|
| 519 |
+
q = httpx.QueryParams("a=123&a=456&b=789")
|
| 520 |
+
assert q.get("a") == "123"
|
| 521 |
+
"""
|
| 522 |
+
if key in self._dict:
|
| 523 |
+
return self._dict[str(key)][0]
|
| 524 |
+
return default
|
| 525 |
+
|
| 526 |
+
def get_list(self, key: str) -> list[str]:
|
| 527 |
+
"""
|
| 528 |
+
Get all values from the query param for a given key.
|
| 529 |
+
|
| 530 |
+
Usage:
|
| 531 |
+
|
| 532 |
+
q = httpx.QueryParams("a=123&a=456&b=789")
|
| 533 |
+
assert q.get_list("a") == ["123", "456"]
|
| 534 |
+
"""
|
| 535 |
+
return list(self._dict.get(str(key), []))
|
| 536 |
+
|
| 537 |
+
def set(self, key: str, value: typing.Any = None) -> QueryParams:
|
| 538 |
+
"""
|
| 539 |
+
Return a new QueryParams instance, setting the value of a key.
|
| 540 |
+
|
| 541 |
+
Usage:
|
| 542 |
+
|
| 543 |
+
q = httpx.QueryParams("a=123")
|
| 544 |
+
q = q.set("a", "456")
|
| 545 |
+
assert q == httpx.QueryParams("a=456")
|
| 546 |
+
"""
|
| 547 |
+
q = QueryParams()
|
| 548 |
+
q._dict = dict(self._dict)
|
| 549 |
+
q._dict[str(key)] = [primitive_value_to_str(value)]
|
| 550 |
+
return q
|
| 551 |
+
|
| 552 |
+
def add(self, key: str, value: typing.Any = None) -> QueryParams:
|
| 553 |
+
"""
|
| 554 |
+
Return a new QueryParams instance, setting or appending the value of a key.
|
| 555 |
+
|
| 556 |
+
Usage:
|
| 557 |
+
|
| 558 |
+
q = httpx.QueryParams("a=123")
|
| 559 |
+
q = q.add("a", "456")
|
| 560 |
+
assert q == httpx.QueryParams("a=123&a=456")
|
| 561 |
+
"""
|
| 562 |
+
q = QueryParams()
|
| 563 |
+
q._dict = dict(self._dict)
|
| 564 |
+
q._dict[str(key)] = q.get_list(key) + [primitive_value_to_str(value)]
|
| 565 |
+
return q
|
| 566 |
+
|
| 567 |
+
def remove(self, key: str) -> QueryParams:
|
| 568 |
+
"""
|
| 569 |
+
Return a new QueryParams instance, removing the value of a key.
|
| 570 |
+
|
| 571 |
+
Usage:
|
| 572 |
+
|
| 573 |
+
q = httpx.QueryParams("a=123")
|
| 574 |
+
q = q.remove("a")
|
| 575 |
+
assert q == httpx.QueryParams("")
|
| 576 |
+
"""
|
| 577 |
+
q = QueryParams()
|
| 578 |
+
q._dict = dict(self._dict)
|
| 579 |
+
q._dict.pop(str(key), None)
|
| 580 |
+
return q
|
| 581 |
+
|
| 582 |
+
def merge(self, params: QueryParamTypes | None = None) -> QueryParams:
|
| 583 |
+
"""
|
| 584 |
+
Return a new QueryParams instance, updated with.
|
| 585 |
+
|
| 586 |
+
Usage:
|
| 587 |
+
|
| 588 |
+
q = httpx.QueryParams("a=123")
|
| 589 |
+
q = q.merge({"b": "456"})
|
| 590 |
+
assert q == httpx.QueryParams("a=123&b=456")
|
| 591 |
+
|
| 592 |
+
q = httpx.QueryParams("a=123")
|
| 593 |
+
q = q.merge({"a": "456", "b": "789"})
|
| 594 |
+
assert q == httpx.QueryParams("a=456&b=789")
|
| 595 |
+
"""
|
| 596 |
+
q = QueryParams(params)
|
| 597 |
+
q._dict = {**self._dict, **q._dict}
|
| 598 |
+
return q
|
| 599 |
+
|
| 600 |
+
def __getitem__(self, key: typing.Any) -> str:
|
| 601 |
+
return self._dict[key][0]
|
| 602 |
+
|
| 603 |
+
def __contains__(self, key: typing.Any) -> bool:
|
| 604 |
+
return key in self._dict
|
| 605 |
+
|
| 606 |
+
def __iter__(self) -> typing.Iterator[typing.Any]:
|
| 607 |
+
return iter(self.keys())
|
| 608 |
+
|
| 609 |
+
def __len__(self) -> int:
|
| 610 |
+
return len(self._dict)
|
| 611 |
+
|
| 612 |
+
def __bool__(self) -> bool:
|
| 613 |
+
return bool(self._dict)
|
| 614 |
+
|
| 615 |
+
def __hash__(self) -> int:
|
| 616 |
+
return hash(str(self))
|
| 617 |
+
|
| 618 |
+
def __eq__(self, other: typing.Any) -> bool:
|
| 619 |
+
if not isinstance(other, self.__class__):
|
| 620 |
+
return False
|
| 621 |
+
return sorted(self.multi_items()) == sorted(other.multi_items())
|
| 622 |
+
|
| 623 |
+
def __str__(self) -> str:
|
| 624 |
+
return urlencode(self.multi_items())
|
| 625 |
+
|
| 626 |
+
def __repr__(self) -> str:
|
| 627 |
+
class_name = self.__class__.__name__
|
| 628 |
+
query_string = str(self)
|
| 629 |
+
return f"{class_name}({query_string!r})"
|
| 630 |
+
|
| 631 |
+
def update(self, params: QueryParamTypes | None = None) -> None:
|
| 632 |
+
raise RuntimeError(
|
| 633 |
+
"QueryParams are immutable since 0.18.0. "
|
| 634 |
+
"Use `q = q.merge(...)` to create an updated copy."
|
| 635 |
+
)
|
| 636 |
+
|
| 637 |
+
def __setitem__(self, key: str, value: str) -> None:
|
| 638 |
+
raise RuntimeError(
|
| 639 |
+
"QueryParams are immutable since 0.18.0. "
|
| 640 |
+
"Use `q = q.set(key, value)` to create an updated copy."
|
| 641 |
+
)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_utils.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import ipaddress
|
| 4 |
+
import os
|
| 5 |
+
import re
|
| 6 |
+
import typing
|
| 7 |
+
from urllib.request import getproxies
|
| 8 |
+
|
| 9 |
+
from ._types import PrimitiveData
|
| 10 |
+
|
| 11 |
+
if typing.TYPE_CHECKING: # pragma: no cover
|
| 12 |
+
from ._urls import URL
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def primitive_value_to_str(value: PrimitiveData) -> str:
|
| 16 |
+
"""
|
| 17 |
+
Coerce a primitive data type into a string value.
|
| 18 |
+
|
| 19 |
+
Note that we prefer JSON-style 'true'/'false' for boolean values here.
|
| 20 |
+
"""
|
| 21 |
+
if value is True:
|
| 22 |
+
return "true"
|
| 23 |
+
elif value is False:
|
| 24 |
+
return "false"
|
| 25 |
+
elif value is None:
|
| 26 |
+
return ""
|
| 27 |
+
return str(value)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def get_environment_proxies() -> dict[str, str | None]:
|
| 31 |
+
"""Gets proxy information from the environment"""
|
| 32 |
+
|
| 33 |
+
# urllib.request.getproxies() falls back on System
|
| 34 |
+
# Registry and Config for proxies on Windows and macOS.
|
| 35 |
+
# We don't want to propagate non-HTTP proxies into
|
| 36 |
+
# our configuration such as 'TRAVIS_APT_PROXY'.
|
| 37 |
+
proxy_info = getproxies()
|
| 38 |
+
mounts: dict[str, str | None] = {}
|
| 39 |
+
|
| 40 |
+
for scheme in ("http", "https", "all"):
|
| 41 |
+
if proxy_info.get(scheme):
|
| 42 |
+
hostname = proxy_info[scheme]
|
| 43 |
+
mounts[f"{scheme}://"] = (
|
| 44 |
+
hostname if "://" in hostname else f"http://{hostname}"
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")]
|
| 48 |
+
for hostname in no_proxy_hosts:
|
| 49 |
+
# See https://curl.haxx.se/libcurl/c/CURLOPT_NOPROXY.html for details
|
| 50 |
+
# on how names in `NO_PROXY` are handled.
|
| 51 |
+
if hostname == "*":
|
| 52 |
+
# If NO_PROXY=* is used or if "*" occurs as any one of the comma
|
| 53 |
+
# separated hostnames, then we should just bypass any information
|
| 54 |
+
# from HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and always ignore
|
| 55 |
+
# proxies.
|
| 56 |
+
return {}
|
| 57 |
+
elif hostname:
|
| 58 |
+
# NO_PROXY=.google.com is marked as "all://*.google.com,
|
| 59 |
+
# which disables "www.google.com" but not "google.com"
|
| 60 |
+
# NO_PROXY=google.com is marked as "all://*google.com,
|
| 61 |
+
# which disables "www.google.com" and "google.com".
|
| 62 |
+
# (But not "wwwgoogle.com")
|
| 63 |
+
# NO_PROXY can include domains, IPv6, IPv4 addresses and "localhost"
|
| 64 |
+
# NO_PROXY=example.com,::1,localhost,192.168.0.0/16
|
| 65 |
+
if "://" in hostname:
|
| 66 |
+
mounts[hostname] = None
|
| 67 |
+
elif is_ipv4_hostname(hostname):
|
| 68 |
+
mounts[f"all://{hostname}"] = None
|
| 69 |
+
elif is_ipv6_hostname(hostname):
|
| 70 |
+
mounts[f"all://[{hostname}]"] = None
|
| 71 |
+
elif hostname.lower() == "localhost":
|
| 72 |
+
mounts[f"all://{hostname}"] = None
|
| 73 |
+
else:
|
| 74 |
+
mounts[f"all://*{hostname}"] = None
|
| 75 |
+
|
| 76 |
+
return mounts
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def to_bytes(value: str | bytes, encoding: str = "utf-8") -> bytes:
|
| 80 |
+
return value.encode(encoding) if isinstance(value, str) else value
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def to_str(value: str | bytes, encoding: str = "utf-8") -> str:
|
| 84 |
+
return value if isinstance(value, str) else value.decode(encoding)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def to_bytes_or_str(value: str, match_type_of: typing.AnyStr) -> typing.AnyStr:
|
| 88 |
+
return value if isinstance(match_type_of, str) else value.encode()
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def unquote(value: str) -> str:
|
| 92 |
+
return value[1:-1] if value[0] == value[-1] == '"' else value
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def peek_filelike_length(stream: typing.Any) -> int | None:
|
| 96 |
+
"""
|
| 97 |
+
Given a file-like stream object, return its length in number of bytes
|
| 98 |
+
without reading it into memory.
|
| 99 |
+
"""
|
| 100 |
+
try:
|
| 101 |
+
# Is it an actual file?
|
| 102 |
+
fd = stream.fileno()
|
| 103 |
+
# Yup, seems to be an actual file.
|
| 104 |
+
length = os.fstat(fd).st_size
|
| 105 |
+
except (AttributeError, OSError):
|
| 106 |
+
# No... Maybe it's something that supports random access, like `io.BytesIO`?
|
| 107 |
+
try:
|
| 108 |
+
# Assuming so, go to end of stream to figure out its length,
|
| 109 |
+
# then put it back in place.
|
| 110 |
+
offset = stream.tell()
|
| 111 |
+
length = stream.seek(0, os.SEEK_END)
|
| 112 |
+
stream.seek(offset)
|
| 113 |
+
except (AttributeError, OSError):
|
| 114 |
+
# Not even that? Sorry, we're doomed...
|
| 115 |
+
return None
|
| 116 |
+
|
| 117 |
+
return length
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
class URLPattern:
|
| 121 |
+
"""
|
| 122 |
+
A utility class currently used for making lookups against proxy keys...
|
| 123 |
+
|
| 124 |
+
# Wildcard matching...
|
| 125 |
+
>>> pattern = URLPattern("all://")
|
| 126 |
+
>>> pattern.matches(httpx.URL("http://example.com"))
|
| 127 |
+
True
|
| 128 |
+
|
| 129 |
+
# Witch scheme matching...
|
| 130 |
+
>>> pattern = URLPattern("https://")
|
| 131 |
+
>>> pattern.matches(httpx.URL("https://example.com"))
|
| 132 |
+
True
|
| 133 |
+
>>> pattern.matches(httpx.URL("http://example.com"))
|
| 134 |
+
False
|
| 135 |
+
|
| 136 |
+
# With domain matching...
|
| 137 |
+
>>> pattern = URLPattern("https://example.com")
|
| 138 |
+
>>> pattern.matches(httpx.URL("https://example.com"))
|
| 139 |
+
True
|
| 140 |
+
>>> pattern.matches(httpx.URL("http://example.com"))
|
| 141 |
+
False
|
| 142 |
+
>>> pattern.matches(httpx.URL("https://other.com"))
|
| 143 |
+
False
|
| 144 |
+
|
| 145 |
+
# Wildcard scheme, with domain matching...
|
| 146 |
+
>>> pattern = URLPattern("all://example.com")
|
| 147 |
+
>>> pattern.matches(httpx.URL("https://example.com"))
|
| 148 |
+
True
|
| 149 |
+
>>> pattern.matches(httpx.URL("http://example.com"))
|
| 150 |
+
True
|
| 151 |
+
>>> pattern.matches(httpx.URL("https://other.com"))
|
| 152 |
+
False
|
| 153 |
+
|
| 154 |
+
# With port matching...
|
| 155 |
+
>>> pattern = URLPattern("https://example.com:1234")
|
| 156 |
+
>>> pattern.matches(httpx.URL("https://example.com:1234"))
|
| 157 |
+
True
|
| 158 |
+
>>> pattern.matches(httpx.URL("https://example.com"))
|
| 159 |
+
False
|
| 160 |
+
"""
|
| 161 |
+
|
| 162 |
+
def __init__(self, pattern: str) -> None:
|
| 163 |
+
from ._urls import URL
|
| 164 |
+
|
| 165 |
+
if pattern and ":" not in pattern:
|
| 166 |
+
raise ValueError(
|
| 167 |
+
f"Proxy keys should use proper URL forms rather "
|
| 168 |
+
f"than plain scheme strings. "
|
| 169 |
+
f'Instead of "{pattern}", use "{pattern}://"'
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
url = URL(pattern)
|
| 173 |
+
self.pattern = pattern
|
| 174 |
+
self.scheme = "" if url.scheme == "all" else url.scheme
|
| 175 |
+
self.host = "" if url.host == "*" else url.host
|
| 176 |
+
self.port = url.port
|
| 177 |
+
if not url.host or url.host == "*":
|
| 178 |
+
self.host_regex: typing.Pattern[str] | None = None
|
| 179 |
+
elif url.host.startswith("*."):
|
| 180 |
+
# *.example.com should match "www.example.com", but not "example.com"
|
| 181 |
+
domain = re.escape(url.host[2:])
|
| 182 |
+
self.host_regex = re.compile(f"^.+\\.{domain}$")
|
| 183 |
+
elif url.host.startswith("*"):
|
| 184 |
+
# *example.com should match "www.example.com" and "example.com"
|
| 185 |
+
domain = re.escape(url.host[1:])
|
| 186 |
+
self.host_regex = re.compile(f"^(.+\\.)?{domain}$")
|
| 187 |
+
else:
|
| 188 |
+
# example.com should match "example.com" but not "www.example.com"
|
| 189 |
+
domain = re.escape(url.host)
|
| 190 |
+
self.host_regex = re.compile(f"^{domain}$")
|
| 191 |
+
|
| 192 |
+
def matches(self, other: URL) -> bool:
|
| 193 |
+
if self.scheme and self.scheme != other.scheme:
|
| 194 |
+
return False
|
| 195 |
+
if (
|
| 196 |
+
self.host
|
| 197 |
+
and self.host_regex is not None
|
| 198 |
+
and not self.host_regex.match(other.host)
|
| 199 |
+
):
|
| 200 |
+
return False
|
| 201 |
+
if self.port is not None and self.port != other.port:
|
| 202 |
+
return False
|
| 203 |
+
return True
|
| 204 |
+
|
| 205 |
+
@property
|
| 206 |
+
def priority(self) -> tuple[int, int, int]:
|
| 207 |
+
"""
|
| 208 |
+
The priority allows URLPattern instances to be sortable, so that
|
| 209 |
+
we can match from most specific to least specific.
|
| 210 |
+
"""
|
| 211 |
+
# URLs with a port should take priority over URLs without a port.
|
| 212 |
+
port_priority = 0 if self.port is not None else 1
|
| 213 |
+
# Longer hostnames should match first.
|
| 214 |
+
host_priority = -len(self.host)
|
| 215 |
+
# Longer schemes should match first.
|
| 216 |
+
scheme_priority = -len(self.scheme)
|
| 217 |
+
return (port_priority, host_priority, scheme_priority)
|
| 218 |
+
|
| 219 |
+
def __hash__(self) -> int:
|
| 220 |
+
return hash(self.pattern)
|
| 221 |
+
|
| 222 |
+
def __lt__(self, other: URLPattern) -> bool:
|
| 223 |
+
return self.priority < other.priority
|
| 224 |
+
|
| 225 |
+
def __eq__(self, other: typing.Any) -> bool:
|
| 226 |
+
return isinstance(other, URLPattern) and self.pattern == other.pattern
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def is_ipv4_hostname(hostname: str) -> bool:
|
| 230 |
+
try:
|
| 231 |
+
ipaddress.IPv4Address(hostname.split("/")[0])
|
| 232 |
+
except Exception:
|
| 233 |
+
return False
|
| 234 |
+
return True
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def is_ipv6_hostname(hostname: str) -> bool:
|
| 238 |
+
try:
|
| 239 |
+
ipaddress.IPv6Address(hostname.split("/")[0])
|
| 240 |
+
except Exception:
|
| 241 |
+
return False
|
| 242 |
+
return True
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/py.typed
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/METADATA
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: httpx-sse
|
| 3 |
+
Version: 0.4.3
|
| 4 |
+
Summary: Consume Server-Sent Event (SSE) messages with HTTPX.
|
| 5 |
+
Author-email: Florimond Manca <florimond.manca@protonmail.com>
|
| 6 |
+
License: MIT
|
| 7 |
+
Project-URL: Homepage, https://github.com/florimondmanca/httpx-sse
|
| 8 |
+
Classifier: Development Status :: 4 - Beta
|
| 9 |
+
Classifier: Intended Audience :: Developers
|
| 10 |
+
Classifier: Programming Language :: Python :: 3
|
| 11 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 12 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 13 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 14 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 17 |
+
Requires-Python: >=3.9
|
| 18 |
+
Description-Content-Type: text/markdown
|
| 19 |
+
License-File: LICENSE
|
| 20 |
+
Dynamic: license-file
|
| 21 |
+
|
| 22 |
+
# httpx-sse
|
| 23 |
+
|
| 24 |
+
[](https://dev.azure.com/florimondmanca/public/_build?definitionId=19)
|
| 25 |
+
[](https://codecov.io/gh/florimondmanca/httpx-sse)
|
| 26 |
+
[](https://pypi.org/project/httpx-sse)
|
| 27 |
+
|
| 28 |
+
Consume [Server-Sent Event (SSE)](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events) messages with [HTTPX](https://www.python-httpx.org).
|
| 29 |
+
|
| 30 |
+
**Table of contents**
|
| 31 |
+
|
| 32 |
+
- [Installation](#installation)
|
| 33 |
+
- [Quickstart](#quickstart)
|
| 34 |
+
- [How-To](#how-to)
|
| 35 |
+
- [API Reference](#api-reference)
|
| 36 |
+
|
| 37 |
+
## Installation
|
| 38 |
+
|
| 39 |
+
**NOTE**: This is beta software. Please be sure to pin your dependencies.
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
pip install httpx-sse=="0.4.*"
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
## Quickstart
|
| 46 |
+
|
| 47 |
+
`httpx-sse` provides the [`connect_sse`](#connect_sse) and [`aconnect_sse`](#aconnect_sse) helpers for connecting to an SSE endpoint. The resulting [`EventSource`](#eventsource) object exposes the [`.iter_sse()`](#iter_sse) and [`.aiter_sse()`](#aiter_sse) methods to iterate over the server-sent events.
|
| 48 |
+
|
| 49 |
+
Example usage:
|
| 50 |
+
|
| 51 |
+
```python
|
| 52 |
+
import httpx
|
| 53 |
+
from httpx_sse import connect_sse
|
| 54 |
+
|
| 55 |
+
with httpx.Client() as client:
|
| 56 |
+
with connect_sse(client, "GET", "http://localhost:8000/sse") as event_source:
|
| 57 |
+
for sse in event_source.iter_sse():
|
| 58 |
+
print(sse.event, sse.data, sse.id, sse.retry)
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
You can try this against this example Starlette server ([credit](https://sysid.github.io/sse/)):
|
| 62 |
+
|
| 63 |
+
```python
|
| 64 |
+
# Requirements: pip install uvicorn starlette sse-starlette
|
| 65 |
+
import asyncio
|
| 66 |
+
import uvicorn
|
| 67 |
+
from starlette.applications import Starlette
|
| 68 |
+
from starlette.routing import Route
|
| 69 |
+
from sse_starlette.sse import EventSourceResponse
|
| 70 |
+
|
| 71 |
+
async def numbers(minimum, maximum):
|
| 72 |
+
for i in range(minimum, maximum + 1):
|
| 73 |
+
await asyncio.sleep(0.9)
|
| 74 |
+
yield {"data": i}
|
| 75 |
+
|
| 76 |
+
async def sse(request):
|
| 77 |
+
generator = numbers(1, 5)
|
| 78 |
+
return EventSourceResponse(generator)
|
| 79 |
+
|
| 80 |
+
routes = [
|
| 81 |
+
Route("/sse", endpoint=sse)
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
+
app = Starlette(routes=routes)
|
| 85 |
+
|
| 86 |
+
if __name__ == "__main__":
|
| 87 |
+
uvicorn.run(app)
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## How-To
|
| 91 |
+
|
| 92 |
+
### Calling into Python web apps
|
| 93 |
+
|
| 94 |
+
You can [call into Python web apps](https://www.python-httpx.org/async/#calling-into-python-web-apps) with HTTPX and `httpx-sse` to test SSE endpoints directly.
|
| 95 |
+
|
| 96 |
+
Here's an example of calling into a Starlette ASGI app...
|
| 97 |
+
|
| 98 |
+
```python
|
| 99 |
+
import asyncio
|
| 100 |
+
|
| 101 |
+
import httpx
|
| 102 |
+
from httpx_sse import aconnect_sse
|
| 103 |
+
from sse_starlette.sse import EventSourceResponse
|
| 104 |
+
from starlette.applications import Starlette
|
| 105 |
+
from starlette.routing import Route
|
| 106 |
+
|
| 107 |
+
async def auth_events(request):
|
| 108 |
+
async def events():
|
| 109 |
+
yield {
|
| 110 |
+
"event": "login",
|
| 111 |
+
"data": '{"user_id": "4135"}',
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
return EventSourceResponse(events())
|
| 115 |
+
|
| 116 |
+
app = Starlette(routes=[Route("/sse/auth/", endpoint=auth_events)])
|
| 117 |
+
|
| 118 |
+
async def main():
|
| 119 |
+
async with httpx.AsyncClient(transport=httpx.ASGITransport(app)) as client:
|
| 120 |
+
async with aconnect_sse(
|
| 121 |
+
client, "GET", "http://localhost:8000/sse/auth/"
|
| 122 |
+
) as event_source:
|
| 123 |
+
events = [sse async for sse in event_source.aiter_sse()]
|
| 124 |
+
(sse,) = events
|
| 125 |
+
assert sse.event == "login"
|
| 126 |
+
assert sse.json() == {"user_id": "4135"}
|
| 127 |
+
|
| 128 |
+
asyncio.run(main())
|
| 129 |
+
```
|
| 130 |
+
|
| 131 |
+
### Handling reconnections
|
| 132 |
+
|
| 133 |
+
_(Advanced)_
|
| 134 |
+
|
| 135 |
+
`SSETransport` and `AsyncSSETransport` don't have reconnection built-in. This is because how to perform retries is generally dependent on your use case. As a result, if the connection breaks while attempting to read from the server, you will get an `httpx.ReadError` from `iter_sse()` (or `aiter_sse()`).
|
| 136 |
+
|
| 137 |
+
However, `httpx-sse` does allow implementing reconnection by using the `Last-Event-ID` and reconnection time (in milliseconds), exposed as `sse.id` and `sse.retry` respectively.
|
| 138 |
+
|
| 139 |
+
Here's how you might achieve this using [`stamina`](https://github.com/hynek/stamina)...
|
| 140 |
+
|
| 141 |
+
```python
|
| 142 |
+
import time
|
| 143 |
+
from typing import Iterator
|
| 144 |
+
|
| 145 |
+
import httpx
|
| 146 |
+
from httpx_sse import connect_sse, ServerSentEvent
|
| 147 |
+
from stamina import retry
|
| 148 |
+
|
| 149 |
+
def iter_sse_retrying(client, method, url):
|
| 150 |
+
last_event_id = ""
|
| 151 |
+
reconnection_delay = 0.0
|
| 152 |
+
|
| 153 |
+
# `stamina` will apply jitter and exponential backoff on top of
|
| 154 |
+
# the `retry` reconnection delay sent by the server.
|
| 155 |
+
@retry(on=httpx.ReadError)
|
| 156 |
+
def _iter_sse():
|
| 157 |
+
nonlocal last_event_id, reconnection_delay
|
| 158 |
+
|
| 159 |
+
time.sleep(reconnection_delay)
|
| 160 |
+
|
| 161 |
+
headers = {"Accept": "text/event-stream"}
|
| 162 |
+
|
| 163 |
+
if last_event_id:
|
| 164 |
+
headers["Last-Event-ID"] = last_event_id
|
| 165 |
+
|
| 166 |
+
with connect_sse(client, method, url, headers=headers) as event_source:
|
| 167 |
+
for sse in event_source.iter_sse():
|
| 168 |
+
last_event_id = sse.id
|
| 169 |
+
|
| 170 |
+
if sse.retry is not None:
|
| 171 |
+
reconnection_delay = sse.retry / 1000
|
| 172 |
+
|
| 173 |
+
yield sse
|
| 174 |
+
|
| 175 |
+
return _iter_sse()
|
| 176 |
+
```
|
| 177 |
+
|
| 178 |
+
Usage:
|
| 179 |
+
|
| 180 |
+
```python
|
| 181 |
+
with httpx.Client() as client:
|
| 182 |
+
for sse in iter_sse_retrying(client, "GET", "http://localhost:8000/sse"):
|
| 183 |
+
print(sse.event, sse.data)
|
| 184 |
+
```
|
| 185 |
+
|
| 186 |
+
## API Reference
|
| 187 |
+
|
| 188 |
+
### `connect_sse`
|
| 189 |
+
|
| 190 |
+
```python
|
| 191 |
+
def connect_sse(
|
| 192 |
+
client: httpx.Client,
|
| 193 |
+
method: str,
|
| 194 |
+
url: Union[str, httpx.URL],
|
| 195 |
+
**kwargs,
|
| 196 |
+
) -> ContextManager[EventSource]
|
| 197 |
+
```
|
| 198 |
+
|
| 199 |
+
Connect to an SSE endpoint and return an [`EventSource`](#eventsource) context manager.
|
| 200 |
+
|
| 201 |
+
This sets `Cache-Control: no-store` on the request, as per the SSE spec, as well as `Accept: text/event-stream`.
|
| 202 |
+
|
| 203 |
+
If the response `Content-Type` is not `text/event-stream`, this will raise an [`SSEError`](#sseerror).
|
| 204 |
+
|
| 205 |
+
### `aconnect_sse`
|
| 206 |
+
|
| 207 |
+
```python
|
| 208 |
+
async def aconnect_sse(
|
| 209 |
+
client: httpx.AsyncClient,
|
| 210 |
+
method: str,
|
| 211 |
+
url: Union[str, httpx.URL],
|
| 212 |
+
**kwargs,
|
| 213 |
+
) -> AsyncContextManager[EventSource]
|
| 214 |
+
```
|
| 215 |
+
|
| 216 |
+
An async equivalent to [`connect_sse`](#connect_sse).
|
| 217 |
+
|
| 218 |
+
### `EventSource`
|
| 219 |
+
|
| 220 |
+
```python
|
| 221 |
+
def __init__(response: httpx.Response)
|
| 222 |
+
```
|
| 223 |
+
|
| 224 |
+
Helper for working with an SSE response.
|
| 225 |
+
|
| 226 |
+
#### `response`
|
| 227 |
+
|
| 228 |
+
The underlying [`httpx.Response`](https://www.python-httpx.org/api/#response).
|
| 229 |
+
|
| 230 |
+
You may use this to perform more operations and checks on the response, such as checking for HTTP status errors:
|
| 231 |
+
|
| 232 |
+
```python
|
| 233 |
+
with connect_sse(...) as event_source:
|
| 234 |
+
event_source.response.raise_for_status()
|
| 235 |
+
|
| 236 |
+
for sse in event_source.iter_sse():
|
| 237 |
+
...
|
| 238 |
+
```
|
| 239 |
+
|
| 240 |
+
#### `iter_sse`
|
| 241 |
+
|
| 242 |
+
```python
|
| 243 |
+
def iter_sse() -> Iterator[ServerSentEvent]
|
| 244 |
+
```
|
| 245 |
+
|
| 246 |
+
Decode the response content and yield corresponding [`ServerSentEvent`](#serversentevent).
|
| 247 |
+
|
| 248 |
+
Example usage:
|
| 249 |
+
|
| 250 |
+
```python
|
| 251 |
+
for sse in event_source.iter_sse():
|
| 252 |
+
...
|
| 253 |
+
```
|
| 254 |
+
|
| 255 |
+
#### `aiter_sse`
|
| 256 |
+
|
| 257 |
+
```python
|
| 258 |
+
async def iter_sse() -> AsyncIterator[ServerSentEvent]
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
An async equivalent to `iter_sse`.
|
| 262 |
+
|
| 263 |
+
### `ServerSentEvent`
|
| 264 |
+
|
| 265 |
+
Represents a server-sent event.
|
| 266 |
+
|
| 267 |
+
* `event: str` - Defaults to `"message"`.
|
| 268 |
+
* `data: str` - Defaults to `""`.
|
| 269 |
+
* `id: str` - Defaults to `""`.
|
| 270 |
+
* `retry: str | None` - Defaults to `None`.
|
| 271 |
+
|
| 272 |
+
Methods:
|
| 273 |
+
|
| 274 |
+
* `json() -> Any` - Returns `sse.data` decoded as JSON.
|
| 275 |
+
|
| 276 |
+
### `SSEError`
|
| 277 |
+
|
| 278 |
+
An error that occurred while making a request to an SSE endpoint.
|
| 279 |
+
|
| 280 |
+
Parents:
|
| 281 |
+
|
| 282 |
+
* `httpx.TransportError`
|
| 283 |
+
|
| 284 |
+
## License
|
| 285 |
+
|
| 286 |
+
MIT
|
| 287 |
+
|
| 288 |
+
# Changelog
|
| 289 |
+
|
| 290 |
+
All notable changes to this project will be documented in this file.
|
| 291 |
+
|
| 292 |
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
| 293 |
+
|
| 294 |
+
## 0.4.3 - 2025-10-10
|
| 295 |
+
|
| 296 |
+
### Fixed
|
| 297 |
+
|
| 298 |
+
* Fix performance issue introduced by the improved line parsing from release 0.4.2. (Pull #40)
|
| 299 |
+
|
| 300 |
+
## 0.4.2 - 2025-10-07
|
| 301 |
+
|
| 302 |
+
### Fixed
|
| 303 |
+
|
| 304 |
+
* Fix incorrect newline parsing that was not compliant with SSE spec. (Pull #37)
|
| 305 |
+
|
| 306 |
+
## 0.4.1 - 2025-06-24
|
| 307 |
+
|
| 308 |
+
### Fixed
|
| 309 |
+
|
| 310 |
+
* Always close the response async generator in `aiter_sse()`. (Pull #30)
|
| 311 |
+
|
| 312 |
+
## 0.4.0 - 2023-12-22
|
| 313 |
+
|
| 314 |
+
### Removed
|
| 315 |
+
|
| 316 |
+
* Dropped Python 3.7 support, as it has reached EOL. (Pull #21)
|
| 317 |
+
|
| 318 |
+
### Added
|
| 319 |
+
|
| 320 |
+
* Add official support for Python 3.12. (Pull #21)
|
| 321 |
+
|
| 322 |
+
### Fixed
|
| 323 |
+
|
| 324 |
+
* Allow `Content-Type` that contain but are not strictly `text/event-stream`. (Pull #22 by @dbuades)
|
| 325 |
+
* Improve error message when `Content-Type` is missing. (Pull #20 by @jamesbraza)
|
| 326 |
+
|
| 327 |
+
## 0.3.1 - 2023-06-01
|
| 328 |
+
|
| 329 |
+
### Added
|
| 330 |
+
|
| 331 |
+
* Add `__repr__()` for `ServerSentEvent` model, which may help with debugging and other tasks. (Pull #16)
|
| 332 |
+
|
| 333 |
+
## 0.3.0 - 2023-04-27
|
| 334 |
+
|
| 335 |
+
### Changed
|
| 336 |
+
|
| 337 |
+
* Raising an `SSEError` if the response content type is not `text/event-stream` is now performed as part of `iter_sse()` / `aiter_sse()`, instead of `connect_sse()` / `aconnect_sse()`. This allows inspecting the response before iterating on server-sent events, such as checking for error responses. (Pull #12)
|
| 338 |
+
|
| 339 |
+
## 0.2.0 - 2023-03-27
|
| 340 |
+
|
| 341 |
+
### Changed
|
| 342 |
+
|
| 343 |
+
* `connect_sse()` and `aconnect_sse()` now require a `method` argument: `connect_sse(client, "GET", "https://example.org")`. This provides support for SSE requests with HTTP verbs other than `GET`. (Pull #7)
|
| 344 |
+
|
| 345 |
+
## 0.1.0 - 2023-02-05
|
| 346 |
+
|
| 347 |
+
_Initial release_
|
| 348 |
+
|
| 349 |
+
### Added
|
| 350 |
+
|
| 351 |
+
* Add `connect_sse`, `aconnect_sse()`, `ServerSentEvent` and `SSEError`.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/RECORD
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
httpx_sse-0.4.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 2 |
+
httpx_sse-0.4.3.dist-info/METADATA,sha256=1MbYBO5axG6bO2WVwnq4riunz7X2kseLFv9GPkvRqB4,9671
|
| 3 |
+
httpx_sse-0.4.3.dist-info/RECORD,,
|
| 4 |
+
httpx_sse-0.4.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 5 |
+
httpx_sse-0.4.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
| 6 |
+
httpx_sse-0.4.3.dist-info/licenses/LICENSE,sha256=vuxn5O6Dp68m8kK-2Zh6qGPW0t-3dhBqojYMQYfzGQE,1072
|
| 7 |
+
httpx_sse-0.4.3.dist-info/top_level.txt,sha256=hbK3kwBH9d-rEZQvJ6yBawdDylIKfJ8fh0CM4EVNs04,10
|
| 8 |
+
httpx_sse/__init__.py,sha256=G-F8jpJLo4eJwKvKt6ejTQYPhBHgmksZTNuaQySRAjc,282
|
| 9 |
+
httpx_sse/_api.py,sha256=D7pfkajmdHlms75OV8HpdOrOEr5NbWQlDwMfSFcJ8FU,2919
|
| 10 |
+
httpx_sse/_decoders.py,sha256=na1XyVBiCLbRE8tg0o713U5Gz-m9fBxYPfSMmu_xns8,4005
|
| 11 |
+
httpx_sse/_exceptions.py,sha256=sFZBUT1xIM1DvYQ6ckpzrhBG9d8VffFSZFMIEhv1N58,62
|
| 12 |
+
httpx_sse/_models.py,sha256=4WwGeBEGMR2nbROTZ7PyVlu7chmWg7tbkJyXyd5DTx0,1221
|
| 13 |
+
httpx_sse/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (80.9.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2022 Florimond Manca
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
httpx_sse
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ._api import EventSource, aconnect_sse, connect_sse
|
| 2 |
+
from ._exceptions import SSEError
|
| 3 |
+
from ._models import ServerSentEvent
|
| 4 |
+
|
| 5 |
+
__version__ = "0.4.3"
|
| 6 |
+
|
| 7 |
+
__all__ = [
|
| 8 |
+
"__version__",
|
| 9 |
+
"EventSource",
|
| 10 |
+
"connect_sse",
|
| 11 |
+
"aconnect_sse",
|
| 12 |
+
"ServerSentEvent",
|
| 13 |
+
"SSEError",
|
| 14 |
+
]
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_api.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections.abc import AsyncGenerator
|
| 2 |
+
from contextlib import asynccontextmanager, contextmanager
|
| 3 |
+
from typing import Any, AsyncIterator, Iterator, cast
|
| 4 |
+
|
| 5 |
+
import httpx
|
| 6 |
+
|
| 7 |
+
from ._decoders import SSEDecoder, SSELineDecoder
|
| 8 |
+
from ._exceptions import SSEError
|
| 9 |
+
from ._models import ServerSentEvent
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class EventSource:
|
| 13 |
+
def __init__(self, response: httpx.Response) -> None:
|
| 14 |
+
self._response = response
|
| 15 |
+
|
| 16 |
+
def _check_content_type(self) -> None:
|
| 17 |
+
content_type = self._response.headers.get("content-type", "").partition(";")[0]
|
| 18 |
+
if "text/event-stream" not in content_type:
|
| 19 |
+
raise SSEError(
|
| 20 |
+
"Expected response header Content-Type to contain 'text/event-stream', "
|
| 21 |
+
f"got {content_type!r}"
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
@property
|
| 25 |
+
def response(self) -> httpx.Response:
|
| 26 |
+
return self._response
|
| 27 |
+
|
| 28 |
+
def iter_sse(self) -> Iterator[ServerSentEvent]:
|
| 29 |
+
self._check_content_type()
|
| 30 |
+
decoder = SSEDecoder()
|
| 31 |
+
for line in _iter_sse_lines(self._response):
|
| 32 |
+
line = line.rstrip("\n")
|
| 33 |
+
sse = decoder.decode(line)
|
| 34 |
+
if sse is not None:
|
| 35 |
+
yield sse
|
| 36 |
+
|
| 37 |
+
async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]:
|
| 38 |
+
self._check_content_type()
|
| 39 |
+
decoder = SSEDecoder()
|
| 40 |
+
lines = cast(AsyncGenerator[str, None], _aiter_sse_lines(self._response))
|
| 41 |
+
try:
|
| 42 |
+
async for line in lines:
|
| 43 |
+
line = line.rstrip("\n")
|
| 44 |
+
sse = decoder.decode(line)
|
| 45 |
+
if sse is not None:
|
| 46 |
+
yield sse
|
| 47 |
+
finally:
|
| 48 |
+
await lines.aclose()
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@contextmanager
|
| 52 |
+
def connect_sse(
|
| 53 |
+
client: httpx.Client, method: str, url: str, **kwargs: Any
|
| 54 |
+
) -> Iterator[EventSource]:
|
| 55 |
+
headers = kwargs.pop("headers", {})
|
| 56 |
+
headers["Accept"] = "text/event-stream"
|
| 57 |
+
headers["Cache-Control"] = "no-store"
|
| 58 |
+
|
| 59 |
+
with client.stream(method, url, headers=headers, **kwargs) as response:
|
| 60 |
+
yield EventSource(response)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
@asynccontextmanager
|
| 64 |
+
async def aconnect_sse(
|
| 65 |
+
client: httpx.AsyncClient,
|
| 66 |
+
method: str,
|
| 67 |
+
url: str,
|
| 68 |
+
**kwargs: Any,
|
| 69 |
+
) -> AsyncIterator[EventSource]:
|
| 70 |
+
headers = kwargs.pop("headers", {})
|
| 71 |
+
headers["Accept"] = "text/event-stream"
|
| 72 |
+
headers["Cache-Control"] = "no-store"
|
| 73 |
+
|
| 74 |
+
async with client.stream(method, url, headers=headers, **kwargs) as response:
|
| 75 |
+
yield EventSource(response)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
async def _aiter_sse_lines(response: httpx.Response) -> AsyncIterator[str]:
|
| 79 |
+
decoder = SSELineDecoder()
|
| 80 |
+
async for text in response.aiter_text():
|
| 81 |
+
for line in decoder.decode(text):
|
| 82 |
+
yield line
|
| 83 |
+
for line in decoder.flush():
|
| 84 |
+
yield line
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def _iter_sse_lines(response: httpx.Response) -> Iterator[str]:
|
| 88 |
+
decoder = SSELineDecoder()
|
| 89 |
+
for text in response.iter_text():
|
| 90 |
+
for line in decoder.decode(text):
|
| 91 |
+
yield line
|
| 92 |
+
for line in decoder.flush():
|
| 93 |
+
yield line
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_decoders.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Optional
|
| 2 |
+
|
| 3 |
+
from ._models import ServerSentEvent
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def _splitlines_sse(text: str) -> List[str]:
|
| 7 |
+
"""Split text on \r\n, \r, or \n only."""
|
| 8 |
+
if not text:
|
| 9 |
+
return []
|
| 10 |
+
|
| 11 |
+
if "\r" not in text:
|
| 12 |
+
lines = text.split("\n")
|
| 13 |
+
else:
|
| 14 |
+
normalized = text.replace("\r\n", "\n").replace("\r", "\n")
|
| 15 |
+
lines = normalized.split("\n")
|
| 16 |
+
|
| 17 |
+
if text[-1] in "\r\n":
|
| 18 |
+
lines.pop()
|
| 19 |
+
|
| 20 |
+
return lines
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class SSELineDecoder:
|
| 24 |
+
"""
|
| 25 |
+
Handles incrementally reading lines from text.
|
| 26 |
+
|
| 27 |
+
Mostly a copy of httpx._decoders.LineDecoder, but as per SSE spec, only \r\n, \r,
|
| 28 |
+
and \n are treated as newlines, which differs from the behavior of splitlines()
|
| 29 |
+
used by httpx._decoders.LineDecoder.
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
def __init__(self) -> None:
|
| 33 |
+
self.buffer: list[str] = []
|
| 34 |
+
self.trailing_cr: bool = False
|
| 35 |
+
|
| 36 |
+
def decode(self, text: str) -> list[str]:
|
| 37 |
+
# We always push a trailing `\r` into the next decode iteration.
|
| 38 |
+
if self.trailing_cr:
|
| 39 |
+
text = "\r" + text
|
| 40 |
+
self.trailing_cr = False
|
| 41 |
+
if text.endswith("\r"):
|
| 42 |
+
self.trailing_cr = True
|
| 43 |
+
text = text[:-1]
|
| 44 |
+
|
| 45 |
+
if not text:
|
| 46 |
+
# NOTE: the edge case input of empty text doesn't occur in practice,
|
| 47 |
+
# because other httpx internals filter out this value
|
| 48 |
+
return [] # pragma: no cover
|
| 49 |
+
|
| 50 |
+
trailing_newline = text[-1] in "\n\r"
|
| 51 |
+
lines = _splitlines_sse(text)
|
| 52 |
+
|
| 53 |
+
if len(lines) == 1 and not trailing_newline:
|
| 54 |
+
# No new lines, buffer the input and continue.
|
| 55 |
+
self.buffer.append(lines[0])
|
| 56 |
+
return []
|
| 57 |
+
|
| 58 |
+
if self.buffer:
|
| 59 |
+
# Include any existing buffer in the first portion of the
|
| 60 |
+
# splitlines result.
|
| 61 |
+
lines = ["".join(self.buffer) + lines[0]] + lines[1:]
|
| 62 |
+
self.buffer = []
|
| 63 |
+
|
| 64 |
+
if not trailing_newline:
|
| 65 |
+
# If the last segment of splitlines is not newline terminated,
|
| 66 |
+
# then drop it from our output and start a new buffer.
|
| 67 |
+
self.buffer = [lines.pop()]
|
| 68 |
+
|
| 69 |
+
return lines
|
| 70 |
+
|
| 71 |
+
def flush(self) -> list[str]:
|
| 72 |
+
if not self.buffer and not self.trailing_cr:
|
| 73 |
+
return []
|
| 74 |
+
|
| 75 |
+
lines = ["".join(self.buffer)]
|
| 76 |
+
self.buffer = []
|
| 77 |
+
self.trailing_cr = False
|
| 78 |
+
return lines
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class SSEDecoder:
|
| 82 |
+
def __init__(self) -> None:
|
| 83 |
+
self._event = ""
|
| 84 |
+
self._data: List[str] = []
|
| 85 |
+
self._last_event_id = ""
|
| 86 |
+
self._retry: Optional[int] = None
|
| 87 |
+
|
| 88 |
+
def decode(self, line: str) -> Optional[ServerSentEvent]:
|
| 89 |
+
# See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501
|
| 90 |
+
|
| 91 |
+
if not line:
|
| 92 |
+
if (
|
| 93 |
+
not self._event
|
| 94 |
+
and not self._data
|
| 95 |
+
and not self._last_event_id
|
| 96 |
+
and self._retry is None
|
| 97 |
+
):
|
| 98 |
+
return None
|
| 99 |
+
|
| 100 |
+
sse = ServerSentEvent(
|
| 101 |
+
event=self._event,
|
| 102 |
+
data="\n".join(self._data),
|
| 103 |
+
id=self._last_event_id,
|
| 104 |
+
retry=self._retry,
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
# NOTE: as per the SSE spec, do not reset last_event_id.
|
| 108 |
+
self._event = ""
|
| 109 |
+
self._data = []
|
| 110 |
+
self._retry = None
|
| 111 |
+
|
| 112 |
+
return sse
|
| 113 |
+
|
| 114 |
+
if line.startswith(":"):
|
| 115 |
+
return None
|
| 116 |
+
|
| 117 |
+
fieldname, _, value = line.partition(":")
|
| 118 |
+
|
| 119 |
+
if value.startswith(" "):
|
| 120 |
+
value = value[1:]
|
| 121 |
+
|
| 122 |
+
if fieldname == "event":
|
| 123 |
+
self._event = value
|
| 124 |
+
elif fieldname == "data":
|
| 125 |
+
self._data.append(value)
|
| 126 |
+
elif fieldname == "id":
|
| 127 |
+
if "\0" in value:
|
| 128 |
+
pass
|
| 129 |
+
else:
|
| 130 |
+
self._last_event_id = value
|
| 131 |
+
elif fieldname == "retry":
|
| 132 |
+
try:
|
| 133 |
+
self._retry = int(value)
|
| 134 |
+
except (TypeError, ValueError):
|
| 135 |
+
pass
|
| 136 |
+
else:
|
| 137 |
+
pass # Field is ignored.
|
| 138 |
+
|
| 139 |
+
return None
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_exceptions.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import httpx
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class SSEError(httpx.TransportError):
|
| 5 |
+
pass
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_models.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from typing import Any, Optional
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class ServerSentEvent:
|
| 6 |
+
def __init__(
|
| 7 |
+
self,
|
| 8 |
+
event: Optional[str] = None,
|
| 9 |
+
data: Optional[str] = None,
|
| 10 |
+
id: Optional[str] = None,
|
| 11 |
+
retry: Optional[int] = None,
|
| 12 |
+
) -> None:
|
| 13 |
+
if not event:
|
| 14 |
+
event = "message"
|
| 15 |
+
|
| 16 |
+
if data is None:
|
| 17 |
+
data = ""
|
| 18 |
+
|
| 19 |
+
if id is None:
|
| 20 |
+
id = ""
|
| 21 |
+
|
| 22 |
+
self._event = event
|
| 23 |
+
self._data = data
|
| 24 |
+
self._id = id
|
| 25 |
+
self._retry = retry
|
| 26 |
+
|
| 27 |
+
@property
|
| 28 |
+
def event(self) -> str:
|
| 29 |
+
return self._event
|
| 30 |
+
|
| 31 |
+
@property
|
| 32 |
+
def data(self) -> str:
|
| 33 |
+
return self._data
|
| 34 |
+
|
| 35 |
+
@property
|
| 36 |
+
def id(self) -> str:
|
| 37 |
+
return self._id
|
| 38 |
+
|
| 39 |
+
@property
|
| 40 |
+
def retry(self) -> Optional[int]:
|
| 41 |
+
return self._retry
|
| 42 |
+
|
| 43 |
+
def json(self) -> Any:
|
| 44 |
+
return json.loads(self.data)
|
| 45 |
+
|
| 46 |
+
def __repr__(self) -> str:
|
| 47 |
+
pieces = [f"event={self.event!r}"]
|
| 48 |
+
if self.data != "":
|
| 49 |
+
pieces.append(f"data={self.data!r}")
|
| 50 |
+
if self.id != "":
|
| 51 |
+
pieces.append(f"id={self.id!r}")
|
| 52 |
+
if self.retry is not None:
|
| 53 |
+
pieces.append(f"retry={self.retry!r}")
|
| 54 |
+
return f"ServerSentEvent({', '.join(pieces)})"
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/py.typed
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/METADATA
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: idna
|
| 3 |
+
Version: 3.18
|
| 4 |
+
Summary: Internationalized Domain Names in Applications (IDNA)
|
| 5 |
+
Author-email: Kim Davies <kim+pypi@gumleaf.org>
|
| 6 |
+
Requires-Python: >=3.9
|
| 7 |
+
Description-Content-Type: text/markdown
|
| 8 |
+
License-Expression: BSD-3-Clause
|
| 9 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 10 |
+
Classifier: Intended Audience :: Developers
|
| 11 |
+
Classifier: Intended Audience :: System Administrators
|
| 12 |
+
Classifier: Operating System :: OS Independent
|
| 13 |
+
Classifier: Programming Language :: Python
|
| 14 |
+
Classifier: Programming Language :: Python :: 3
|
| 15 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 22 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 23 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 24 |
+
Classifier: Topic :: Internet :: Name Service (DNS)
|
| 25 |
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
| 26 |
+
Classifier: Topic :: Utilities
|
| 27 |
+
License-File: LICENSE.md
|
| 28 |
+
Requires-Dist: ruff >= 0.6.2 ; extra == "all"
|
| 29 |
+
Requires-Dist: mypy >= 1.11.2 ; extra == "all"
|
| 30 |
+
Requires-Dist: pytest >= 8.3.2 ; extra == "all"
|
| 31 |
+
Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.md
|
| 32 |
+
Project-URL: Issue tracker, https://github.com/kjd/idna/issues
|
| 33 |
+
Project-URL: Source, https://github.com/kjd/idna
|
| 34 |
+
Provides-Extra: all
|
| 35 |
+
|
| 36 |
+
# Internationalized Domain Names in Applications (IDNA)
|
| 37 |
+
|
| 38 |
+
Support for [Internationalized Domain Names in Applications
|
| 39 |
+
(IDNA)](https://tools.ietf.org/html/rfc5891) and [Unicode IDNA
|
| 40 |
+
Compatibility Processing](https://unicode.org/reports/tr46/). It
|
| 41 |
+
supersedes the standard library's `encodings.idna`, which only
|
| 42 |
+
implements the 2003 specification, offering broader script coverage and
|
| 43 |
+
limiting domains with known security vulnerabilities.
|
| 44 |
+
|
| 45 |
+
## Usage
|
| 46 |
+
|
| 47 |
+
Package may be installed from [PyPI](https://pypi.org/project/idna/) via
|
| 48 |
+
the typical methods (e.g. `python3 -m pip install idna`)
|
| 49 |
+
|
| 50 |
+
For typical usage, the `encode` and `decode` functions will take a
|
| 51 |
+
domain name argument and perform a conversion to ASCII-compatible encoding
|
| 52 |
+
(known as A-labels), or to Unicode strings (known as U-labels)
|
| 53 |
+
respectively.
|
| 54 |
+
|
| 55 |
+
```pycon
|
| 56 |
+
>>> import idna
|
| 57 |
+
>>> idna.encode('ドメイン.テスト')
|
| 58 |
+
b'xn--eckwd4c7c.xn--zckzah'
|
| 59 |
+
>>> print(idna.decode('xn--eckwd4c7c.xn--zckzah'))
|
| 60 |
+
ドメイン.テスト
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
Conversions can be applied at a per-label basis using the `ulabel` or
|
| 64 |
+
`alabel` functions for specialized use cases.
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
### Compatibility Mapping (UTS #46)
|
| 68 |
+
|
| 69 |
+
This library provides support for [Unicode IDNA Compatibility
|
| 70 |
+
Processing](https://unicode.org/reports/tr46/) which normalizes input from
|
| 71 |
+
different potential ways a user may input a domain prior to performing the IDNA
|
| 72 |
+
conversion operations. This functionality, known as a
|
| 73 |
+
[mapping](https://tools.ietf.org/html/rfc5895), is considered by the
|
| 74 |
+
specification to be a local user-interface issue distinct from IDNA
|
| 75 |
+
conversion functionality.
|
| 76 |
+
|
| 77 |
+
For example, "Königsgäßchen" is not a permissible label as capital letters
|
| 78 |
+
are not allowed. UTS 46 will convert this into lower case prior to applying
|
| 79 |
+
the IDNA conversion.
|
| 80 |
+
|
| 81 |
+
```pycon
|
| 82 |
+
>>> import idna
|
| 83 |
+
>>> idna.encode('Königsgäßchen')
|
| 84 |
+
...
|
| 85 |
+
idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed
|
| 86 |
+
>>> idna.encode('Königsgäßchen', uts46=True)
|
| 87 |
+
b'xn--knigsgchen-b4a3dun'
|
| 88 |
+
>>> idna.decode('xn--knigsgchen-b4a3dun')
|
| 89 |
+
'königsgäßchen'
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
When performing a decode operation for display purposes, `decode()`
|
| 93 |
+
accepts a `display=True` argument that leaves any `xn--` label that
|
| 94 |
+
fails to decode unchanged. This is useful for user interface display
|
| 95 |
+
where a domain is in use, the A-label form can be presented when it
|
| 96 |
+
is not a valid IDN.
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
## Exceptions
|
| 100 |
+
|
| 101 |
+
All errors raised during conversion derive from the `idna.IDNAError`
|
| 102 |
+
base class. The more specific exceptions are:
|
| 103 |
+
|
| 104 |
+
* `idna.IDNABidiError` — raised when a label contains an illegal
|
| 105 |
+
combination of left-to-right and right-to-left characters.
|
| 106 |
+
* `idna.InvalidCodepoint` — raised when a label contains a codepoint
|
| 107 |
+
that is INVALID for IDNA.
|
| 108 |
+
* `idna.InvalidCodepointContext` — raised when a CONTEXTO or CONTEXTJ
|
| 109 |
+
codepoint appears in a position whose contextual requirements are
|
| 110 |
+
not satisfied.
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
## Command-line tool
|
| 114 |
+
|
| 115 |
+
The package supports command-line usage to convert domain names
|
| 116 |
+
between their Unicode and ASCII-compatible forms. It can be run either
|
| 117 |
+
as a module (`python3 -m idna`) or, once installed (such as with `uv
|
| 118 |
+
tool` or `pipx`), via the `idna` script:
|
| 119 |
+
|
| 120 |
+
```bash
|
| 121 |
+
$ uv tool install idna
|
| 122 |
+
$ idna xn--e1afmkfd.xn--p1ai
|
| 123 |
+
пример.рф
|
| 124 |
+
$ idna пример.рф
|
| 125 |
+
xn--e1afmkfd.xn--p1ai
|
| 126 |
+
```
|
| 127 |
+
|
| 128 |
+
Mode can be specified with `-e`/`--encode` or `-d`/`--decode`, otherwise
|
| 129 |
+
it will be chosen automatically based on the first input. Multiple
|
| 130 |
+
domains can be supplied either as arguments or through standard input.
|
| 131 |
+
UTS #46 mapping is applied by default, which lets the tool accept
|
| 132 |
+
inputs that aren't strictly valid IDNA 2008 by normalising them first,
|
| 133 |
+
pass `--strict` to disable UTS #46.
|
| 134 |
+
|
| 135 |
+
Conversion failures are reported on stderr together with the
|
| 136 |
+
offending input; processing continues with the remaining domains and
|
| 137 |
+
the tool exits with a non-zero status if any conversion failed.
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
## Additional Notes
|
| 141 |
+
|
| 142 |
+
* **Version support**. This library supports Python 3.9 and higher.
|
| 143 |
+
As this library serves as a low-level toolkit for a variety of
|
| 144 |
+
applications, we strive to support all versions of Python that are
|
| 145 |
+
not beyond end-of-life.
|
| 146 |
+
|
| 147 |
+
* **Emoji**. It is an occasional request to support emoji domains in
|
| 148 |
+
this library. Encoding of symbols like emoji is expressly prohibited by
|
| 149 |
+
the IDNA technical standard, and emoji domains are broadly phased
|
| 150 |
+
out across the domain industry due to associated security risks.
|
| 151 |
+
|
| 152 |
+
* **Regenerating lookup tables**. The IDNA and UTS 46 functionality
|
| 153 |
+
relies upon pre-calculated lookup tables, generated using the
|
| 154 |
+
`idna-data` script in [`tools/`](tools/README.md).
|
| 155 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/RECORD
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/idna,sha256=DpM3F7tbov4G3PhMS4ABNVg-eKZCeW0DaKSiYqKvqyU,337
|
| 2 |
+
idna-3.18.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 3 |
+
idna-3.18.dist-info/METADATA,sha256=Rt_m5axGLQ9oDs2avPZugptqIzSCS02eOXmzETXK8oE,6119
|
| 4 |
+
idna-3.18.dist-info/RECORD,,
|
| 5 |
+
idna-3.18.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 6 |
+
idna-3.18.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
| 7 |
+
idna-3.18.dist-info/entry_points.txt,sha256=7H3nGOHap3jnLE5e7q7Ywr9Vq8axB7WIj5-C_4N2vhw,38
|
| 8 |
+
idna-3.18.dist-info/licenses/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541
|
| 9 |
+
idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868
|
| 10 |
+
idna/__main__.py,sha256=4JMK66Wj4uLZTKbF-sT3LAxOsr6buig77PmOkJCRRxw,83
|
| 11 |
+
idna/cli.py,sha256=swqJLMNc8Uzs60KziNpbWnHuqlG3WRQwJSbo4n8xDAo,4139
|
| 12 |
+
idna/codec.py,sha256=JRbo-f7pEkLdWeiH89Z72UR4VBYhvKDFrQBeNX6sRDE,5040
|
| 13 |
+
idna/compat.py,sha256=AepA39ceRHxkfHP41-FvKW5Ki-f4PfUZ90RUMlCNdmo,1353
|
| 14 |
+
idna/core.py,sha256=SfOr1xO3PoE0RDYx7bMciAnjiyjJPbPw_93AB5IUYOw,24685
|
| 15 |
+
idna/idnadata.py,sha256=Af-mo8WBmkhAK6TyXKOQH88OX0mQNDKtdL7UWtQpppk,44862
|
| 16 |
+
idna/intranges.py,sha256=g49scLSkqJtAhLmOODa7hVHriSjmb60tiTsEoocJdBI,1851
|
| 17 |
+
idna/package_data.py,sha256=TeI94EqAFAFaXfBJwsOPUMLn2969uirPa-DaeaceAyU,21
|
| 18 |
+
idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 19 |
+
idna/uts46data.py,sha256=jujNz5QqWMcJf-XYLv4X1jBvb5FlI0t6-e1mILsgbPk,234325
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: flit 3.12.0
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
idna=idna.cli:main
|
| 3 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/licenses/LICENSE.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BSD 3-Clause License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2013-2026, Kim Davies and contributors.
|
| 4 |
+
All rights reserved.
|
| 5 |
+
|
| 6 |
+
Redistribution and use in source and binary forms, with or without
|
| 7 |
+
modification, are permitted provided that the following conditions are
|
| 8 |
+
met:
|
| 9 |
+
|
| 10 |
+
1. Redistributions of source code must retain the above copyright
|
| 11 |
+
notice, this list of conditions and the following disclaimer.
|
| 12 |
+
|
| 13 |
+
2. Redistributions in binary form must reproduce the above copyright
|
| 14 |
+
notice, this list of conditions and the following disclaimer in the
|
| 15 |
+
documentation and/or other materials provided with the distribution.
|
| 16 |
+
|
| 17 |
+
3. Neither the name of the copyright holder nor the names of its
|
| 18 |
+
contributors may be used to endorse or promote products derived from
|
| 19 |
+
this software without specific prior written permission.
|
| 20 |
+
|
| 21 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 22 |
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| 23 |
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| 24 |
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| 25 |
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| 26 |
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
| 27 |
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
| 28 |
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
| 29 |
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
| 30 |
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 31 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .core import (
|
| 2 |
+
IDNABidiError,
|
| 3 |
+
IDNAError,
|
| 4 |
+
InvalidCodepoint,
|
| 5 |
+
InvalidCodepointContext,
|
| 6 |
+
alabel,
|
| 7 |
+
check_bidi,
|
| 8 |
+
check_hyphen_ok,
|
| 9 |
+
check_initial_combiner,
|
| 10 |
+
check_label,
|
| 11 |
+
check_nfc,
|
| 12 |
+
decode,
|
| 13 |
+
encode,
|
| 14 |
+
ulabel,
|
| 15 |
+
uts46_remap,
|
| 16 |
+
valid_contextj,
|
| 17 |
+
valid_contexto,
|
| 18 |
+
valid_label_length,
|
| 19 |
+
valid_string_length,
|
| 20 |
+
)
|
| 21 |
+
from .intranges import intranges_contain
|
| 22 |
+
from .package_data import __version__
|
| 23 |
+
|
| 24 |
+
__all__ = [
|
| 25 |
+
"__version__",
|
| 26 |
+
"IDNABidiError",
|
| 27 |
+
"IDNAError",
|
| 28 |
+
"InvalidCodepoint",
|
| 29 |
+
"InvalidCodepointContext",
|
| 30 |
+
"alabel",
|
| 31 |
+
"check_bidi",
|
| 32 |
+
"check_hyphen_ok",
|
| 33 |
+
"check_initial_combiner",
|
| 34 |
+
"check_label",
|
| 35 |
+
"check_nfc",
|
| 36 |
+
"decode",
|
| 37 |
+
"encode",
|
| 38 |
+
"intranges_contain",
|
| 39 |
+
"ulabel",
|
| 40 |
+
"uts46_remap",
|
| 41 |
+
"valid_contextj",
|
| 42 |
+
"valid_contexto",
|
| 43 |
+
"valid_label_length",
|
| 44 |
+
"valid_string_length",
|
| 45 |
+
]
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/__main__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
|
| 3 |
+
from .cli import main
|
| 4 |
+
|
| 5 |
+
if __name__ == "__main__":
|
| 6 |
+
sys.exit(main())
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/cli.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Command-line interface for the :mod:`idna` package.
|
| 2 |
+
|
| 3 |
+
Invoked via ``python -m idna``. See :func:`main` for the entry point.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import argparse
|
| 7 |
+
import sys
|
| 8 |
+
from collections.abc import Iterable
|
| 9 |
+
from itertools import chain
|
| 10 |
+
from typing import IO, Optional
|
| 11 |
+
|
| 12 |
+
from . import IDNAError, decode, encode
|
| 13 |
+
from .core import _alabel_prefix, _unicode_dots_re
|
| 14 |
+
from .package_data import __version__
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _looks_like_alabel(s: str) -> bool:
|
| 18 |
+
"""Return True if any label in ``s`` carries the ``xn--`` ACE prefix."""
|
| 19 |
+
prefix = _alabel_prefix.decode("ascii")
|
| 20 |
+
return any(label.lower().startswith(prefix) for label in _unicode_dots_re.split(s))
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def _build_parser() -> argparse.ArgumentParser:
|
| 24 |
+
parser = argparse.ArgumentParser(
|
| 25 |
+
prog="python -m idna",
|
| 26 |
+
description=(
|
| 27 |
+
"Convert a domain name between its Unicode (U-label) and "
|
| 28 |
+
"ASCII-compatible (A-label) forms. With no mode flag, the "
|
| 29 |
+
"direction is chosen from the first input — if it contains "
|
| 30 |
+
"an xn-- label the stream is decoded, otherwise it is "
|
| 31 |
+
"encoded — and the same mode is applied to every remaining "
|
| 32 |
+
"input. UTS #46 mapping is applied by default; pass "
|
| 33 |
+
"--strict to disable it. When no domains are given on the "
|
| 34 |
+
"command line and stdin is piped, one domain per line is "
|
| 35 |
+
"read from stdin."
|
| 36 |
+
),
|
| 37 |
+
)
|
| 38 |
+
mode = parser.add_mutually_exclusive_group()
|
| 39 |
+
mode.add_argument(
|
| 40 |
+
"-e",
|
| 41 |
+
"--encode",
|
| 42 |
+
dest="mode",
|
| 43 |
+
action="store_const",
|
| 44 |
+
const="encode",
|
| 45 |
+
help="Encode the input to its ASCII A-label form.",
|
| 46 |
+
)
|
| 47 |
+
mode.add_argument(
|
| 48 |
+
"-d",
|
| 49 |
+
"--decode",
|
| 50 |
+
dest="mode",
|
| 51 |
+
action="store_const",
|
| 52 |
+
const="decode",
|
| 53 |
+
help="Decode the input from its ASCII A-label form.",
|
| 54 |
+
)
|
| 55 |
+
parser.add_argument(
|
| 56 |
+
"--strict",
|
| 57 |
+
action="store_true",
|
| 58 |
+
help="Disable the default UTS #46 mapping and apply IDNA 2008 rules verbatim.",
|
| 59 |
+
)
|
| 60 |
+
parser.add_argument(
|
| 61 |
+
"--version",
|
| 62 |
+
action="version",
|
| 63 |
+
version=f"idna {__version__}",
|
| 64 |
+
)
|
| 65 |
+
parser.add_argument(
|
| 66 |
+
"domain",
|
| 67 |
+
nargs="*",
|
| 68 |
+
help="One or more domain names to convert. Omit to read from stdin.",
|
| 69 |
+
)
|
| 70 |
+
return parser
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def _iter_stdin(stream: IO[str]) -> Iterable[str]:
|
| 74 |
+
"""Yield non-empty stripped lines from ``stream``, ignoring blanks."""
|
| 75 |
+
for line in stream:
|
| 76 |
+
stripped = line.strip()
|
| 77 |
+
if stripped:
|
| 78 |
+
yield stripped
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _convert_one(domain: str, mode: str, uts46: bool) -> bool:
|
| 82 |
+
"""Convert ``domain`` and write the result; return ``False`` on failure."""
|
| 83 |
+
try:
|
| 84 |
+
if mode == "decode":
|
| 85 |
+
print(decode(domain, uts46=uts46))
|
| 86 |
+
else:
|
| 87 |
+
print(encode(domain, uts46=uts46).decode("ascii"))
|
| 88 |
+
except IDNAError as err:
|
| 89 |
+
print(f"idna: {mode} failed for {domain!r}: {err}", file=sys.stderr)
|
| 90 |
+
return False
|
| 91 |
+
return True
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def main(argv: Optional[list[str]] = None) -> int:
|
| 95 |
+
"""Entry point for ``python -m idna``.
|
| 96 |
+
|
| 97 |
+
When more than one domain is supplied (via positional arguments or
|
| 98 |
+
piped stdin) and no mode flag is given, the first input determines
|
| 99 |
+
the direction and that mode is applied uniformly to the rest.
|
| 100 |
+
|
| 101 |
+
:param argv: Argument list excluding the program name. Defaults to
|
| 102 |
+
:data:`sys.argv` when ``None``.
|
| 103 |
+
:returns: ``0`` on success, ``1`` if any conversion fails.
|
| 104 |
+
"""
|
| 105 |
+
parser = _build_parser()
|
| 106 |
+
args = parser.parse_args(argv)
|
| 107 |
+
uts46 = not args.strict
|
| 108 |
+
|
| 109 |
+
if args.domain:
|
| 110 |
+
domains: Iterable[str] = args.domain
|
| 111 |
+
elif not sys.stdin.isatty():
|
| 112 |
+
domains = _iter_stdin(sys.stdin)
|
| 113 |
+
else:
|
| 114 |
+
parser.error("a domain argument is required when stdin is a terminal")
|
| 115 |
+
|
| 116 |
+
iterator = iter(domains)
|
| 117 |
+
first = next(iterator, None)
|
| 118 |
+
if first is None:
|
| 119 |
+
return 0
|
| 120 |
+
|
| 121 |
+
mode = args.mode or ("decode" if _looks_like_alabel(first) else "encode")
|
| 122 |
+
|
| 123 |
+
results = [_convert_one(domain, mode, uts46) for domain in chain([first], iterator)]
|
| 124 |
+
return 0 if all(results) else 1
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
if __name__ == "__main__":
|
| 128 |
+
sys.exit(main())
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/codec.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import codecs
|
| 2 |
+
from typing import Any, Optional
|
| 3 |
+
|
| 4 |
+
from .core import IDNAError, _unicode_dots_re, alabel, decode, encode, ulabel
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Codec(codecs.Codec):
|
| 8 |
+
"""Stateless IDNA 2008 codec.
|
| 9 |
+
|
| 10 |
+
Implements the :class:`codecs.Codec` protocol so that the whole-domain
|
| 11 |
+
encoder (:func:`idna.encode`) and decoder (:func:`idna.decode`) are
|
| 12 |
+
accessible through the standard codec machinery as ``"idna2008"``.
|
| 13 |
+
|
| 14 |
+
Only the ``"strict"`` error handler is supported; any other handler
|
| 15 |
+
raises :exc:`~idna.IDNAError`.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def encode(self, data: str, errors: str = "strict") -> tuple[bytes, int]: # ty: ignore[invalid-method-override]
|
| 19 |
+
if errors != "strict":
|
| 20 |
+
raise IDNAError(f'Unsupported error handling "{errors}"')
|
| 21 |
+
|
| 22 |
+
if not data:
|
| 23 |
+
return b"", 0
|
| 24 |
+
|
| 25 |
+
return encode(data), len(data)
|
| 26 |
+
|
| 27 |
+
def decode(self, data: bytes, errors: str = "strict") -> tuple[str, int]: # ty: ignore[invalid-method-override]
|
| 28 |
+
if errors != "strict":
|
| 29 |
+
raise IDNAError(f'Unsupported error handling "{errors}"')
|
| 30 |
+
|
| 31 |
+
if not data:
|
| 32 |
+
return "", 0
|
| 33 |
+
|
| 34 |
+
return decode(data), len(data)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class IncrementalEncoder(codecs.BufferedIncrementalEncoder):
|
| 38 |
+
"""Incremental IDNA 2008 encoder.
|
| 39 |
+
|
| 40 |
+
Buffers a partial trailing label across calls until either the next
|
| 41 |
+
label separator is seen or ``final=True``, so that streamed input is
|
| 42 |
+
encoded one whole label at a time. Any of the four Unicode label
|
| 43 |
+
separators (``U+002E``, ``U+3002``, ``U+FF0E``, ``U+FF61``) ends a
|
| 44 |
+
label; the result always uses ``U+002E`` as the separator.
|
| 45 |
+
|
| 46 |
+
Only the ``"strict"`` error handler is supported.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
def _buffer_encode(self, data: str, errors: str, final: bool) -> tuple[bytes, int]: # ty: ignore[invalid-method-override]
|
| 50 |
+
if errors != "strict":
|
| 51 |
+
raise IDNAError(f'Unsupported error handling "{errors}"')
|
| 52 |
+
|
| 53 |
+
if not data:
|
| 54 |
+
return b"", 0
|
| 55 |
+
|
| 56 |
+
labels = _unicode_dots_re.split(data)
|
| 57 |
+
trailing_dot = b""
|
| 58 |
+
if labels:
|
| 59 |
+
if not labels[-1]:
|
| 60 |
+
trailing_dot = b"."
|
| 61 |
+
del labels[-1]
|
| 62 |
+
elif not final:
|
| 63 |
+
# Keep potentially unfinished label until the next call
|
| 64 |
+
del labels[-1]
|
| 65 |
+
if labels:
|
| 66 |
+
trailing_dot = b"."
|
| 67 |
+
|
| 68 |
+
result = []
|
| 69 |
+
size = 0
|
| 70 |
+
for label in labels:
|
| 71 |
+
result.append(alabel(label))
|
| 72 |
+
if size:
|
| 73 |
+
size += 1
|
| 74 |
+
size += len(label)
|
| 75 |
+
|
| 76 |
+
# Join with U+002E
|
| 77 |
+
result_bytes = b".".join(result) + trailing_dot
|
| 78 |
+
size += len(trailing_dot)
|
| 79 |
+
return result_bytes, size
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class IncrementalDecoder(codecs.BufferedIncrementalDecoder):
|
| 83 |
+
"""Incremental IDNA 2008 decoder.
|
| 84 |
+
|
| 85 |
+
Buffers a partial trailing label across calls until either the next
|
| 86 |
+
label separator is seen or ``final=True``, so that streamed input is
|
| 87 |
+
decoded one whole label at a time.
|
| 88 |
+
|
| 89 |
+
Only the ``"strict"`` error handler is supported.
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
def _buffer_decode(self, data: Any, errors: str, final: bool) -> tuple[str, int]: # ty: ignore[invalid-method-override]
|
| 93 |
+
if errors != "strict":
|
| 94 |
+
raise IDNAError(f'Unsupported error handling "{errors}"')
|
| 95 |
+
|
| 96 |
+
if not data:
|
| 97 |
+
return ("", 0)
|
| 98 |
+
|
| 99 |
+
if not isinstance(data, str):
|
| 100 |
+
data = str(data, "ascii")
|
| 101 |
+
|
| 102 |
+
labels = _unicode_dots_re.split(data)
|
| 103 |
+
trailing_dot = ""
|
| 104 |
+
if labels:
|
| 105 |
+
if not labels[-1]:
|
| 106 |
+
trailing_dot = "."
|
| 107 |
+
del labels[-1]
|
| 108 |
+
elif not final:
|
| 109 |
+
# Keep potentially unfinished label until the next call
|
| 110 |
+
del labels[-1]
|
| 111 |
+
if labels:
|
| 112 |
+
trailing_dot = "."
|
| 113 |
+
|
| 114 |
+
result = []
|
| 115 |
+
size = 0
|
| 116 |
+
for label in labels:
|
| 117 |
+
result.append(ulabel(label))
|
| 118 |
+
if size:
|
| 119 |
+
size += 1
|
| 120 |
+
size += len(label)
|
| 121 |
+
|
| 122 |
+
result_str = ".".join(result) + trailing_dot
|
| 123 |
+
size += len(trailing_dot)
|
| 124 |
+
return (result_str, size)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class StreamWriter(Codec, codecs.StreamWriter):
|
| 128 |
+
pass
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
class StreamReader(Codec, codecs.StreamReader):
|
| 132 |
+
pass
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def search_function(name: str) -> Optional[codecs.CodecInfo]:
|
| 136 |
+
"""Codec search function registered with :mod:`codecs`.
|
| 137 |
+
|
| 138 |
+
Returns a :class:`codecs.CodecInfo` for the ``"idna2008"`` codec name
|
| 139 |
+
so that ``str.encode("idna2008")`` and ``bytes.decode("idna2008")``
|
| 140 |
+
invoke the IDNA 2008 codec defined in this module.
|
| 141 |
+
|
| 142 |
+
:param name: The codec name being looked up.
|
| 143 |
+
:returns: A :class:`codecs.CodecInfo` instance if ``name`` is
|
| 144 |
+
``"idna2008"``, otherwise ``None``.
|
| 145 |
+
"""
|
| 146 |
+
if name != "idna2008":
|
| 147 |
+
return None
|
| 148 |
+
return codecs.CodecInfo(
|
| 149 |
+
name=name,
|
| 150 |
+
encode=Codec().encode,
|
| 151 |
+
decode=Codec().decode, # type: ignore
|
| 152 |
+
incrementalencoder=IncrementalEncoder,
|
| 153 |
+
incrementaldecoder=IncrementalDecoder,
|
| 154 |
+
streamwriter=StreamWriter,
|
| 155 |
+
streamreader=StreamReader,
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
codecs.register(search_function)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/compat.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, Union
|
| 2 |
+
|
| 3 |
+
from .core import decode, encode
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def ToASCII(label: str) -> bytes:
|
| 7 |
+
"""Compatibility shim for :rfc:`3490` ``ToASCII``.
|
| 8 |
+
|
| 9 |
+
Delegates to :func:`idna.encode` (IDNA 2008). Provided to ease porting
|
| 10 |
+
of code written against the legacy :mod:`encodings.idna` API; new code
|
| 11 |
+
should call :func:`idna.encode` directly.
|
| 12 |
+
|
| 13 |
+
:param label: The label or domain to encode.
|
| 14 |
+
:returns: The encoded form as ASCII :class:`bytes`.
|
| 15 |
+
"""
|
| 16 |
+
return encode(label)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def ToUnicode(label: Union[bytes, bytearray]) -> str:
|
| 20 |
+
"""Compatibility shim for :rfc:`3490` ``ToUnicode``.
|
| 21 |
+
|
| 22 |
+
Delegates to :func:`idna.decode` (IDNA 2008). Provided to ease porting
|
| 23 |
+
of code written against the legacy :mod:`encodings.idna` API; new code
|
| 24 |
+
should call :func:`idna.decode` directly.
|
| 25 |
+
|
| 26 |
+
:param label: The label or domain to decode.
|
| 27 |
+
:returns: The decoded Unicode form.
|
| 28 |
+
"""
|
| 29 |
+
return decode(label)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def nameprep(s: Any) -> None:
|
| 33 |
+
"""Stub for :rfc:`3491` Nameprep, which is not used by IDNA 2008.
|
| 34 |
+
|
| 35 |
+
IDNA 2008 (:rfc:`5891`) replaces Nameprep with the per-codepoint
|
| 36 |
+
validity classes from :rfc:`5892`; this function exists only to
|
| 37 |
+
return a clear error if legacy code attempts to call it.
|
| 38 |
+
|
| 39 |
+
:raises NotImplementedError: Always.
|
| 40 |
+
"""
|
| 41 |
+
raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol")
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/core.py
ADDED
|
@@ -0,0 +1,648 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import bisect
|
| 2 |
+
import re
|
| 3 |
+
import unicodedata
|
| 4 |
+
import warnings
|
| 5 |
+
from typing import Optional, Union
|
| 6 |
+
|
| 7 |
+
from . import idnadata
|
| 8 |
+
from .intranges import intranges_contain
|
| 9 |
+
|
| 10 |
+
_virama_combining_class = 9
|
| 11 |
+
_alabel_prefix = b"xn--"
|
| 12 |
+
_max_input_length = 1024
|
| 13 |
+
_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# Bidi category sets from RFC 5893, hoisted out of the per-codepoint loop
|
| 17 |
+
_bidi_rtl_first = frozenset({"R", "AL"})
|
| 18 |
+
_bidi_rtl_categories = frozenset({"R", "AL", "AN"})
|
| 19 |
+
_bidi_rtl_allowed = frozenset({"R", "AL", "AN", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"})
|
| 20 |
+
_bidi_rtl_valid_ending = frozenset({"R", "AL", "EN", "AN"})
|
| 21 |
+
_bidi_rtl_numeric = frozenset({"AN", "EN"})
|
| 22 |
+
_bidi_ltr_allowed = frozenset({"L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"})
|
| 23 |
+
_bidi_ltr_valid_ending = frozenset({"L", "EN"})
|
| 24 |
+
_bidi_joiner_l_or_d = frozenset({"L", "D"})
|
| 25 |
+
_bidi_joiner_r_or_d = frozenset({"R", "D"})
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def _joining_type(cp: int) -> Optional[str]:
|
| 29 |
+
for jt, ranges in idnadata.joining_types.items():
|
| 30 |
+
if intranges_contain(cp, ranges):
|
| 31 |
+
return jt
|
| 32 |
+
return None
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class IDNAError(UnicodeError):
|
| 36 |
+
"""Base exception for all IDNA-encoding related problems"""
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class IDNABidiError(IDNAError):
|
| 40 |
+
"""Exception when bidirectional requirements are not satisfied"""
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class InvalidCodepoint(IDNAError):
|
| 44 |
+
"""Exception when a disallowed or unallocated codepoint is used"""
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class InvalidCodepointContext(IDNAError):
|
| 48 |
+
"""Exception when the codepoint is not valid in the context it is used"""
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _combining_class(cp: int) -> int:
|
| 52 |
+
v = unicodedata.combining(chr(cp))
|
| 53 |
+
if v == 0 and not unicodedata.name(chr(cp)):
|
| 54 |
+
raise ValueError("Unknown character in unicodedata")
|
| 55 |
+
return v
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def _is_script(cp: str, script: str) -> bool:
|
| 59 |
+
return intranges_contain(ord(cp), idnadata.scripts[script])
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def _punycode(s: str) -> bytes:
|
| 63 |
+
return s.encode("punycode")
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def _unot(s: int) -> str:
|
| 67 |
+
return f"U+{s:04X}"
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def valid_label_length(label: Union[bytes, str]) -> bool:
|
| 71 |
+
"""Check that a label does not exceed the maximum permitted length.
|
| 72 |
+
|
| 73 |
+
Per :rfc:`1035` (and :rfc:`5891` §4.2.4) a DNS label must not exceed
|
| 74 |
+
63 octets. The argument may be either a :class:`str` (a U-label, where
|
| 75 |
+
length is measured in characters) or :class:`bytes` (an A-label, where
|
| 76 |
+
length is measured in octets).
|
| 77 |
+
|
| 78 |
+
:param label: The label to check.
|
| 79 |
+
:returns: ``True`` if the label is within the length limit, otherwise
|
| 80 |
+
``False``.
|
| 81 |
+
"""
|
| 82 |
+
return len(label) <= 63
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def valid_string_length(domain: Union[bytes, str], trailing_dot: bool) -> bool:
|
| 86 |
+
"""Check that a full domain name does not exceed the maximum length.
|
| 87 |
+
|
| 88 |
+
Per :rfc:`1035`, a domain name is limited to 253 octets when no trailing
|
| 89 |
+
dot is present, or 254 octets when one is included.
|
| 90 |
+
|
| 91 |
+
:param domain: The full (possibly multi-label) domain name.
|
| 92 |
+
:param trailing_dot: ``True`` if ``domain`` includes a trailing ``.``.
|
| 93 |
+
:returns: ``True`` if the domain is within the length limit, otherwise
|
| 94 |
+
``False``.
|
| 95 |
+
"""
|
| 96 |
+
return len(domain) <= (254 if trailing_dot else 253)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def check_bidi(label: str, check_ltr: bool = False) -> bool:
|
| 100 |
+
"""Validate the Bidi Rule from :rfc:`5893` for a single label.
|
| 101 |
+
|
| 102 |
+
The Bidi Rule constrains how bidirectional characters (Hebrew, Arabic,
|
| 103 |
+
etc.) may appear within a label. By default the check is only applied
|
| 104 |
+
when the label contains at least one right-to-left character (Unicode
|
| 105 |
+
bidirectional categories ``R``, ``AL``, or ``AN``); set ``check_ltr``
|
| 106 |
+
to ``True`` to apply it to LTR-only labels as well.
|
| 107 |
+
|
| 108 |
+
:param label: The label to validate, as a Unicode string.
|
| 109 |
+
:param check_ltr: If ``True``, apply the rules even when the label
|
| 110 |
+
contains no RTL characters.
|
| 111 |
+
:returns: ``True`` if the label satisfies the Bidi Rule.
|
| 112 |
+
:raises IDNABidiError: If any of Bidi Rule conditions 1-6 are violated,
|
| 113 |
+
or if the directional category of a codepoint cannot be determined.
|
| 114 |
+
"""
|
| 115 |
+
if len(label) > _max_input_length:
|
| 116 |
+
raise IDNAError("Label too long")
|
| 117 |
+
# Bidi rules should only be applied if string contains RTL characters
|
| 118 |
+
bidi_label = False
|
| 119 |
+
for idx, cp in enumerate(label, 1):
|
| 120 |
+
direction = unicodedata.bidirectional(cp)
|
| 121 |
+
if direction == "":
|
| 122 |
+
# String likely comes from a newer version of Unicode
|
| 123 |
+
raise IDNABidiError(f"Unknown directionality in label {label!r} at position {idx}")
|
| 124 |
+
if direction in _bidi_rtl_categories:
|
| 125 |
+
bidi_label = True
|
| 126 |
+
if not bidi_label and not check_ltr:
|
| 127 |
+
return True
|
| 128 |
+
|
| 129 |
+
# Bidi rule 1
|
| 130 |
+
direction = unicodedata.bidirectional(label[0])
|
| 131 |
+
if direction in _bidi_rtl_first:
|
| 132 |
+
rtl = True
|
| 133 |
+
elif direction == "L":
|
| 134 |
+
rtl = False
|
| 135 |
+
else:
|
| 136 |
+
raise IDNABidiError(f"First codepoint in label {label!r} must be directionality L, R or AL")
|
| 137 |
+
|
| 138 |
+
valid_ending = False
|
| 139 |
+
number_type: Optional[str] = None
|
| 140 |
+
for idx, cp in enumerate(label, 1):
|
| 141 |
+
direction = unicodedata.bidirectional(cp)
|
| 142 |
+
|
| 143 |
+
if rtl:
|
| 144 |
+
# Bidi rule 2
|
| 145 |
+
if direction not in _bidi_rtl_allowed:
|
| 146 |
+
raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a right-to-left label")
|
| 147 |
+
# Bidi rule 3
|
| 148 |
+
if direction in _bidi_rtl_valid_ending:
|
| 149 |
+
valid_ending = True
|
| 150 |
+
elif direction != "NSM":
|
| 151 |
+
valid_ending = False
|
| 152 |
+
# Bidi rule 4
|
| 153 |
+
if direction in _bidi_rtl_numeric:
|
| 154 |
+
if not number_type:
|
| 155 |
+
number_type = direction
|
| 156 |
+
elif number_type != direction:
|
| 157 |
+
raise IDNABidiError("Can not mix numeral types in a right-to-left label")
|
| 158 |
+
else:
|
| 159 |
+
# Bidi rule 5
|
| 160 |
+
if direction not in _bidi_ltr_allowed:
|
| 161 |
+
raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a left-to-right label")
|
| 162 |
+
# Bidi rule 6
|
| 163 |
+
if direction in _bidi_ltr_valid_ending:
|
| 164 |
+
valid_ending = True
|
| 165 |
+
elif direction != "NSM":
|
| 166 |
+
valid_ending = False
|
| 167 |
+
|
| 168 |
+
if not valid_ending:
|
| 169 |
+
raise IDNABidiError("Label ends with illegal codepoint directionality")
|
| 170 |
+
|
| 171 |
+
return True
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def check_initial_combiner(label: str) -> bool:
|
| 175 |
+
"""Reject labels that begin with a combining mark.
|
| 176 |
+
|
| 177 |
+
Per :rfc:`5891` §4.2.3.2 a label must not start with a character of
|
| 178 |
+
Unicode general category ``M`` (Mark).
|
| 179 |
+
|
| 180 |
+
:param label: The label to check.
|
| 181 |
+
:returns: ``True`` if the first character is not a combining mark.
|
| 182 |
+
:raises IDNAError: If the label begins with a combining character.
|
| 183 |
+
"""
|
| 184 |
+
if unicodedata.category(label[0])[0] == "M":
|
| 185 |
+
raise IDNAError("Label begins with an illegal combining character")
|
| 186 |
+
return True
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def check_hyphen_ok(label: str) -> bool:
|
| 190 |
+
"""Validate the hyphen restrictions for a label.
|
| 191 |
+
|
| 192 |
+
Per :rfc:`5891` §4.2.3.1 a label must not start or end with a hyphen
|
| 193 |
+
(``U+002D``), and must not have hyphens in both the third and fourth
|
| 194 |
+
positions (the prefix reserved for A-labels).
|
| 195 |
+
|
| 196 |
+
:param label: The label to check.
|
| 197 |
+
:returns: ``True`` if the hyphen restrictions are satisfied.
|
| 198 |
+
:raises IDNAError: If any of the hyphen restrictions are violated.
|
| 199 |
+
"""
|
| 200 |
+
if label[2:4] == "--":
|
| 201 |
+
raise IDNAError("Label has disallowed hyphens in 3rd and 4th position")
|
| 202 |
+
if label[0] == "-" or label[-1] == "-":
|
| 203 |
+
raise IDNAError("Label must not start or end with a hyphen")
|
| 204 |
+
return True
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def check_nfc(label: str) -> None:
|
| 208 |
+
"""Require that a label is in Unicode Normalization Form C.
|
| 209 |
+
|
| 210 |
+
:param label: The label to check.
|
| 211 |
+
:raises IDNAError: If ``label`` differs from its NFC normalisation.
|
| 212 |
+
"""
|
| 213 |
+
if len(label) > _max_input_length:
|
| 214 |
+
raise IDNAError("Label too long")
|
| 215 |
+
if unicodedata.normalize("NFC", label) != label:
|
| 216 |
+
raise IDNAError("Label must be in Normalization Form C")
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def valid_contextj(label: str, pos: int) -> bool:
|
| 220 |
+
"""Validate the CONTEXTJ rules from :rfc:`5892` Appendix A.
|
| 221 |
+
|
| 222 |
+
These rules govern the contextual use of the joiner codepoints
|
| 223 |
+
``U+200C`` (ZERO WIDTH NON-JOINER, Appendix A.1) and ``U+200D``
|
| 224 |
+
(ZERO WIDTH JOINER, Appendix A.2) within a label.
|
| 225 |
+
|
| 226 |
+
:param label: The label containing the codepoint.
|
| 227 |
+
:param pos: Index of the joiner codepoint within ``label``.
|
| 228 |
+
:returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTJ
|
| 229 |
+
rule, ``False`` otherwise (including when the codepoint at
|
| 230 |
+
``pos`` is not a recognised joiner).
|
| 231 |
+
:raises ValueError: If an adjacent codepoint has no Unicode name when
|
| 232 |
+
determining its combining class.
|
| 233 |
+
:raises IDNAError: If ``label`` exceeds the defensive input length limit.
|
| 234 |
+
"""
|
| 235 |
+
if len(label) > _max_input_length:
|
| 236 |
+
raise IDNAError("Label too long")
|
| 237 |
+
cp_value = ord(label[pos])
|
| 238 |
+
|
| 239 |
+
if cp_value == 0x200C:
|
| 240 |
+
if pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class:
|
| 241 |
+
return True
|
| 242 |
+
|
| 243 |
+
ok = False
|
| 244 |
+
for i in range(pos - 1, -1, -1):
|
| 245 |
+
joining_type = _joining_type(ord(label[i]))
|
| 246 |
+
if joining_type == "T":
|
| 247 |
+
continue
|
| 248 |
+
if joining_type in _bidi_joiner_l_or_d:
|
| 249 |
+
ok = True
|
| 250 |
+
break
|
| 251 |
+
break
|
| 252 |
+
|
| 253 |
+
if not ok:
|
| 254 |
+
return False
|
| 255 |
+
|
| 256 |
+
ok = False
|
| 257 |
+
for i in range(pos + 1, len(label)):
|
| 258 |
+
joining_type = _joining_type(ord(label[i]))
|
| 259 |
+
if joining_type == "T":
|
| 260 |
+
continue
|
| 261 |
+
if joining_type in _bidi_joiner_r_or_d:
|
| 262 |
+
ok = True
|
| 263 |
+
break
|
| 264 |
+
break
|
| 265 |
+
return ok
|
| 266 |
+
|
| 267 |
+
if cp_value == 0x200D:
|
| 268 |
+
return pos > 0 and _combining_class(ord(label[pos - 1])) == _virama_combining_class
|
| 269 |
+
|
| 270 |
+
return False
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
def valid_contexto(label: str, pos: int, exception: bool = False) -> bool:
|
| 274 |
+
"""Validate the CONTEXTO rules from :rfc:`5892` Appendix A.
|
| 275 |
+
|
| 276 |
+
Covers the contextual rules for codepoints such as MIDDLE DOT
|
| 277 |
+
(``U+00B7``), Greek lower numeral sign, Hebrew punctuation, Katakana
|
| 278 |
+
middle dot, and the Arabic-Indic / Extended Arabic-Indic digit ranges.
|
| 279 |
+
|
| 280 |
+
:param label: The label containing the codepoint.
|
| 281 |
+
:param pos: Index of the codepoint within ``label``.
|
| 282 |
+
:param exception: Reserved for forward compatibility; currently unused.
|
| 283 |
+
:returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTO
|
| 284 |
+
rule, ``False`` otherwise (including when the codepoint is not a
|
| 285 |
+
recognised CONTEXTO codepoint).
|
| 286 |
+
:raises IDNAError: If ``label`` exceeds the defensive input length limit.
|
| 287 |
+
"""
|
| 288 |
+
if len(label) > _max_input_length:
|
| 289 |
+
raise IDNAError("Label too long")
|
| 290 |
+
cp_value = ord(label[pos])
|
| 291 |
+
|
| 292 |
+
if cp_value == 0x00B7:
|
| 293 |
+
return 0 < pos < len(label) - 1 and ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C
|
| 294 |
+
|
| 295 |
+
if cp_value == 0x0375:
|
| 296 |
+
if pos < len(label) - 1 and len(label) > 1:
|
| 297 |
+
return _is_script(label[pos + 1], "Greek")
|
| 298 |
+
return False
|
| 299 |
+
|
| 300 |
+
if cp_value in {0x05F3, 0x05F4}:
|
| 301 |
+
if pos > 0:
|
| 302 |
+
return _is_script(label[pos - 1], "Hebrew")
|
| 303 |
+
return False
|
| 304 |
+
|
| 305 |
+
if cp_value == 0x30FB:
|
| 306 |
+
for cp in label:
|
| 307 |
+
if cp == "\u30fb":
|
| 308 |
+
continue
|
| 309 |
+
if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"):
|
| 310 |
+
return True
|
| 311 |
+
return False
|
| 312 |
+
|
| 313 |
+
if 0x660 <= cp_value <= 0x669:
|
| 314 |
+
return not any(0x6F0 <= ord(cp) <= 0x06F9 for cp in label)
|
| 315 |
+
|
| 316 |
+
if 0x6F0 <= cp_value <= 0x6F9:
|
| 317 |
+
return not any(0x660 <= ord(cp) <= 0x0669 for cp in label)
|
| 318 |
+
|
| 319 |
+
return False
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def check_label(label: Union[str, bytes, bytearray]) -> None:
|
| 323 |
+
"""Run the full set of IDNA 2008 validity checks on a single label.
|
| 324 |
+
|
| 325 |
+
Applies, in order: NFC normalisation (:func:`check_nfc`), hyphen
|
| 326 |
+
restrictions (:func:`check_hyphen_ok`), the no-leading-combiner rule
|
| 327 |
+
(:func:`check_initial_combiner`), per-codepoint validity (PVALID,
|
| 328 |
+
CONTEXTJ, CONTEXTO classes from :rfc:`5892`), and the Bidi Rule
|
| 329 |
+
(:func:`check_bidi`).
|
| 330 |
+
|
| 331 |
+
:param label: The label to validate. ``bytes`` or ``bytearray`` input
|
| 332 |
+
is decoded as UTF-8 first.
|
| 333 |
+
:raises IDNAError: If the label is empty or fails a structural rule.
|
| 334 |
+
:raises InvalidCodepoint: If the label contains a DISALLOWED or
|
| 335 |
+
UNASSIGNED codepoint.
|
| 336 |
+
:raises InvalidCodepointContext: If a CONTEXTJ or CONTEXTO codepoint
|
| 337 |
+
is not valid in its context.
|
| 338 |
+
:raises IDNABidiError: If the Bidi Rule is violated.
|
| 339 |
+
"""
|
| 340 |
+
if len(label) > _max_input_length:
|
| 341 |
+
raise IDNAError("Label too long")
|
| 342 |
+
if isinstance(label, (bytes, bytearray)):
|
| 343 |
+
label = label.decode("utf-8")
|
| 344 |
+
if len(label) == 0:
|
| 345 |
+
raise IDNAError("Empty Label")
|
| 346 |
+
|
| 347 |
+
# Reject on domain length rather than label length so support some UTS 46
|
| 348 |
+
# use cases, still reducing processing of label contextual rules
|
| 349 |
+
if not valid_string_length(label, trailing_dot=True):
|
| 350 |
+
raise IDNAError("Label too long")
|
| 351 |
+
|
| 352 |
+
check_nfc(label)
|
| 353 |
+
check_hyphen_ok(label)
|
| 354 |
+
check_initial_combiner(label)
|
| 355 |
+
|
| 356 |
+
for pos, cp in enumerate(label):
|
| 357 |
+
cp_value = ord(cp)
|
| 358 |
+
if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
|
| 359 |
+
continue
|
| 360 |
+
if intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
|
| 361 |
+
try:
|
| 362 |
+
if not valid_contextj(label, pos):
|
| 363 |
+
raise InvalidCodepointContext(f"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
|
| 364 |
+
except ValueError as err:
|
| 365 |
+
raise IDNAError(
|
| 366 |
+
f"Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}"
|
| 367 |
+
) from err
|
| 368 |
+
elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]):
|
| 369 |
+
if not valid_contexto(label, pos):
|
| 370 |
+
raise InvalidCodepointContext(f"Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
|
| 371 |
+
else:
|
| 372 |
+
raise InvalidCodepoint(f"Codepoint {_unot(cp_value)} at position {pos + 1} of {label!r} not allowed")
|
| 373 |
+
|
| 374 |
+
check_bidi(label)
|
| 375 |
+
|
| 376 |
+
|
| 377 |
+
def alabel(label: str) -> bytes:
|
| 378 |
+
"""Convert a single U-label into its A-label form.
|
| 379 |
+
|
| 380 |
+
The result is the ASCII-Compatible Encoding (ACE) form per :rfc:`5891`
|
| 381 |
+
§4: the label is validated, Punycode-encoded, and prefixed with
|
| 382 |
+
``xn--``. Pure ASCII labels that are already valid IDNA labels are
|
| 383 |
+
returned unchanged (as :class:`bytes`).
|
| 384 |
+
|
| 385 |
+
:param label: The label to convert, as a Unicode string.
|
| 386 |
+
:returns: The A-label as ASCII-encoded :class:`bytes`.
|
| 387 |
+
:raises IDNAError: If the label is invalid or the resulting A-label
|
| 388 |
+
exceeds 63 octets.
|
| 389 |
+
"""
|
| 390 |
+
if len(label) > _max_input_length:
|
| 391 |
+
raise IDNAError("Label too long")
|
| 392 |
+
try:
|
| 393 |
+
label_bytes = label.encode("ascii")
|
| 394 |
+
except UnicodeEncodeError:
|
| 395 |
+
pass
|
| 396 |
+
else:
|
| 397 |
+
ulabel(label_bytes)
|
| 398 |
+
if not valid_label_length(label_bytes):
|
| 399 |
+
raise IDNAError("Label too long")
|
| 400 |
+
return label_bytes
|
| 401 |
+
|
| 402 |
+
check_label(label)
|
| 403 |
+
label_bytes = _alabel_prefix + _punycode(label)
|
| 404 |
+
|
| 405 |
+
if not valid_label_length(label_bytes):
|
| 406 |
+
raise IDNAError("Label too long")
|
| 407 |
+
|
| 408 |
+
return label_bytes
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def ulabel(label: Union[str, bytes, bytearray]) -> str:
|
| 412 |
+
"""Convert a single A-label into its U-label form.
|
| 413 |
+
|
| 414 |
+
Performs the inverse of :func:`alabel`: an ``xn--``-prefixed label is
|
| 415 |
+
Punycode-decoded and validated. Labels that are already Unicode (or
|
| 416 |
+
plain ASCII without the ACE prefix) are validated and returned as a
|
| 417 |
+
Unicode string.
|
| 418 |
+
|
| 419 |
+
:param label: The label to convert. ``bytes`` or ``bytearray`` input
|
| 420 |
+
is treated as ASCII.
|
| 421 |
+
:returns: The U-label as a Unicode string.
|
| 422 |
+
:raises IDNAError: If the label is malformed or fails validation.
|
| 423 |
+
"""
|
| 424 |
+
if len(label) > _max_input_length:
|
| 425 |
+
raise IDNAError("Label too long")
|
| 426 |
+
if not isinstance(label, (bytes, bytearray)):
|
| 427 |
+
try:
|
| 428 |
+
label_bytes = label.encode("ascii")
|
| 429 |
+
except UnicodeEncodeError:
|
| 430 |
+
check_label(label)
|
| 431 |
+
return label
|
| 432 |
+
else:
|
| 433 |
+
label_bytes = bytes(label)
|
| 434 |
+
|
| 435 |
+
label_bytes = label_bytes.lower()
|
| 436 |
+
if label_bytes.startswith(_alabel_prefix):
|
| 437 |
+
label_bytes = label_bytes[len(_alabel_prefix) :]
|
| 438 |
+
if not label_bytes:
|
| 439 |
+
raise IDNAError("Malformed A-label, no Punycode eligible content found")
|
| 440 |
+
if label_bytes.endswith(b"-"):
|
| 441 |
+
raise IDNAError("A-label must not end with a hyphen")
|
| 442 |
+
else:
|
| 443 |
+
check_label(label_bytes)
|
| 444 |
+
return label_bytes.decode("ascii")
|
| 445 |
+
|
| 446 |
+
try:
|
| 447 |
+
label = label_bytes.decode("punycode")
|
| 448 |
+
except UnicodeError as err:
|
| 449 |
+
raise IDNAError("Invalid A-label") from err
|
| 450 |
+
check_label(label)
|
| 451 |
+
return label
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str:
|
| 455 |
+
"""Apply the UTS #46 character mapping to a domain string.
|
| 456 |
+
|
| 457 |
+
Implements the mapping table from `UTS #46 §4
|
| 458 |
+
<https://www.unicode.org/reports/tr46/>`_: each character is kept,
|
| 459 |
+
replaced, or rejected based on its status (``V``, ``M``, ``D``, ``3``,
|
| 460 |
+
``I``). The result is returned in Normalisation Form C.
|
| 461 |
+
|
| 462 |
+
:param domain: The full domain name to remap.
|
| 463 |
+
:param std3_rules: If ``True``, apply the stricter STD3 ASCII rules
|
| 464 |
+
(status ``3`` codepoints raise instead of being kept or mapped).
|
| 465 |
+
:param transitional: If ``True``, use transitional processing (status
|
| 466 |
+
``D`` codepoints are mapped instead of kept). Transitional
|
| 467 |
+
processing has been removed from UTS #46 and this option is
|
| 468 |
+
retained only for backwards compatibility.
|
| 469 |
+
:returns: The remapped domain, in Normalisation Form C.
|
| 470 |
+
:raises InvalidCodepoint: If the domain contains a disallowed
|
| 471 |
+
codepoint under the chosen rules.
|
| 472 |
+
:raises IDNAError: If ``domain`` exceeds the defensive input length limit.
|
| 473 |
+
"""
|
| 474 |
+
if len(domain) > _max_input_length:
|
| 475 |
+
raise IDNAError("Domain too long")
|
| 476 |
+
from .uts46data import uts46_replacements, uts46_starts, uts46_statuses
|
| 477 |
+
|
| 478 |
+
output = ""
|
| 479 |
+
|
| 480 |
+
for pos, char in enumerate(domain):
|
| 481 |
+
code_point = ord(char)
|
| 482 |
+
i = code_point if code_point < 256 else bisect.bisect_right(uts46_starts, code_point) - 1
|
| 483 |
+
status = chr(uts46_statuses[i])
|
| 484 |
+
replacement: Optional[str] = uts46_replacements[i]
|
| 485 |
+
|
| 486 |
+
# UTS #46 §4: V is always valid, D is deviation (kept unless transitional),
|
| 487 |
+
# 3 is disallowed-STD3 (kept unmapped if std3_rules is off and no mapping).
|
| 488 |
+
keep_as_is = (
|
| 489 |
+
status == "V" or (status == "D" and not transitional) or (status == "3" and not std3_rules and replacement is None)
|
| 490 |
+
)
|
| 491 |
+
# M is mapped, 3-with-replacement and transitional D fall through to the
|
| 492 |
+
# same replacement output path.
|
| 493 |
+
use_replacement = replacement is not None and (
|
| 494 |
+
status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
|
| 495 |
+
)
|
| 496 |
+
|
| 497 |
+
if keep_as_is:
|
| 498 |
+
output += char
|
| 499 |
+
elif use_replacement:
|
| 500 |
+
assert replacement is not None # narrowed by use_replacement
|
| 501 |
+
output += replacement
|
| 502 |
+
elif status == "I":
|
| 503 |
+
continue
|
| 504 |
+
else:
|
| 505 |
+
raise InvalidCodepoint(f"Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r}")
|
| 506 |
+
|
| 507 |
+
return unicodedata.normalize("NFC", output)
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
def encode(
|
| 511 |
+
s: Union[str, bytes, bytearray],
|
| 512 |
+
strict: bool = False,
|
| 513 |
+
uts46: bool = False,
|
| 514 |
+
std3_rules: bool = False,
|
| 515 |
+
transitional: bool = False,
|
| 516 |
+
) -> bytes:
|
| 517 |
+
"""Encode a Unicode domain name into its ASCII (A-label) form.
|
| 518 |
+
|
| 519 |
+
Splits the input on label separators (only ``U+002E`` if ``strict`` is
|
| 520 |
+
set; otherwise also IDEOGRAPHIC FULL STOP ``U+3002``, FULLWIDTH FULL
|
| 521 |
+
STOP ``U+FF0E``, and HALFWIDTH IDEOGRAPHIC FULL STOP ``U+FF61``),
|
| 522 |
+
encodes each label with :func:`alabel`, and rejoins them with ``.``.
|
| 523 |
+
Optionally pre-processes the input through :func:`uts46_remap`.
|
| 524 |
+
|
| 525 |
+
:param s: The domain name to encode.
|
| 526 |
+
:param strict: If ``True``, only ``U+002E`` is recognised as a label
|
| 527 |
+
separator.
|
| 528 |
+
:param uts46: If ``True``, apply UTS #46 mapping before encoding.
|
| 529 |
+
:param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is
|
| 530 |
+
``True``.
|
| 531 |
+
:param transitional: Forwarded to :func:`uts46_remap` when ``uts46``
|
| 532 |
+
is ``True``. Deprecated: emits a :class:`DeprecationWarning` and
|
| 533 |
+
will be removed in a future version.
|
| 534 |
+
:returns: The encoded domain as ASCII :class:`bytes`.
|
| 535 |
+
:raises IDNAError: If the domain is empty, contains an invalid label,
|
| 536 |
+
or exceeds the maximum domain length.
|
| 537 |
+
"""
|
| 538 |
+
if transitional:
|
| 539 |
+
warnings.warn(
|
| 540 |
+
"Transitional processing has been removed from UTS #46. "
|
| 541 |
+
"The transitional argument will be removed in a future version.",
|
| 542 |
+
DeprecationWarning,
|
| 543 |
+
stacklevel=2,
|
| 544 |
+
)
|
| 545 |
+
if not isinstance(s, str):
|
| 546 |
+
try:
|
| 547 |
+
s = str(s, "ascii")
|
| 548 |
+
except (UnicodeDecodeError, TypeError) as err:
|
| 549 |
+
raise IDNAError("should pass a unicode string to the function rather than a byte string.") from err
|
| 550 |
+
if len(s) > _max_input_length:
|
| 551 |
+
raise IDNAError("Domain too long")
|
| 552 |
+
if uts46:
|
| 553 |
+
s = uts46_remap(s, std3_rules, transitional)
|
| 554 |
+
|
| 555 |
+
# Reject inputs that exceed the maximum DNS domain length up-front
|
| 556 |
+
# to avoid expensive computation on long inputs.
|
| 557 |
+
if not valid_string_length(s, trailing_dot=True):
|
| 558 |
+
raise IDNAError("Domain too long")
|
| 559 |
+
|
| 560 |
+
trailing_dot = False
|
| 561 |
+
result = []
|
| 562 |
+
labels = s.split(".") if strict else _unicode_dots_re.split(s)
|
| 563 |
+
if not labels or labels == [""]:
|
| 564 |
+
raise IDNAError("Empty domain")
|
| 565 |
+
if labels[-1] == "":
|
| 566 |
+
del labels[-1]
|
| 567 |
+
trailing_dot = True
|
| 568 |
+
for label in labels:
|
| 569 |
+
s = alabel(label)
|
| 570 |
+
if s:
|
| 571 |
+
result.append(s)
|
| 572 |
+
else:
|
| 573 |
+
raise IDNAError("Empty label")
|
| 574 |
+
if trailing_dot:
|
| 575 |
+
result.append(b"")
|
| 576 |
+
s = b".".join(result)
|
| 577 |
+
if not valid_string_length(s, trailing_dot):
|
| 578 |
+
raise IDNAError("Domain too long")
|
| 579 |
+
return s
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
def decode(
|
| 583 |
+
s: Union[str, bytes, bytearray],
|
| 584 |
+
strict: bool = False,
|
| 585 |
+
uts46: bool = False,
|
| 586 |
+
std3_rules: bool = False,
|
| 587 |
+
display: bool = False,
|
| 588 |
+
) -> str:
|
| 589 |
+
"""Decode an A-label-encoded domain name back to Unicode.
|
| 590 |
+
|
| 591 |
+
Splits the input on label separators (see :func:`encode` for the
|
| 592 |
+
rules), decodes each label with :func:`ulabel`, and rejoins them
|
| 593 |
+
with ``.``. Optionally pre-processes the input through
|
| 594 |
+
:func:`uts46_remap`.
|
| 595 |
+
|
| 596 |
+
:param s: The domain name to decode.
|
| 597 |
+
:param strict: If ``True``, only ``U+002E`` is recognised as a label
|
| 598 |
+
separator.
|
| 599 |
+
:param uts46: If ``True``, apply UTS #46 mapping before decoding.
|
| 600 |
+
:param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is
|
| 601 |
+
``True``.
|
| 602 |
+
:param display: If ``True``, any ``xn--`` label that fails IDNA
|
| 603 |
+
validation is passed through unchanged (lowercased) rather than
|
| 604 |
+
aborting the whole call. Intended for "decode for display"
|
| 605 |
+
consumers (e.g. URL libraries, HTTP clients) that want to show
|
| 606 |
+
the user the label as it appears on the wire when it cannot be
|
| 607 |
+
rendered as Unicode. Matches the per-label recovery prescribed
|
| 608 |
+
by UTS #46 §4 and the WHATWG URL "domain to Unicode" algorithm.
|
| 609 |
+
:returns: The decoded domain as a Unicode string.
|
| 610 |
+
:raises IDNAError: If the input is not valid ASCII, contains an
|
| 611 |
+
invalid label, or is empty.
|
| 612 |
+
"""
|
| 613 |
+
if not isinstance(s, str):
|
| 614 |
+
try:
|
| 615 |
+
s = str(s, "ascii")
|
| 616 |
+
except (UnicodeDecodeError, TypeError) as err:
|
| 617 |
+
raise IDNAError("Invalid ASCII in A-label") from err
|
| 618 |
+
if len(s) > _max_input_length:
|
| 619 |
+
raise IDNAError("Domain too long")
|
| 620 |
+
if uts46:
|
| 621 |
+
s = uts46_remap(s, std3_rules, False)
|
| 622 |
+
# Reject inputs that exceed the maximum DNS domain length up-front
|
| 623 |
+
# to avoid expensive computation on long inputs.
|
| 624 |
+
if not valid_string_length(s, trailing_dot=True):
|
| 625 |
+
raise IDNAError("Domain too long")
|
| 626 |
+
trailing_dot = False
|
| 627 |
+
result = []
|
| 628 |
+
labels = s.split(".") if strict else _unicode_dots_re.split(s)
|
| 629 |
+
if not labels or labels == [""]:
|
| 630 |
+
raise IDNAError("Empty domain")
|
| 631 |
+
if not labels[-1]:
|
| 632 |
+
del labels[-1]
|
| 633 |
+
trailing_dot = True
|
| 634 |
+
for label in labels:
|
| 635 |
+
try:
|
| 636 |
+
u = ulabel(label)
|
| 637 |
+
except IDNAError:
|
| 638 |
+
if display and label[:4].lower() == "xn--":
|
| 639 |
+
u = label.lower()
|
| 640 |
+
else:
|
| 641 |
+
raise
|
| 642 |
+
if u:
|
| 643 |
+
result.append(u)
|
| 644 |
+
else:
|
| 645 |
+
raise IDNAError("Empty label")
|
| 646 |
+
if trailing_dot:
|
| 647 |
+
result.append("")
|
| 648 |
+
return ".".join(result)
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/idnadata.py
ADDED
|
@@ -0,0 +1,1897 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file is automatically generated by tools/idna-data
|
| 2 |
+
|
| 3 |
+
__version__ = "17.0.0"
|
| 4 |
+
|
| 5 |
+
scripts = {
|
| 6 |
+
"Greek": (
|
| 7 |
+
0x37000000374,
|
| 8 |
+
0x37500000378,
|
| 9 |
+
0x37A0000037E,
|
| 10 |
+
0x37F00000380,
|
| 11 |
+
0x38400000385,
|
| 12 |
+
0x38600000387,
|
| 13 |
+
0x3880000038B,
|
| 14 |
+
0x38C0000038D,
|
| 15 |
+
0x38E000003A2,
|
| 16 |
+
0x3A3000003E2,
|
| 17 |
+
0x3F000000400,
|
| 18 |
+
0x1D2600001D2B,
|
| 19 |
+
0x1D5D00001D62,
|
| 20 |
+
0x1D6600001D6B,
|
| 21 |
+
0x1DBF00001DC0,
|
| 22 |
+
0x1F0000001F16,
|
| 23 |
+
0x1F1800001F1E,
|
| 24 |
+
0x1F2000001F46,
|
| 25 |
+
0x1F4800001F4E,
|
| 26 |
+
0x1F5000001F58,
|
| 27 |
+
0x1F5900001F5A,
|
| 28 |
+
0x1F5B00001F5C,
|
| 29 |
+
0x1F5D00001F5E,
|
| 30 |
+
0x1F5F00001F7E,
|
| 31 |
+
0x1F8000001FB5,
|
| 32 |
+
0x1FB600001FC5,
|
| 33 |
+
0x1FC600001FD4,
|
| 34 |
+
0x1FD600001FDC,
|
| 35 |
+
0x1FDD00001FF0,
|
| 36 |
+
0x1FF200001FF5,
|
| 37 |
+
0x1FF600001FFF,
|
| 38 |
+
0x212600002127,
|
| 39 |
+
0xAB650000AB66,
|
| 40 |
+
0x101400001018F,
|
| 41 |
+
0x101A0000101A1,
|
| 42 |
+
0x1D2000001D246,
|
| 43 |
+
),
|
| 44 |
+
"Han": (
|
| 45 |
+
0x2E8000002E9A,
|
| 46 |
+
0x2E9B00002EF4,
|
| 47 |
+
0x2F0000002FD6,
|
| 48 |
+
0x300500003006,
|
| 49 |
+
0x300700003008,
|
| 50 |
+
0x30210000302A,
|
| 51 |
+
0x30380000303C,
|
| 52 |
+
0x340000004DC0,
|
| 53 |
+
0x4E000000A000,
|
| 54 |
+
0xF9000000FA6E,
|
| 55 |
+
0xFA700000FADA,
|
| 56 |
+
0x16FE200016FE4,
|
| 57 |
+
0x16FF000016FF7,
|
| 58 |
+
0x200000002A6E0,
|
| 59 |
+
0x2A7000002B81E,
|
| 60 |
+
0x2B8200002CEAE,
|
| 61 |
+
0x2CEB00002EBE1,
|
| 62 |
+
0x2EBF00002EE5E,
|
| 63 |
+
0x2F8000002FA1E,
|
| 64 |
+
0x300000003134B,
|
| 65 |
+
0x313500003347A,
|
| 66 |
+
),
|
| 67 |
+
"Hebrew": (
|
| 68 |
+
0x591000005C8,
|
| 69 |
+
0x5D0000005EB,
|
| 70 |
+
0x5EF000005F5,
|
| 71 |
+
0xFB1D0000FB37,
|
| 72 |
+
0xFB380000FB3D,
|
| 73 |
+
0xFB3E0000FB3F,
|
| 74 |
+
0xFB400000FB42,
|
| 75 |
+
0xFB430000FB45,
|
| 76 |
+
0xFB460000FB50,
|
| 77 |
+
),
|
| 78 |
+
"Hiragana": (
|
| 79 |
+
0x304100003097,
|
| 80 |
+
0x309D000030A0,
|
| 81 |
+
0x1B0010001B120,
|
| 82 |
+
0x1B1320001B133,
|
| 83 |
+
0x1B1500001B153,
|
| 84 |
+
0x1F2000001F201,
|
| 85 |
+
),
|
| 86 |
+
"Katakana": (
|
| 87 |
+
0x30A1000030FB,
|
| 88 |
+
0x30FD00003100,
|
| 89 |
+
0x31F000003200,
|
| 90 |
+
0x32D0000032FF,
|
| 91 |
+
0x330000003358,
|
| 92 |
+
0xFF660000FF70,
|
| 93 |
+
0xFF710000FF9E,
|
| 94 |
+
0x1AFF00001AFF4,
|
| 95 |
+
0x1AFF50001AFFC,
|
| 96 |
+
0x1AFFD0001AFFF,
|
| 97 |
+
0x1B0000001B001,
|
| 98 |
+
0x1B1200001B123,
|
| 99 |
+
0x1B1550001B156,
|
| 100 |
+
0x1B1640001B168,
|
| 101 |
+
),
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
joining_types = {
|
| 106 |
+
"C": (
|
| 107 |
+
0x64000000641,
|
| 108 |
+
0x7FA000007FB,
|
| 109 |
+
0x88300000886,
|
| 110 |
+
0x180A0000180B,
|
| 111 |
+
0x200D0000200E,
|
| 112 |
+
),
|
| 113 |
+
"D": (
|
| 114 |
+
0x62000000621,
|
| 115 |
+
0x62600000627,
|
| 116 |
+
0x62800000629,
|
| 117 |
+
0x62A0000062F,
|
| 118 |
+
0x63300000640,
|
| 119 |
+
0x64100000648,
|
| 120 |
+
0x6490000064B,
|
| 121 |
+
0x66E00000670,
|
| 122 |
+
0x67800000688,
|
| 123 |
+
0x69A000006C0,
|
| 124 |
+
0x6C1000006C3,
|
| 125 |
+
0x6CC000006CD,
|
| 126 |
+
0x6CE000006CF,
|
| 127 |
+
0x6D0000006D2,
|
| 128 |
+
0x6FA000006FD,
|
| 129 |
+
0x6FF00000700,
|
| 130 |
+
0x71200000715,
|
| 131 |
+
0x71A0000071E,
|
| 132 |
+
0x71F00000728,
|
| 133 |
+
0x7290000072A,
|
| 134 |
+
0x72B0000072C,
|
| 135 |
+
0x72D0000072F,
|
| 136 |
+
0x74E00000759,
|
| 137 |
+
0x75C0000076B,
|
| 138 |
+
0x76D00000771,
|
| 139 |
+
0x77200000773,
|
| 140 |
+
0x77500000778,
|
| 141 |
+
0x77A00000780,
|
| 142 |
+
0x7CA000007EB,
|
| 143 |
+
0x84100000846,
|
| 144 |
+
0x84800000849,
|
| 145 |
+
0x84A00000854,
|
| 146 |
+
0x85500000856,
|
| 147 |
+
0x86000000861,
|
| 148 |
+
0x86200000866,
|
| 149 |
+
0x86800000869,
|
| 150 |
+
0x88600000887,
|
| 151 |
+
0x8890000088E,
|
| 152 |
+
0x88F00000890,
|
| 153 |
+
0x8A0000008AA,
|
| 154 |
+
0x8AF000008B1,
|
| 155 |
+
0x8B3000008B9,
|
| 156 |
+
0x8BA000008C9,
|
| 157 |
+
0x180700001808,
|
| 158 |
+
0x182000001879,
|
| 159 |
+
0x1887000018A9,
|
| 160 |
+
0x18AA000018AB,
|
| 161 |
+
0xA8400000A872,
|
| 162 |
+
0x10AC000010AC5,
|
| 163 |
+
0x10AD300010AD7,
|
| 164 |
+
0x10AD800010ADD,
|
| 165 |
+
0x10ADE00010AE1,
|
| 166 |
+
0x10AEB00010AEF,
|
| 167 |
+
0x10B8000010B81,
|
| 168 |
+
0x10B8200010B83,
|
| 169 |
+
0x10B8600010B89,
|
| 170 |
+
0x10B8A00010B8C,
|
| 171 |
+
0x10B8D00010B8E,
|
| 172 |
+
0x10B9000010B91,
|
| 173 |
+
0x10BAD00010BAF,
|
| 174 |
+
0x10D0100010D22,
|
| 175 |
+
0x10D2300010D24,
|
| 176 |
+
0x10EC300010EC5,
|
| 177 |
+
0x10EC600010EC8,
|
| 178 |
+
0x10F3000010F33,
|
| 179 |
+
0x10F3400010F45,
|
| 180 |
+
0x10F5100010F54,
|
| 181 |
+
0x10F7000010F74,
|
| 182 |
+
0x10F7600010F82,
|
| 183 |
+
0x10FB000010FB1,
|
| 184 |
+
0x10FB200010FB4,
|
| 185 |
+
0x10FB800010FB9,
|
| 186 |
+
0x10FBB00010FBD,
|
| 187 |
+
0x10FBE00010FC0,
|
| 188 |
+
0x10FC100010FC2,
|
| 189 |
+
0x10FC400010FC5,
|
| 190 |
+
0x10FCA00010FCB,
|
| 191 |
+
0x1E9000001E944,
|
| 192 |
+
),
|
| 193 |
+
"L": (
|
| 194 |
+
0xA8720000A873,
|
| 195 |
+
0x10ACD00010ACE,
|
| 196 |
+
0x10AD700010AD8,
|
| 197 |
+
0x10D0000010D01,
|
| 198 |
+
0x10FCB00010FCC,
|
| 199 |
+
),
|
| 200 |
+
"R": (
|
| 201 |
+
0x62200000626,
|
| 202 |
+
0x62700000628,
|
| 203 |
+
0x6290000062A,
|
| 204 |
+
0x62F00000633,
|
| 205 |
+
0x64800000649,
|
| 206 |
+
0x67100000674,
|
| 207 |
+
0x67500000678,
|
| 208 |
+
0x6880000069A,
|
| 209 |
+
0x6C0000006C1,
|
| 210 |
+
0x6C3000006CC,
|
| 211 |
+
0x6CD000006CE,
|
| 212 |
+
0x6CF000006D0,
|
| 213 |
+
0x6D2000006D4,
|
| 214 |
+
0x6D5000006D6,
|
| 215 |
+
0x6EE000006F0,
|
| 216 |
+
0x71000000711,
|
| 217 |
+
0x7150000071A,
|
| 218 |
+
0x71E0000071F,
|
| 219 |
+
0x72800000729,
|
| 220 |
+
0x72A0000072B,
|
| 221 |
+
0x72C0000072D,
|
| 222 |
+
0x72F00000730,
|
| 223 |
+
0x74D0000074E,
|
| 224 |
+
0x7590000075C,
|
| 225 |
+
0x76B0000076D,
|
| 226 |
+
0x77100000772,
|
| 227 |
+
0x77300000775,
|
| 228 |
+
0x7780000077A,
|
| 229 |
+
0x84000000841,
|
| 230 |
+
0x84600000848,
|
| 231 |
+
0x8490000084A,
|
| 232 |
+
0x85400000855,
|
| 233 |
+
0x85600000859,
|
| 234 |
+
0x86700000868,
|
| 235 |
+
0x8690000086B,
|
| 236 |
+
0x87000000883,
|
| 237 |
+
0x88E0000088F,
|
| 238 |
+
0x8AA000008AD,
|
| 239 |
+
0x8AE000008AF,
|
| 240 |
+
0x8B1000008B3,
|
| 241 |
+
0x8B9000008BA,
|
| 242 |
+
0x10AC500010AC6,
|
| 243 |
+
0x10AC700010AC8,
|
| 244 |
+
0x10AC900010ACB,
|
| 245 |
+
0x10ACE00010AD3,
|
| 246 |
+
0x10ADD00010ADE,
|
| 247 |
+
0x10AE100010AE2,
|
| 248 |
+
0x10AE400010AE5,
|
| 249 |
+
0x10AEF00010AF0,
|
| 250 |
+
0x10B8100010B82,
|
| 251 |
+
0x10B8300010B86,
|
| 252 |
+
0x10B8900010B8A,
|
| 253 |
+
0x10B8C00010B8D,
|
| 254 |
+
0x10B8E00010B90,
|
| 255 |
+
0x10B9100010B92,
|
| 256 |
+
0x10BA900010BAD,
|
| 257 |
+
0x10D2200010D23,
|
| 258 |
+
0x10EC200010EC3,
|
| 259 |
+
0x10F3300010F34,
|
| 260 |
+
0x10F5400010F55,
|
| 261 |
+
0x10F7400010F76,
|
| 262 |
+
0x10FB400010FB7,
|
| 263 |
+
0x10FB900010FBB,
|
| 264 |
+
0x10FBD00010FBE,
|
| 265 |
+
0x10FC200010FC4,
|
| 266 |
+
0x10FC900010FCA,
|
| 267 |
+
),
|
| 268 |
+
"T": (
|
| 269 |
+
0xAD000000AE,
|
| 270 |
+
0x30000000370,
|
| 271 |
+
0x4830000048A,
|
| 272 |
+
0x591000005BE,
|
| 273 |
+
0x5BF000005C0,
|
| 274 |
+
0x5C1000005C3,
|
| 275 |
+
0x5C4000005C6,
|
| 276 |
+
0x5C7000005C8,
|
| 277 |
+
0x6100000061B,
|
| 278 |
+
0x61C0000061D,
|
| 279 |
+
0x64B00000660,
|
| 280 |
+
0x67000000671,
|
| 281 |
+
0x6D6000006DD,
|
| 282 |
+
0x6DF000006E5,
|
| 283 |
+
0x6E7000006E9,
|
| 284 |
+
0x6EA000006EE,
|
| 285 |
+
0x70F00000710,
|
| 286 |
+
0x71100000712,
|
| 287 |
+
0x7300000074B,
|
| 288 |
+
0x7A6000007B1,
|
| 289 |
+
0x7EB000007F4,
|
| 290 |
+
0x7FD000007FE,
|
| 291 |
+
0x8160000081A,
|
| 292 |
+
0x81B00000824,
|
| 293 |
+
0x82500000828,
|
| 294 |
+
0x8290000082E,
|
| 295 |
+
0x8590000085C,
|
| 296 |
+
0x897000008A0,
|
| 297 |
+
0x8CA000008E2,
|
| 298 |
+
0x8E300000903,
|
| 299 |
+
0x93A0000093B,
|
| 300 |
+
0x93C0000093D,
|
| 301 |
+
0x94100000949,
|
| 302 |
+
0x94D0000094E,
|
| 303 |
+
0x95100000958,
|
| 304 |
+
0x96200000964,
|
| 305 |
+
0x98100000982,
|
| 306 |
+
0x9BC000009BD,
|
| 307 |
+
0x9C1000009C5,
|
| 308 |
+
0x9CD000009CE,
|
| 309 |
+
0x9E2000009E4,
|
| 310 |
+
0x9FE000009FF,
|
| 311 |
+
0xA0100000A03,
|
| 312 |
+
0xA3C00000A3D,
|
| 313 |
+
0xA4100000A43,
|
| 314 |
+
0xA4700000A49,
|
| 315 |
+
0xA4B00000A4E,
|
| 316 |
+
0xA5100000A52,
|
| 317 |
+
0xA7000000A72,
|
| 318 |
+
0xA7500000A76,
|
| 319 |
+
0xA8100000A83,
|
| 320 |
+
0xABC00000ABD,
|
| 321 |
+
0xAC100000AC6,
|
| 322 |
+
0xAC700000AC9,
|
| 323 |
+
0xACD00000ACE,
|
| 324 |
+
0xAE200000AE4,
|
| 325 |
+
0xAFA00000B00,
|
| 326 |
+
0xB0100000B02,
|
| 327 |
+
0xB3C00000B3D,
|
| 328 |
+
0xB3F00000B40,
|
| 329 |
+
0xB4100000B45,
|
| 330 |
+
0xB4D00000B4E,
|
| 331 |
+
0xB5500000B57,
|
| 332 |
+
0xB6200000B64,
|
| 333 |
+
0xB8200000B83,
|
| 334 |
+
0xBC000000BC1,
|
| 335 |
+
0xBCD00000BCE,
|
| 336 |
+
0xC0000000C01,
|
| 337 |
+
0xC0400000C05,
|
| 338 |
+
0xC3C00000C3D,
|
| 339 |
+
0xC3E00000C41,
|
| 340 |
+
0xC4600000C49,
|
| 341 |
+
0xC4A00000C4E,
|
| 342 |
+
0xC5500000C57,
|
| 343 |
+
0xC6200000C64,
|
| 344 |
+
0xC8100000C82,
|
| 345 |
+
0xCBC00000CBD,
|
| 346 |
+
0xCBF00000CC0,
|
| 347 |
+
0xCC600000CC7,
|
| 348 |
+
0xCCC00000CCE,
|
| 349 |
+
0xCE200000CE4,
|
| 350 |
+
0xD0000000D02,
|
| 351 |
+
0xD3B00000D3D,
|
| 352 |
+
0xD4100000D45,
|
| 353 |
+
0xD4D00000D4E,
|
| 354 |
+
0xD6200000D64,
|
| 355 |
+
0xD8100000D82,
|
| 356 |
+
0xDCA00000DCB,
|
| 357 |
+
0xDD200000DD5,
|
| 358 |
+
0xDD600000DD7,
|
| 359 |
+
0xE3100000E32,
|
| 360 |
+
0xE3400000E3B,
|
| 361 |
+
0xE4700000E4F,
|
| 362 |
+
0xEB100000EB2,
|
| 363 |
+
0xEB400000EBD,
|
| 364 |
+
0xEC800000ECF,
|
| 365 |
+
0xF1800000F1A,
|
| 366 |
+
0xF3500000F36,
|
| 367 |
+
0xF3700000F38,
|
| 368 |
+
0xF3900000F3A,
|
| 369 |
+
0xF7100000F7F,
|
| 370 |
+
0xF8000000F85,
|
| 371 |
+
0xF8600000F88,
|
| 372 |
+
0xF8D00000F98,
|
| 373 |
+
0xF9900000FBD,
|
| 374 |
+
0xFC600000FC7,
|
| 375 |
+
0x102D00001031,
|
| 376 |
+
0x103200001038,
|
| 377 |
+
0x10390000103B,
|
| 378 |
+
0x103D0000103F,
|
| 379 |
+
0x10580000105A,
|
| 380 |
+
0x105E00001061,
|
| 381 |
+
0x107100001075,
|
| 382 |
+
0x108200001083,
|
| 383 |
+
0x108500001087,
|
| 384 |
+
0x108D0000108E,
|
| 385 |
+
0x109D0000109E,
|
| 386 |
+
0x135D00001360,
|
| 387 |
+
0x171200001715,
|
| 388 |
+
0x173200001734,
|
| 389 |
+
0x175200001754,
|
| 390 |
+
0x177200001774,
|
| 391 |
+
0x17B4000017B6,
|
| 392 |
+
0x17B7000017BE,
|
| 393 |
+
0x17C6000017C7,
|
| 394 |
+
0x17C9000017D4,
|
| 395 |
+
0x17DD000017DE,
|
| 396 |
+
0x180B0000180E,
|
| 397 |
+
0x180F00001810,
|
| 398 |
+
0x188500001887,
|
| 399 |
+
0x18A9000018AA,
|
| 400 |
+
0x192000001923,
|
| 401 |
+
0x192700001929,
|
| 402 |
+
0x193200001933,
|
| 403 |
+
0x19390000193C,
|
| 404 |
+
0x1A1700001A19,
|
| 405 |
+
0x1A1B00001A1C,
|
| 406 |
+
0x1A5600001A57,
|
| 407 |
+
0x1A5800001A5F,
|
| 408 |
+
0x1A6000001A61,
|
| 409 |
+
0x1A6200001A63,
|
| 410 |
+
0x1A6500001A6D,
|
| 411 |
+
0x1A7300001A7D,
|
| 412 |
+
0x1A7F00001A80,
|
| 413 |
+
0x1AB000001ADE,
|
| 414 |
+
0x1AE000001AEC,
|
| 415 |
+
0x1B0000001B04,
|
| 416 |
+
0x1B3400001B35,
|
| 417 |
+
0x1B3600001B3B,
|
| 418 |
+
0x1B3C00001B3D,
|
| 419 |
+
0x1B4200001B43,
|
| 420 |
+
0x1B6B00001B74,
|
| 421 |
+
0x1B8000001B82,
|
| 422 |
+
0x1BA200001BA6,
|
| 423 |
+
0x1BA800001BAA,
|
| 424 |
+
0x1BAB00001BAE,
|
| 425 |
+
0x1BE600001BE7,
|
| 426 |
+
0x1BE800001BEA,
|
| 427 |
+
0x1BED00001BEE,
|
| 428 |
+
0x1BEF00001BF2,
|
| 429 |
+
0x1C2C00001C34,
|
| 430 |
+
0x1C3600001C38,
|
| 431 |
+
0x1CD000001CD3,
|
| 432 |
+
0x1CD400001CE1,
|
| 433 |
+
0x1CE200001CE9,
|
| 434 |
+
0x1CED00001CEE,
|
| 435 |
+
0x1CF400001CF5,
|
| 436 |
+
0x1CF800001CFA,
|
| 437 |
+
0x1DC000001E00,
|
| 438 |
+
0x200B0000200C,
|
| 439 |
+
0x200E00002010,
|
| 440 |
+
0x202A0000202F,
|
| 441 |
+
0x206000002065,
|
| 442 |
+
0x206A00002070,
|
| 443 |
+
0x20D0000020F1,
|
| 444 |
+
0x2CEF00002CF2,
|
| 445 |
+
0x2D7F00002D80,
|
| 446 |
+
0x2DE000002E00,
|
| 447 |
+
0x302A0000302E,
|
| 448 |
+
0x30990000309B,
|
| 449 |
+
0xA66F0000A673,
|
| 450 |
+
0xA6740000A67E,
|
| 451 |
+
0xA69E0000A6A0,
|
| 452 |
+
0xA6F00000A6F2,
|
| 453 |
+
0xA8020000A803,
|
| 454 |
+
0xA8060000A807,
|
| 455 |
+
0xA80B0000A80C,
|
| 456 |
+
0xA8250000A827,
|
| 457 |
+
0xA82C0000A82D,
|
| 458 |
+
0xA8C40000A8C6,
|
| 459 |
+
0xA8E00000A8F2,
|
| 460 |
+
0xA8FF0000A900,
|
| 461 |
+
0xA9260000A92E,
|
| 462 |
+
0xA9470000A952,
|
| 463 |
+
0xA9800000A983,
|
| 464 |
+
0xA9B30000A9B4,
|
| 465 |
+
0xA9B60000A9BA,
|
| 466 |
+
0xA9BC0000A9BE,
|
| 467 |
+
0xA9E50000A9E6,
|
| 468 |
+
0xAA290000AA2F,
|
| 469 |
+
0xAA310000AA33,
|
| 470 |
+
0xAA350000AA37,
|
| 471 |
+
0xAA430000AA44,
|
| 472 |
+
0xAA4C0000AA4D,
|
| 473 |
+
0xAA7C0000AA7D,
|
| 474 |
+
0xAAB00000AAB1,
|
| 475 |
+
0xAAB20000AAB5,
|
| 476 |
+
0xAAB70000AAB9,
|
| 477 |
+
0xAABE0000AAC0,
|
| 478 |
+
0xAAC10000AAC2,
|
| 479 |
+
0xAAEC0000AAEE,
|
| 480 |
+
0xAAF60000AAF7,
|
| 481 |
+
0xABE50000ABE6,
|
| 482 |
+
0xABE80000ABE9,
|
| 483 |
+
0xABED0000ABEE,
|
| 484 |
+
0xFB1E0000FB1F,
|
| 485 |
+
0xFE000000FE10,
|
| 486 |
+
0xFE200000FE30,
|
| 487 |
+
0xFEFF0000FF00,
|
| 488 |
+
0xFFF90000FFFC,
|
| 489 |
+
0x101FD000101FE,
|
| 490 |
+
0x102E0000102E1,
|
| 491 |
+
0x103760001037B,
|
| 492 |
+
0x10A0100010A04,
|
| 493 |
+
0x10A0500010A07,
|
| 494 |
+
0x10A0C00010A10,
|
| 495 |
+
0x10A3800010A3B,
|
| 496 |
+
0x10A3F00010A40,
|
| 497 |
+
0x10AE500010AE7,
|
| 498 |
+
0x10D2400010D28,
|
| 499 |
+
0x10D6900010D6E,
|
| 500 |
+
0x10EAB00010EAD,
|
| 501 |
+
0x10EFA00010F00,
|
| 502 |
+
0x10F4600010F51,
|
| 503 |
+
0x10F8200010F86,
|
| 504 |
+
0x1100100011002,
|
| 505 |
+
0x1103800011047,
|
| 506 |
+
0x1107000011071,
|
| 507 |
+
0x1107300011075,
|
| 508 |
+
0x1107F00011082,
|
| 509 |
+
0x110B3000110B7,
|
| 510 |
+
0x110B9000110BB,
|
| 511 |
+
0x110C2000110C3,
|
| 512 |
+
0x1110000011103,
|
| 513 |
+
0x111270001112C,
|
| 514 |
+
0x1112D00011135,
|
| 515 |
+
0x1117300011174,
|
| 516 |
+
0x1118000011182,
|
| 517 |
+
0x111B6000111BF,
|
| 518 |
+
0x111C9000111CD,
|
| 519 |
+
0x111CF000111D0,
|
| 520 |
+
0x1122F00011232,
|
| 521 |
+
0x1123400011235,
|
| 522 |
+
0x1123600011238,
|
| 523 |
+
0x1123E0001123F,
|
| 524 |
+
0x1124100011242,
|
| 525 |
+
0x112DF000112E0,
|
| 526 |
+
0x112E3000112EB,
|
| 527 |
+
0x1130000011302,
|
| 528 |
+
0x1133B0001133D,
|
| 529 |
+
0x1134000011341,
|
| 530 |
+
0x113660001136D,
|
| 531 |
+
0x1137000011375,
|
| 532 |
+
0x113BB000113C1,
|
| 533 |
+
0x113CE000113CF,
|
| 534 |
+
0x113D0000113D1,
|
| 535 |
+
0x113D2000113D3,
|
| 536 |
+
0x113E1000113E3,
|
| 537 |
+
0x1143800011440,
|
| 538 |
+
0x1144200011445,
|
| 539 |
+
0x1144600011447,
|
| 540 |
+
0x1145E0001145F,
|
| 541 |
+
0x114B3000114B9,
|
| 542 |
+
0x114BA000114BB,
|
| 543 |
+
0x114BF000114C1,
|
| 544 |
+
0x114C2000114C4,
|
| 545 |
+
0x115B2000115B6,
|
| 546 |
+
0x115BC000115BE,
|
| 547 |
+
0x115BF000115C1,
|
| 548 |
+
0x115DC000115DE,
|
| 549 |
+
0x116330001163B,
|
| 550 |
+
0x1163D0001163E,
|
| 551 |
+
0x1163F00011641,
|
| 552 |
+
0x116AB000116AC,
|
| 553 |
+
0x116AD000116AE,
|
| 554 |
+
0x116B0000116B6,
|
| 555 |
+
0x116B7000116B8,
|
| 556 |
+
0x1171D0001171E,
|
| 557 |
+
0x1171F00011720,
|
| 558 |
+
0x1172200011726,
|
| 559 |
+
0x117270001172C,
|
| 560 |
+
0x1182F00011838,
|
| 561 |
+
0x118390001183B,
|
| 562 |
+
0x1193B0001193D,
|
| 563 |
+
0x1193E0001193F,
|
| 564 |
+
0x1194300011944,
|
| 565 |
+
0x119D4000119D8,
|
| 566 |
+
0x119DA000119DC,
|
| 567 |
+
0x119E0000119E1,
|
| 568 |
+
0x11A0100011A0B,
|
| 569 |
+
0x11A3300011A39,
|
| 570 |
+
0x11A3B00011A3F,
|
| 571 |
+
0x11A4700011A48,
|
| 572 |
+
0x11A5100011A57,
|
| 573 |
+
0x11A5900011A5C,
|
| 574 |
+
0x11A8A00011A97,
|
| 575 |
+
0x11A9800011A9A,
|
| 576 |
+
0x11B6000011B61,
|
| 577 |
+
0x11B6200011B65,
|
| 578 |
+
0x11B6600011B67,
|
| 579 |
+
0x11C3000011C37,
|
| 580 |
+
0x11C3800011C3E,
|
| 581 |
+
0x11C3F00011C40,
|
| 582 |
+
0x11C9200011CA8,
|
| 583 |
+
0x11CAA00011CB1,
|
| 584 |
+
0x11CB200011CB4,
|
| 585 |
+
0x11CB500011CB7,
|
| 586 |
+
0x11D3100011D37,
|
| 587 |
+
0x11D3A00011D3B,
|
| 588 |
+
0x11D3C00011D3E,
|
| 589 |
+
0x11D3F00011D46,
|
| 590 |
+
0x11D4700011D48,
|
| 591 |
+
0x11D9000011D92,
|
| 592 |
+
0x11D9500011D96,
|
| 593 |
+
0x11D9700011D98,
|
| 594 |
+
0x11EF300011EF5,
|
| 595 |
+
0x11F0000011F02,
|
| 596 |
+
0x11F3600011F3B,
|
| 597 |
+
0x11F4000011F41,
|
| 598 |
+
0x11F4200011F43,
|
| 599 |
+
0x11F5A00011F5B,
|
| 600 |
+
0x1343000013441,
|
| 601 |
+
0x1344700013456,
|
| 602 |
+
0x1611E0001612A,
|
| 603 |
+
0x1612D00016130,
|
| 604 |
+
0x16AF000016AF5,
|
| 605 |
+
0x16B3000016B37,
|
| 606 |
+
0x16F4F00016F50,
|
| 607 |
+
0x16F8F00016F93,
|
| 608 |
+
0x16FE400016FE5,
|
| 609 |
+
0x1BC9D0001BC9F,
|
| 610 |
+
0x1BCA00001BCA4,
|
| 611 |
+
0x1CF000001CF2E,
|
| 612 |
+
0x1CF300001CF47,
|
| 613 |
+
0x1D1670001D16A,
|
| 614 |
+
0x1D1730001D183,
|
| 615 |
+
0x1D1850001D18C,
|
| 616 |
+
0x1D1AA0001D1AE,
|
| 617 |
+
0x1D2420001D245,
|
| 618 |
+
0x1DA000001DA37,
|
| 619 |
+
0x1DA3B0001DA6D,
|
| 620 |
+
0x1DA750001DA76,
|
| 621 |
+
0x1DA840001DA85,
|
| 622 |
+
0x1DA9B0001DAA0,
|
| 623 |
+
0x1DAA10001DAB0,
|
| 624 |
+
0x1E0000001E007,
|
| 625 |
+
0x1E0080001E019,
|
| 626 |
+
0x1E01B0001E022,
|
| 627 |
+
0x1E0230001E025,
|
| 628 |
+
0x1E0260001E02B,
|
| 629 |
+
0x1E08F0001E090,
|
| 630 |
+
0x1E1300001E137,
|
| 631 |
+
0x1E2AE0001E2AF,
|
| 632 |
+
0x1E2EC0001E2F0,
|
| 633 |
+
0x1E4EC0001E4F0,
|
| 634 |
+
0x1E5EE0001E5F0,
|
| 635 |
+
0x1E6E30001E6E4,
|
| 636 |
+
0x1E6E60001E6E7,
|
| 637 |
+
0x1E6EE0001E6F0,
|
| 638 |
+
0x1E6F50001E6F6,
|
| 639 |
+
0x1E8D00001E8D7,
|
| 640 |
+
0x1E9440001E94C,
|
| 641 |
+
0xE0001000E0002,
|
| 642 |
+
0xE0020000E0080,
|
| 643 |
+
0xE0100000E01F0,
|
| 644 |
+
),
|
| 645 |
+
}
|
| 646 |
+
|
| 647 |
+
|
| 648 |
+
codepoint_classes = {
|
| 649 |
+
"PVALID": (
|
| 650 |
+
0x2D0000002E,
|
| 651 |
+
0x300000003A,
|
| 652 |
+
0x610000007B,
|
| 653 |
+
0xDF000000F7,
|
| 654 |
+
0xF800000100,
|
| 655 |
+
0x10100000102,
|
| 656 |
+
0x10300000104,
|
| 657 |
+
0x10500000106,
|
| 658 |
+
0x10700000108,
|
| 659 |
+
0x1090000010A,
|
| 660 |
+
0x10B0000010C,
|
| 661 |
+
0x10D0000010E,
|
| 662 |
+
0x10F00000110,
|
| 663 |
+
0x11100000112,
|
| 664 |
+
0x11300000114,
|
| 665 |
+
0x11500000116,
|
| 666 |
+
0x11700000118,
|
| 667 |
+
0x1190000011A,
|
| 668 |
+
0x11B0000011C,
|
| 669 |
+
0x11D0000011E,
|
| 670 |
+
0x11F00000120,
|
| 671 |
+
0x12100000122,
|
| 672 |
+
0x12300000124,
|
| 673 |
+
0x12500000126,
|
| 674 |
+
0x12700000128,
|
| 675 |
+
0x1290000012A,
|
| 676 |
+
0x12B0000012C,
|
| 677 |
+
0x12D0000012E,
|
| 678 |
+
0x12F00000130,
|
| 679 |
+
0x13100000132,
|
| 680 |
+
0x13500000136,
|
| 681 |
+
0x13700000139,
|
| 682 |
+
0x13A0000013B,
|
| 683 |
+
0x13C0000013D,
|
| 684 |
+
0x13E0000013F,
|
| 685 |
+
0x14200000143,
|
| 686 |
+
0x14400000145,
|
| 687 |
+
0x14600000147,
|
| 688 |
+
0x14800000149,
|
| 689 |
+
0x14B0000014C,
|
| 690 |
+
0x14D0000014E,
|
| 691 |
+
0x14F00000150,
|
| 692 |
+
0x15100000152,
|
| 693 |
+
0x15300000154,
|
| 694 |
+
0x15500000156,
|
| 695 |
+
0x15700000158,
|
| 696 |
+
0x1590000015A,
|
| 697 |
+
0x15B0000015C,
|
| 698 |
+
0x15D0000015E,
|
| 699 |
+
0x15F00000160,
|
| 700 |
+
0x16100000162,
|
| 701 |
+
0x16300000164,
|
| 702 |
+
0x16500000166,
|
| 703 |
+
0x16700000168,
|
| 704 |
+
0x1690000016A,
|
| 705 |
+
0x16B0000016C,
|
| 706 |
+
0x16D0000016E,
|
| 707 |
+
0x16F00000170,
|
| 708 |
+
0x17100000172,
|
| 709 |
+
0x17300000174,
|
| 710 |
+
0x17500000176,
|
| 711 |
+
0x17700000178,
|
| 712 |
+
0x17A0000017B,
|
| 713 |
+
0x17C0000017D,
|
| 714 |
+
0x17E0000017F,
|
| 715 |
+
0x18000000181,
|
| 716 |
+
0x18300000184,
|
| 717 |
+
0x18500000186,
|
| 718 |
+
0x18800000189,
|
| 719 |
+
0x18C0000018E,
|
| 720 |
+
0x19200000193,
|
| 721 |
+
0x19500000196,
|
| 722 |
+
0x1990000019C,
|
| 723 |
+
0x19E0000019F,
|
| 724 |
+
0x1A1000001A2,
|
| 725 |
+
0x1A3000001A4,
|
| 726 |
+
0x1A5000001A6,
|
| 727 |
+
0x1A8000001A9,
|
| 728 |
+
0x1AA000001AC,
|
| 729 |
+
0x1AD000001AE,
|
| 730 |
+
0x1B0000001B1,
|
| 731 |
+
0x1B4000001B5,
|
| 732 |
+
0x1B6000001B7,
|
| 733 |
+
0x1B9000001BC,
|
| 734 |
+
0x1BD000001C4,
|
| 735 |
+
0x1CE000001CF,
|
| 736 |
+
0x1D0000001D1,
|
| 737 |
+
0x1D2000001D3,
|
| 738 |
+
0x1D4000001D5,
|
| 739 |
+
0x1D6000001D7,
|
| 740 |
+
0x1D8000001D9,
|
| 741 |
+
0x1DA000001DB,
|
| 742 |
+
0x1DC000001DE,
|
| 743 |
+
0x1DF000001E0,
|
| 744 |
+
0x1E1000001E2,
|
| 745 |
+
0x1E3000001E4,
|
| 746 |
+
0x1E5000001E6,
|
| 747 |
+
0x1E7000001E8,
|
| 748 |
+
0x1E9000001EA,
|
| 749 |
+
0x1EB000001EC,
|
| 750 |
+
0x1ED000001EE,
|
| 751 |
+
0x1EF000001F1,
|
| 752 |
+
0x1F5000001F6,
|
| 753 |
+
0x1F9000001FA,
|
| 754 |
+
0x1FB000001FC,
|
| 755 |
+
0x1FD000001FE,
|
| 756 |
+
0x1FF00000200,
|
| 757 |
+
0x20100000202,
|
| 758 |
+
0x20300000204,
|
| 759 |
+
0x20500000206,
|
| 760 |
+
0x20700000208,
|
| 761 |
+
0x2090000020A,
|
| 762 |
+
0x20B0000020C,
|
| 763 |
+
0x20D0000020E,
|
| 764 |
+
0x20F00000210,
|
| 765 |
+
0x21100000212,
|
| 766 |
+
0x21300000214,
|
| 767 |
+
0x21500000216,
|
| 768 |
+
0x21700000218,
|
| 769 |
+
0x2190000021A,
|
| 770 |
+
0x21B0000021C,
|
| 771 |
+
0x21D0000021E,
|
| 772 |
+
0x21F00000220,
|
| 773 |
+
0x22100000222,
|
| 774 |
+
0x22300000224,
|
| 775 |
+
0x22500000226,
|
| 776 |
+
0x22700000228,
|
| 777 |
+
0x2290000022A,
|
| 778 |
+
0x22B0000022C,
|
| 779 |
+
0x22D0000022E,
|
| 780 |
+
0x22F00000230,
|
| 781 |
+
0x23100000232,
|
| 782 |
+
0x2330000023A,
|
| 783 |
+
0x23C0000023D,
|
| 784 |
+
0x23F00000241,
|
| 785 |
+
0x24200000243,
|
| 786 |
+
0x24700000248,
|
| 787 |
+
0x2490000024A,
|
| 788 |
+
0x24B0000024C,
|
| 789 |
+
0x24D0000024E,
|
| 790 |
+
0x24F000002B0,
|
| 791 |
+
0x2B9000002C2,
|
| 792 |
+
0x2C6000002D2,
|
| 793 |
+
0x2EC000002ED,
|
| 794 |
+
0x2EE000002EF,
|
| 795 |
+
0x30000000340,
|
| 796 |
+
0x34200000343,
|
| 797 |
+
0x3460000034F,
|
| 798 |
+
0x35000000370,
|
| 799 |
+
0x37100000372,
|
| 800 |
+
0x37300000374,
|
| 801 |
+
0x37700000378,
|
| 802 |
+
0x37B0000037E,
|
| 803 |
+
0x39000000391,
|
| 804 |
+
0x3AC000003CF,
|
| 805 |
+
0x3D7000003D8,
|
| 806 |
+
0x3D9000003DA,
|
| 807 |
+
0x3DB000003DC,
|
| 808 |
+
0x3DD000003DE,
|
| 809 |
+
0x3DF000003E0,
|
| 810 |
+
0x3E1000003E2,
|
| 811 |
+
0x3E3000003E4,
|
| 812 |
+
0x3E5000003E6,
|
| 813 |
+
0x3E7000003E8,
|
| 814 |
+
0x3E9000003EA,
|
| 815 |
+
0x3EB000003EC,
|
| 816 |
+
0x3ED000003EE,
|
| 817 |
+
0x3EF000003F0,
|
| 818 |
+
0x3F3000003F4,
|
| 819 |
+
0x3F8000003F9,
|
| 820 |
+
0x3FB000003FD,
|
| 821 |
+
0x43000000460,
|
| 822 |
+
0x46100000462,
|
| 823 |
+
0x46300000464,
|
| 824 |
+
0x46500000466,
|
| 825 |
+
0x46700000468,
|
| 826 |
+
0x4690000046A,
|
| 827 |
+
0x46B0000046C,
|
| 828 |
+
0x46D0000046E,
|
| 829 |
+
0x46F00000470,
|
| 830 |
+
0x47100000472,
|
| 831 |
+
0x47300000474,
|
| 832 |
+
0x47500000476,
|
| 833 |
+
0x47700000478,
|
| 834 |
+
0x4790000047A,
|
| 835 |
+
0x47B0000047C,
|
| 836 |
+
0x47D0000047E,
|
| 837 |
+
0x47F00000480,
|
| 838 |
+
0x48100000482,
|
| 839 |
+
0x48300000488,
|
| 840 |
+
0x48B0000048C,
|
| 841 |
+
0x48D0000048E,
|
| 842 |
+
0x48F00000490,
|
| 843 |
+
0x49100000492,
|
| 844 |
+
0x49300000494,
|
| 845 |
+
0x49500000496,
|
| 846 |
+
0x49700000498,
|
| 847 |
+
0x4990000049A,
|
| 848 |
+
0x49B0000049C,
|
| 849 |
+
0x49D0000049E,
|
| 850 |
+
0x49F000004A0,
|
| 851 |
+
0x4A1000004A2,
|
| 852 |
+
0x4A3000004A4,
|
| 853 |
+
0x4A5000004A6,
|
| 854 |
+
0x4A7000004A8,
|
| 855 |
+
0x4A9000004AA,
|
| 856 |
+
0x4AB000004AC,
|
| 857 |
+
0x4AD000004AE,
|
| 858 |
+
0x4AF000004B0,
|
| 859 |
+
0x4B1000004B2,
|
| 860 |
+
0x4B3000004B4,
|
| 861 |
+
0x4B5000004B6,
|
| 862 |
+
0x4B7000004B8,
|
| 863 |
+
0x4B9000004BA,
|
| 864 |
+
0x4BB000004BC,
|
| 865 |
+
0x4BD000004BE,
|
| 866 |
+
0x4BF000004C0,
|
| 867 |
+
0x4C2000004C3,
|
| 868 |
+
0x4C4000004C5,
|
| 869 |
+
0x4C6000004C7,
|
| 870 |
+
0x4C8000004C9,
|
| 871 |
+
0x4CA000004CB,
|
| 872 |
+
0x4CC000004CD,
|
| 873 |
+
0x4CE000004D0,
|
| 874 |
+
0x4D1000004D2,
|
| 875 |
+
0x4D3000004D4,
|
| 876 |
+
0x4D5000004D6,
|
| 877 |
+
0x4D7000004D8,
|
| 878 |
+
0x4D9000004DA,
|
| 879 |
+
0x4DB000004DC,
|
| 880 |
+
0x4DD000004DE,
|
| 881 |
+
0x4DF000004E0,
|
| 882 |
+
0x4E1000004E2,
|
| 883 |
+
0x4E3000004E4,
|
| 884 |
+
0x4E5000004E6,
|
| 885 |
+
0x4E7000004E8,
|
| 886 |
+
0x4E9000004EA,
|
| 887 |
+
0x4EB000004EC,
|
| 888 |
+
0x4ED000004EE,
|
| 889 |
+
0x4EF000004F0,
|
| 890 |
+
0x4F1000004F2,
|
| 891 |
+
0x4F3000004F4,
|
| 892 |
+
0x4F5000004F6,
|
| 893 |
+
0x4F7000004F8,
|
| 894 |
+
0x4F9000004FA,
|
| 895 |
+
0x4FB000004FC,
|
| 896 |
+
0x4FD000004FE,
|
| 897 |
+
0x4FF00000500,
|
| 898 |
+
0x50100000502,
|
| 899 |
+
0x50300000504,
|
| 900 |
+
0x50500000506,
|
| 901 |
+
0x50700000508,
|
| 902 |
+
0x5090000050A,
|
| 903 |
+
0x50B0000050C,
|
| 904 |
+
0x50D0000050E,
|
| 905 |
+
0x50F00000510,
|
| 906 |
+
0x51100000512,
|
| 907 |
+
0x51300000514,
|
| 908 |
+
0x51500000516,
|
| 909 |
+
0x51700000518,
|
| 910 |
+
0x5190000051A,
|
| 911 |
+
0x51B0000051C,
|
| 912 |
+
0x51D0000051E,
|
| 913 |
+
0x51F00000520,
|
| 914 |
+
0x52100000522,
|
| 915 |
+
0x52300000524,
|
| 916 |
+
0x52500000526,
|
| 917 |
+
0x52700000528,
|
| 918 |
+
0x5290000052A,
|
| 919 |
+
0x52B0000052C,
|
| 920 |
+
0x52D0000052E,
|
| 921 |
+
0x52F00000530,
|
| 922 |
+
0x5590000055A,
|
| 923 |
+
0x56000000587,
|
| 924 |
+
0x58800000589,
|
| 925 |
+
0x591000005BE,
|
| 926 |
+
0x5BF000005C0,
|
| 927 |
+
0x5C1000005C3,
|
| 928 |
+
0x5C4000005C6,
|
| 929 |
+
0x5C7000005C8,
|
| 930 |
+
0x5D0000005EB,
|
| 931 |
+
0x5EF000005F3,
|
| 932 |
+
0x6100000061B,
|
| 933 |
+
0x62000000640,
|
| 934 |
+
0x64100000660,
|
| 935 |
+
0x66E00000675,
|
| 936 |
+
0x679000006D4,
|
| 937 |
+
0x6D5000006DD,
|
| 938 |
+
0x6DF000006E9,
|
| 939 |
+
0x6EA000006F0,
|
| 940 |
+
0x6FA00000700,
|
| 941 |
+
0x7100000074B,
|
| 942 |
+
0x74D000007B2,
|
| 943 |
+
0x7C0000007F6,
|
| 944 |
+
0x7FD000007FE,
|
| 945 |
+
0x8000000082E,
|
| 946 |
+
0x8400000085C,
|
| 947 |
+
0x8600000086B,
|
| 948 |
+
0x87000000888,
|
| 949 |
+
0x88900000890,
|
| 950 |
+
0x897000008E2,
|
| 951 |
+
0x8E300000958,
|
| 952 |
+
0x96000000964,
|
| 953 |
+
0x96600000970,
|
| 954 |
+
0x97100000984,
|
| 955 |
+
0x9850000098D,
|
| 956 |
+
0x98F00000991,
|
| 957 |
+
0x993000009A9,
|
| 958 |
+
0x9AA000009B1,
|
| 959 |
+
0x9B2000009B3,
|
| 960 |
+
0x9B6000009BA,
|
| 961 |
+
0x9BC000009C5,
|
| 962 |
+
0x9C7000009C9,
|
| 963 |
+
0x9CB000009CF,
|
| 964 |
+
0x9D7000009D8,
|
| 965 |
+
0x9E0000009E4,
|
| 966 |
+
0x9E6000009F2,
|
| 967 |
+
0x9FC000009FD,
|
| 968 |
+
0x9FE000009FF,
|
| 969 |
+
0xA0100000A04,
|
| 970 |
+
0xA0500000A0B,
|
| 971 |
+
0xA0F00000A11,
|
| 972 |
+
0xA1300000A29,
|
| 973 |
+
0xA2A00000A31,
|
| 974 |
+
0xA3200000A33,
|
| 975 |
+
0xA3500000A36,
|
| 976 |
+
0xA3800000A3A,
|
| 977 |
+
0xA3C00000A3D,
|
| 978 |
+
0xA3E00000A43,
|
| 979 |
+
0xA4700000A49,
|
| 980 |
+
0xA4B00000A4E,
|
| 981 |
+
0xA5100000A52,
|
| 982 |
+
0xA5C00000A5D,
|
| 983 |
+
0xA6600000A76,
|
| 984 |
+
0xA8100000A84,
|
| 985 |
+
0xA8500000A8E,
|
| 986 |
+
0xA8F00000A92,
|
| 987 |
+
0xA9300000AA9,
|
| 988 |
+
0xAAA00000AB1,
|
| 989 |
+
0xAB200000AB4,
|
| 990 |
+
0xAB500000ABA,
|
| 991 |
+
0xABC00000AC6,
|
| 992 |
+
0xAC700000ACA,
|
| 993 |
+
0xACB00000ACE,
|
| 994 |
+
0xAD000000AD1,
|
| 995 |
+
0xAE000000AE4,
|
| 996 |
+
0xAE600000AF0,
|
| 997 |
+
0xAF900000B00,
|
| 998 |
+
0xB0100000B04,
|
| 999 |
+
0xB0500000B0D,
|
| 1000 |
+
0xB0F00000B11,
|
| 1001 |
+
0xB1300000B29,
|
| 1002 |
+
0xB2A00000B31,
|
| 1003 |
+
0xB3200000B34,
|
| 1004 |
+
0xB3500000B3A,
|
| 1005 |
+
0xB3C00000B45,
|
| 1006 |
+
0xB4700000B49,
|
| 1007 |
+
0xB4B00000B4E,
|
| 1008 |
+
0xB5500000B58,
|
| 1009 |
+
0xB5F00000B64,
|
| 1010 |
+
0xB6600000B70,
|
| 1011 |
+
0xB7100000B72,
|
| 1012 |
+
0xB8200000B84,
|
| 1013 |
+
0xB8500000B8B,
|
| 1014 |
+
0xB8E00000B91,
|
| 1015 |
+
0xB9200000B96,
|
| 1016 |
+
0xB9900000B9B,
|
| 1017 |
+
0xB9C00000B9D,
|
| 1018 |
+
0xB9E00000BA0,
|
| 1019 |
+
0xBA300000BA5,
|
| 1020 |
+
0xBA800000BAB,
|
| 1021 |
+
0xBAE00000BBA,
|
| 1022 |
+
0xBBE00000BC3,
|
| 1023 |
+
0xBC600000BC9,
|
| 1024 |
+
0xBCA00000BCE,
|
| 1025 |
+
0xBD000000BD1,
|
| 1026 |
+
0xBD700000BD8,
|
| 1027 |
+
0xBE600000BF0,
|
| 1028 |
+
0xC0000000C0D,
|
| 1029 |
+
0xC0E00000C11,
|
| 1030 |
+
0xC1200000C29,
|
| 1031 |
+
0xC2A00000C3A,
|
| 1032 |
+
0xC3C00000C45,
|
| 1033 |
+
0xC4600000C49,
|
| 1034 |
+
0xC4A00000C4E,
|
| 1035 |
+
0xC5500000C57,
|
| 1036 |
+
0xC5800000C5B,
|
| 1037 |
+
0xC5C00000C5E,
|
| 1038 |
+
0xC6000000C64,
|
| 1039 |
+
0xC6600000C70,
|
| 1040 |
+
0xC8000000C84,
|
| 1041 |
+
0xC8500000C8D,
|
| 1042 |
+
0xC8E00000C91,
|
| 1043 |
+
0xC9200000CA9,
|
| 1044 |
+
0xCAA00000CB4,
|
| 1045 |
+
0xCB500000CBA,
|
| 1046 |
+
0xCBC00000CC5,
|
| 1047 |
+
0xCC600000CC9,
|
| 1048 |
+
0xCCA00000CCE,
|
| 1049 |
+
0xCD500000CD7,
|
| 1050 |
+
0xCDC00000CDF,
|
| 1051 |
+
0xCE000000CE4,
|
| 1052 |
+
0xCE600000CF0,
|
| 1053 |
+
0xCF100000CF4,
|
| 1054 |
+
0xD0000000D0D,
|
| 1055 |
+
0xD0E00000D11,
|
| 1056 |
+
0xD1200000D45,
|
| 1057 |
+
0xD4600000D49,
|
| 1058 |
+
0xD4A00000D4F,
|
| 1059 |
+
0xD5400000D58,
|
| 1060 |
+
0xD5F00000D64,
|
| 1061 |
+
0xD6600000D70,
|
| 1062 |
+
0xD7A00000D80,
|
| 1063 |
+
0xD8100000D84,
|
| 1064 |
+
0xD8500000D97,
|
| 1065 |
+
0xD9A00000DB2,
|
| 1066 |
+
0xDB300000DBC,
|
| 1067 |
+
0xDBD00000DBE,
|
| 1068 |
+
0xDC000000DC7,
|
| 1069 |
+
0xDCA00000DCB,
|
| 1070 |
+
0xDCF00000DD5,
|
| 1071 |
+
0xDD600000DD7,
|
| 1072 |
+
0xDD800000DE0,
|
| 1073 |
+
0xDE600000DF0,
|
| 1074 |
+
0xDF200000DF4,
|
| 1075 |
+
0xE0100000E33,
|
| 1076 |
+
0xE3400000E3B,
|
| 1077 |
+
0xE4000000E4F,
|
| 1078 |
+
0xE5000000E5A,
|
| 1079 |
+
0xE8100000E83,
|
| 1080 |
+
0xE8400000E85,
|
| 1081 |
+
0xE8600000E8B,
|
| 1082 |
+
0xE8C00000EA4,
|
| 1083 |
+
0xEA500000EA6,
|
| 1084 |
+
0xEA700000EB3,
|
| 1085 |
+
0xEB400000EBE,
|
| 1086 |
+
0xEC000000EC5,
|
| 1087 |
+
0xEC600000EC7,
|
| 1088 |
+
0xEC800000ECF,
|
| 1089 |
+
0xED000000EDA,
|
| 1090 |
+
0xEDE00000EE0,
|
| 1091 |
+
0xF0000000F01,
|
| 1092 |
+
0xF0B00000F0C,
|
| 1093 |
+
0xF1800000F1A,
|
| 1094 |
+
0xF2000000F2A,
|
| 1095 |
+
0xF3500000F36,
|
| 1096 |
+
0xF3700000F38,
|
| 1097 |
+
0xF3900000F3A,
|
| 1098 |
+
0xF3E00000F43,
|
| 1099 |
+
0xF4400000F48,
|
| 1100 |
+
0xF4900000F4D,
|
| 1101 |
+
0xF4E00000F52,
|
| 1102 |
+
0xF5300000F57,
|
| 1103 |
+
0xF5800000F5C,
|
| 1104 |
+
0xF5D00000F69,
|
| 1105 |
+
0xF6A00000F6D,
|
| 1106 |
+
0xF7100000F73,
|
| 1107 |
+
0xF7400000F75,
|
| 1108 |
+
0xF7A00000F81,
|
| 1109 |
+
0xF8200000F85,
|
| 1110 |
+
0xF8600000F93,
|
| 1111 |
+
0xF9400000F98,
|
| 1112 |
+
0xF9900000F9D,
|
| 1113 |
+
0xF9E00000FA2,
|
| 1114 |
+
0xFA300000FA7,
|
| 1115 |
+
0xFA800000FAC,
|
| 1116 |
+
0xFAD00000FB9,
|
| 1117 |
+
0xFBA00000FBD,
|
| 1118 |
+
0xFC600000FC7,
|
| 1119 |
+
0x10000000104A,
|
| 1120 |
+
0x10500000109E,
|
| 1121 |
+
0x10D0000010FB,
|
| 1122 |
+
0x10FD00001100,
|
| 1123 |
+
0x120000001249,
|
| 1124 |
+
0x124A0000124E,
|
| 1125 |
+
0x125000001257,
|
| 1126 |
+
0x125800001259,
|
| 1127 |
+
0x125A0000125E,
|
| 1128 |
+
0x126000001289,
|
| 1129 |
+
0x128A0000128E,
|
| 1130 |
+
0x1290000012B1,
|
| 1131 |
+
0x12B2000012B6,
|
| 1132 |
+
0x12B8000012BF,
|
| 1133 |
+
0x12C0000012C1,
|
| 1134 |
+
0x12C2000012C6,
|
| 1135 |
+
0x12C8000012D7,
|
| 1136 |
+
0x12D800001311,
|
| 1137 |
+
0x131200001316,
|
| 1138 |
+
0x13180000135B,
|
| 1139 |
+
0x135D00001360,
|
| 1140 |
+
0x138000001390,
|
| 1141 |
+
0x13A0000013F6,
|
| 1142 |
+
0x14010000166D,
|
| 1143 |
+
0x166F00001680,
|
| 1144 |
+
0x16810000169B,
|
| 1145 |
+
0x16A0000016EB,
|
| 1146 |
+
0x16F1000016F9,
|
| 1147 |
+
0x170000001716,
|
| 1148 |
+
0x171F00001735,
|
| 1149 |
+
0x174000001754,
|
| 1150 |
+
0x17600000176D,
|
| 1151 |
+
0x176E00001771,
|
| 1152 |
+
0x177200001774,
|
| 1153 |
+
0x1780000017B4,
|
| 1154 |
+
0x17B6000017D4,
|
| 1155 |
+
0x17D7000017D8,
|
| 1156 |
+
0x17DC000017DE,
|
| 1157 |
+
0x17E0000017EA,
|
| 1158 |
+
0x18100000181A,
|
| 1159 |
+
0x182000001879,
|
| 1160 |
+
0x1880000018AB,
|
| 1161 |
+
0x18B0000018F6,
|
| 1162 |
+
0x19000000191F,
|
| 1163 |
+
0x19200000192C,
|
| 1164 |
+
0x19300000193C,
|
| 1165 |
+
0x19460000196E,
|
| 1166 |
+
0x197000001975,
|
| 1167 |
+
0x1980000019AC,
|
| 1168 |
+
0x19B0000019CA,
|
| 1169 |
+
0x19D0000019DA,
|
| 1170 |
+
0x1A0000001A1C,
|
| 1171 |
+
0x1A2000001A5F,
|
| 1172 |
+
0x1A6000001A7D,
|
| 1173 |
+
0x1A7F00001A8A,
|
| 1174 |
+
0x1A9000001A9A,
|
| 1175 |
+
0x1AA700001AA8,
|
| 1176 |
+
0x1AB000001ABE,
|
| 1177 |
+
0x1ABF00001ADE,
|
| 1178 |
+
0x1AE000001AEC,
|
| 1179 |
+
0x1B0000001B4D,
|
| 1180 |
+
0x1B5000001B5A,
|
| 1181 |
+
0x1B6B00001B74,
|
| 1182 |
+
0x1B8000001BF4,
|
| 1183 |
+
0x1C0000001C38,
|
| 1184 |
+
0x1C4000001C4A,
|
| 1185 |
+
0x1C4D00001C7E,
|
| 1186 |
+
0x1C8A00001C8B,
|
| 1187 |
+
0x1CD000001CD3,
|
| 1188 |
+
0x1CD400001CFB,
|
| 1189 |
+
0x1D0000001D2C,
|
| 1190 |
+
0x1D2F00001D30,
|
| 1191 |
+
0x1D3B00001D3C,
|
| 1192 |
+
0x1D4E00001D4F,
|
| 1193 |
+
0x1D6B00001D78,
|
| 1194 |
+
0x1D7900001D9B,
|
| 1195 |
+
0x1DC000001E00,
|
| 1196 |
+
0x1E0100001E02,
|
| 1197 |
+
0x1E0300001E04,
|
| 1198 |
+
0x1E0500001E06,
|
| 1199 |
+
0x1E0700001E08,
|
| 1200 |
+
0x1E0900001E0A,
|
| 1201 |
+
0x1E0B00001E0C,
|
| 1202 |
+
0x1E0D00001E0E,
|
| 1203 |
+
0x1E0F00001E10,
|
| 1204 |
+
0x1E1100001E12,
|
| 1205 |
+
0x1E1300001E14,
|
| 1206 |
+
0x1E1500001E16,
|
| 1207 |
+
0x1E1700001E18,
|
| 1208 |
+
0x1E1900001E1A,
|
| 1209 |
+
0x1E1B00001E1C,
|
| 1210 |
+
0x1E1D00001E1E,
|
| 1211 |
+
0x1E1F00001E20,
|
| 1212 |
+
0x1E2100001E22,
|
| 1213 |
+
0x1E2300001E24,
|
| 1214 |
+
0x1E2500001E26,
|
| 1215 |
+
0x1E2700001E28,
|
| 1216 |
+
0x1E2900001E2A,
|
| 1217 |
+
0x1E2B00001E2C,
|
| 1218 |
+
0x1E2D00001E2E,
|
| 1219 |
+
0x1E2F00001E30,
|
| 1220 |
+
0x1E3100001E32,
|
| 1221 |
+
0x1E3300001E34,
|
| 1222 |
+
0x1E3500001E36,
|
| 1223 |
+
0x1E3700001E38,
|
| 1224 |
+
0x1E3900001E3A,
|
| 1225 |
+
0x1E3B00001E3C,
|
| 1226 |
+
0x1E3D00001E3E,
|
| 1227 |
+
0x1E3F00001E40,
|
| 1228 |
+
0x1E4100001E42,
|
| 1229 |
+
0x1E4300001E44,
|
| 1230 |
+
0x1E4500001E46,
|
| 1231 |
+
0x1E4700001E48,
|
| 1232 |
+
0x1E4900001E4A,
|
| 1233 |
+
0x1E4B00001E4C,
|
| 1234 |
+
0x1E4D00001E4E,
|
| 1235 |
+
0x1E4F00001E50,
|
| 1236 |
+
0x1E5100001E52,
|
| 1237 |
+
0x1E5300001E54,
|
| 1238 |
+
0x1E5500001E56,
|
| 1239 |
+
0x1E5700001E58,
|
| 1240 |
+
0x1E5900001E5A,
|
| 1241 |
+
0x1E5B00001E5C,
|
| 1242 |
+
0x1E5D00001E5E,
|
| 1243 |
+
0x1E5F00001E60,
|
| 1244 |
+
0x1E6100001E62,
|
| 1245 |
+
0x1E6300001E64,
|
| 1246 |
+
0x1E6500001E66,
|
| 1247 |
+
0x1E6700001E68,
|
| 1248 |
+
0x1E6900001E6A,
|
| 1249 |
+
0x1E6B00001E6C,
|
| 1250 |
+
0x1E6D00001E6E,
|
| 1251 |
+
0x1E6F00001E70,
|
| 1252 |
+
0x1E7100001E72,
|
| 1253 |
+
0x1E7300001E74,
|
| 1254 |
+
0x1E7500001E76,
|
| 1255 |
+
0x1E7700001E78,
|
| 1256 |
+
0x1E7900001E7A,
|
| 1257 |
+
0x1E7B00001E7C,
|
| 1258 |
+
0x1E7D00001E7E,
|
| 1259 |
+
0x1E7F00001E80,
|
| 1260 |
+
0x1E8100001E82,
|
| 1261 |
+
0x1E8300001E84,
|
| 1262 |
+
0x1E8500001E86,
|
| 1263 |
+
0x1E8700001E88,
|
| 1264 |
+
0x1E8900001E8A,
|
| 1265 |
+
0x1E8B00001E8C,
|
| 1266 |
+
0x1E8D00001E8E,
|
| 1267 |
+
0x1E8F00001E90,
|
| 1268 |
+
0x1E9100001E92,
|
| 1269 |
+
0x1E9300001E94,
|
| 1270 |
+
0x1E9500001E9A,
|
| 1271 |
+
0x1E9C00001E9E,
|
| 1272 |
+
0x1E9F00001EA0,
|
| 1273 |
+
0x1EA100001EA2,
|
| 1274 |
+
0x1EA300001EA4,
|
| 1275 |
+
0x1EA500001EA6,
|
| 1276 |
+
0x1EA700001EA8,
|
| 1277 |
+
0x1EA900001EAA,
|
| 1278 |
+
0x1EAB00001EAC,
|
| 1279 |
+
0x1EAD00001EAE,
|
| 1280 |
+
0x1EAF00001EB0,
|
| 1281 |
+
0x1EB100001EB2,
|
| 1282 |
+
0x1EB300001EB4,
|
| 1283 |
+
0x1EB500001EB6,
|
| 1284 |
+
0x1EB700001EB8,
|
| 1285 |
+
0x1EB900001EBA,
|
| 1286 |
+
0x1EBB00001EBC,
|
| 1287 |
+
0x1EBD00001EBE,
|
| 1288 |
+
0x1EBF00001EC0,
|
| 1289 |
+
0x1EC100001EC2,
|
| 1290 |
+
0x1EC300001EC4,
|
| 1291 |
+
0x1EC500001EC6,
|
| 1292 |
+
0x1EC700001EC8,
|
| 1293 |
+
0x1EC900001ECA,
|
| 1294 |
+
0x1ECB00001ECC,
|
| 1295 |
+
0x1ECD00001ECE,
|
| 1296 |
+
0x1ECF00001ED0,
|
| 1297 |
+
0x1ED100001ED2,
|
| 1298 |
+
0x1ED300001ED4,
|
| 1299 |
+
0x1ED500001ED6,
|
| 1300 |
+
0x1ED700001ED8,
|
| 1301 |
+
0x1ED900001EDA,
|
| 1302 |
+
0x1EDB00001EDC,
|
| 1303 |
+
0x1EDD00001EDE,
|
| 1304 |
+
0x1EDF00001EE0,
|
| 1305 |
+
0x1EE100001EE2,
|
| 1306 |
+
0x1EE300001EE4,
|
| 1307 |
+
0x1EE500001EE6,
|
| 1308 |
+
0x1EE700001EE8,
|
| 1309 |
+
0x1EE900001EEA,
|
| 1310 |
+
0x1EEB00001EEC,
|
| 1311 |
+
0x1EED00001EEE,
|
| 1312 |
+
0x1EEF00001EF0,
|
| 1313 |
+
0x1EF100001EF2,
|
| 1314 |
+
0x1EF300001EF4,
|
| 1315 |
+
0x1EF500001EF6,
|
| 1316 |
+
0x1EF700001EF8,
|
| 1317 |
+
0x1EF900001EFA,
|
| 1318 |
+
0x1EFB00001EFC,
|
| 1319 |
+
0x1EFD00001EFE,
|
| 1320 |
+
0x1EFF00001F08,
|
| 1321 |
+
0x1F1000001F16,
|
| 1322 |
+
0x1F2000001F28,
|
| 1323 |
+
0x1F3000001F38,
|
| 1324 |
+
0x1F4000001F46,
|
| 1325 |
+
0x1F5000001F58,
|
| 1326 |
+
0x1F6000001F68,
|
| 1327 |
+
0x1F7000001F71,
|
| 1328 |
+
0x1F7200001F73,
|
| 1329 |
+
0x1F7400001F75,
|
| 1330 |
+
0x1F7600001F77,
|
| 1331 |
+
0x1F7800001F79,
|
| 1332 |
+
0x1F7A00001F7B,
|
| 1333 |
+
0x1F7C00001F7D,
|
| 1334 |
+
0x1FB000001FB2,
|
| 1335 |
+
0x1FB600001FB7,
|
| 1336 |
+
0x1FC600001FC7,
|
| 1337 |
+
0x1FD000001FD3,
|
| 1338 |
+
0x1FD600001FD8,
|
| 1339 |
+
0x1FE000001FE3,
|
| 1340 |
+
0x1FE400001FE8,
|
| 1341 |
+
0x1FF600001FF7,
|
| 1342 |
+
0x214E0000214F,
|
| 1343 |
+
0x218400002185,
|
| 1344 |
+
0x2C3000002C60,
|
| 1345 |
+
0x2C6100002C62,
|
| 1346 |
+
0x2C6500002C67,
|
| 1347 |
+
0x2C6800002C69,
|
| 1348 |
+
0x2C6A00002C6B,
|
| 1349 |
+
0x2C6C00002C6D,
|
| 1350 |
+
0x2C7100002C72,
|
| 1351 |
+
0x2C7300002C75,
|
| 1352 |
+
0x2C7600002C7C,
|
| 1353 |
+
0x2C8100002C82,
|
| 1354 |
+
0x2C8300002C84,
|
| 1355 |
+
0x2C8500002C86,
|
| 1356 |
+
0x2C8700002C88,
|
| 1357 |
+
0x2C8900002C8A,
|
| 1358 |
+
0x2C8B00002C8C,
|
| 1359 |
+
0x2C8D00002C8E,
|
| 1360 |
+
0x2C8F00002C90,
|
| 1361 |
+
0x2C9100002C92,
|
| 1362 |
+
0x2C9300002C94,
|
| 1363 |
+
0x2C9500002C96,
|
| 1364 |
+
0x2C9700002C98,
|
| 1365 |
+
0x2C9900002C9A,
|
| 1366 |
+
0x2C9B00002C9C,
|
| 1367 |
+
0x2C9D00002C9E,
|
| 1368 |
+
0x2C9F00002CA0,
|
| 1369 |
+
0x2CA100002CA2,
|
| 1370 |
+
0x2CA300002CA4,
|
| 1371 |
+
0x2CA500002CA6,
|
| 1372 |
+
0x2CA700002CA8,
|
| 1373 |
+
0x2CA900002CAA,
|
| 1374 |
+
0x2CAB00002CAC,
|
| 1375 |
+
0x2CAD00002CAE,
|
| 1376 |
+
0x2CAF00002CB0,
|
| 1377 |
+
0x2CB100002CB2,
|
| 1378 |
+
0x2CB300002CB4,
|
| 1379 |
+
0x2CB500002CB6,
|
| 1380 |
+
0x2CB700002CB8,
|
| 1381 |
+
0x2CB900002CBA,
|
| 1382 |
+
0x2CBB00002CBC,
|
| 1383 |
+
0x2CBD00002CBE,
|
| 1384 |
+
0x2CBF00002CC0,
|
| 1385 |
+
0x2CC100002CC2,
|
| 1386 |
+
0x2CC300002CC4,
|
| 1387 |
+
0x2CC500002CC6,
|
| 1388 |
+
0x2CC700002CC8,
|
| 1389 |
+
0x2CC900002CCA,
|
| 1390 |
+
0x2CCB00002CCC,
|
| 1391 |
+
0x2CCD00002CCE,
|
| 1392 |
+
0x2CCF00002CD0,
|
| 1393 |
+
0x2CD100002CD2,
|
| 1394 |
+
0x2CD300002CD4,
|
| 1395 |
+
0x2CD500002CD6,
|
| 1396 |
+
0x2CD700002CD8,
|
| 1397 |
+
0x2CD900002CDA,
|
| 1398 |
+
0x2CDB00002CDC,
|
| 1399 |
+
0x2CDD00002CDE,
|
| 1400 |
+
0x2CDF00002CE0,
|
| 1401 |
+
0x2CE100002CE2,
|
| 1402 |
+
0x2CE300002CE5,
|
| 1403 |
+
0x2CEC00002CED,
|
| 1404 |
+
0x2CEE00002CF2,
|
| 1405 |
+
0x2CF300002CF4,
|
| 1406 |
+
0x2D0000002D26,
|
| 1407 |
+
0x2D2700002D28,
|
| 1408 |
+
0x2D2D00002D2E,
|
| 1409 |
+
0x2D3000002D68,
|
| 1410 |
+
0x2D7F00002D97,
|
| 1411 |
+
0x2DA000002DA7,
|
| 1412 |
+
0x2DA800002DAF,
|
| 1413 |
+
0x2DB000002DB7,
|
| 1414 |
+
0x2DB800002DBF,
|
| 1415 |
+
0x2DC000002DC7,
|
| 1416 |
+
0x2DC800002DCF,
|
| 1417 |
+
0x2DD000002DD7,
|
| 1418 |
+
0x2DD800002DDF,
|
| 1419 |
+
0x2DE000002E00,
|
| 1420 |
+
0x2E2F00002E30,
|
| 1421 |
+
0x300500003008,
|
| 1422 |
+
0x302A0000302E,
|
| 1423 |
+
0x303C0000303D,
|
| 1424 |
+
0x304100003097,
|
| 1425 |
+
0x30990000309B,
|
| 1426 |
+
0x309D0000309F,
|
| 1427 |
+
0x30A1000030FB,
|
| 1428 |
+
0x30FC000030FF,
|
| 1429 |
+
0x310500003130,
|
| 1430 |
+
0x31A0000031C0,
|
| 1431 |
+
0x31F000003200,
|
| 1432 |
+
0x340000004DC0,
|
| 1433 |
+
0x4E000000A48D,
|
| 1434 |
+
0xA4D00000A4FE,
|
| 1435 |
+
0xA5000000A60D,
|
| 1436 |
+
0xA6100000A62C,
|
| 1437 |
+
0xA6410000A642,
|
| 1438 |
+
0xA6430000A644,
|
| 1439 |
+
0xA6450000A646,
|
| 1440 |
+
0xA6470000A648,
|
| 1441 |
+
0xA6490000A64A,
|
| 1442 |
+
0xA64B0000A64C,
|
| 1443 |
+
0xA64D0000A64E,
|
| 1444 |
+
0xA64F0000A650,
|
| 1445 |
+
0xA6510000A652,
|
| 1446 |
+
0xA6530000A654,
|
| 1447 |
+
0xA6550000A656,
|
| 1448 |
+
0xA6570000A658,
|
| 1449 |
+
0xA6590000A65A,
|
| 1450 |
+
0xA65B0000A65C,
|
| 1451 |
+
0xA65D0000A65E,
|
| 1452 |
+
0xA65F0000A660,
|
| 1453 |
+
0xA6610000A662,
|
| 1454 |
+
0xA6630000A664,
|
| 1455 |
+
0xA6650000A666,
|
| 1456 |
+
0xA6670000A668,
|
| 1457 |
+
0xA6690000A66A,
|
| 1458 |
+
0xA66B0000A66C,
|
| 1459 |
+
0xA66D0000A670,
|
| 1460 |
+
0xA6740000A67E,
|
| 1461 |
+
0xA67F0000A680,
|
| 1462 |
+
0xA6810000A682,
|
| 1463 |
+
0xA6830000A684,
|
| 1464 |
+
0xA6850000A686,
|
| 1465 |
+
0xA6870000A688,
|
| 1466 |
+
0xA6890000A68A,
|
| 1467 |
+
0xA68B0000A68C,
|
| 1468 |
+
0xA68D0000A68E,
|
| 1469 |
+
0xA68F0000A690,
|
| 1470 |
+
0xA6910000A692,
|
| 1471 |
+
0xA6930000A694,
|
| 1472 |
+
0xA6950000A696,
|
| 1473 |
+
0xA6970000A698,
|
| 1474 |
+
0xA6990000A69A,
|
| 1475 |
+
0xA69B0000A69C,
|
| 1476 |
+
0xA69E0000A6E6,
|
| 1477 |
+
0xA6F00000A6F2,
|
| 1478 |
+
0xA7170000A720,
|
| 1479 |
+
0xA7230000A724,
|
| 1480 |
+
0xA7250000A726,
|
| 1481 |
+
0xA7270000A728,
|
| 1482 |
+
0xA7290000A72A,
|
| 1483 |
+
0xA72B0000A72C,
|
| 1484 |
+
0xA72D0000A72E,
|
| 1485 |
+
0xA72F0000A732,
|
| 1486 |
+
0xA7330000A734,
|
| 1487 |
+
0xA7350000A736,
|
| 1488 |
+
0xA7370000A738,
|
| 1489 |
+
0xA7390000A73A,
|
| 1490 |
+
0xA73B0000A73C,
|
| 1491 |
+
0xA73D0000A73E,
|
| 1492 |
+
0xA73F0000A740,
|
| 1493 |
+
0xA7410000A742,
|
| 1494 |
+
0xA7430000A744,
|
| 1495 |
+
0xA7450000A746,
|
| 1496 |
+
0xA7470000A748,
|
| 1497 |
+
0xA7490000A74A,
|
| 1498 |
+
0xA74B0000A74C,
|
| 1499 |
+
0xA74D0000A74E,
|
| 1500 |
+
0xA74F0000A750,
|
| 1501 |
+
0xA7510000A752,
|
| 1502 |
+
0xA7530000A754,
|
| 1503 |
+
0xA7550000A756,
|
| 1504 |
+
0xA7570000A758,
|
| 1505 |
+
0xA7590000A75A,
|
| 1506 |
+
0xA75B0000A75C,
|
| 1507 |
+
0xA75D0000A75E,
|
| 1508 |
+
0xA75F0000A760,
|
| 1509 |
+
0xA7610000A762,
|
| 1510 |
+
0xA7630000A764,
|
| 1511 |
+
0xA7650000A766,
|
| 1512 |
+
0xA7670000A768,
|
| 1513 |
+
0xA7690000A76A,
|
| 1514 |
+
0xA76B0000A76C,
|
| 1515 |
+
0xA76D0000A76E,
|
| 1516 |
+
0xA76F0000A770,
|
| 1517 |
+
0xA7710000A779,
|
| 1518 |
+
0xA77A0000A77B,
|
| 1519 |
+
0xA77C0000A77D,
|
| 1520 |
+
0xA77F0000A780,
|
| 1521 |
+
0xA7810000A782,
|
| 1522 |
+
0xA7830000A784,
|
| 1523 |
+
0xA7850000A786,
|
| 1524 |
+
0xA7870000A789,
|
| 1525 |
+
0xA78C0000A78D,
|
| 1526 |
+
0xA78E0000A790,
|
| 1527 |
+
0xA7910000A792,
|
| 1528 |
+
0xA7930000A796,
|
| 1529 |
+
0xA7970000A798,
|
| 1530 |
+
0xA7990000A79A,
|
| 1531 |
+
0xA79B0000A79C,
|
| 1532 |
+
0xA79D0000A79E,
|
| 1533 |
+
0xA79F0000A7A0,
|
| 1534 |
+
0xA7A10000A7A2,
|
| 1535 |
+
0xA7A30000A7A4,
|
| 1536 |
+
0xA7A50000A7A6,
|
| 1537 |
+
0xA7A70000A7A8,
|
| 1538 |
+
0xA7A90000A7AA,
|
| 1539 |
+
0xA7AF0000A7B0,
|
| 1540 |
+
0xA7B50000A7B6,
|
| 1541 |
+
0xA7B70000A7B8,
|
| 1542 |
+
0xA7B90000A7BA,
|
| 1543 |
+
0xA7BB0000A7BC,
|
| 1544 |
+
0xA7BD0000A7BE,
|
| 1545 |
+
0xA7BF0000A7C0,
|
| 1546 |
+
0xA7C10000A7C2,
|
| 1547 |
+
0xA7C30000A7C4,
|
| 1548 |
+
0xA7C80000A7C9,
|
| 1549 |
+
0xA7CA0000A7CB,
|
| 1550 |
+
0xA7CD0000A7CE,
|
| 1551 |
+
0xA7CF0000A7D0,
|
| 1552 |
+
0xA7D10000A7D2,
|
| 1553 |
+
0xA7D30000A7D4,
|
| 1554 |
+
0xA7D50000A7D6,
|
| 1555 |
+
0xA7D70000A7D8,
|
| 1556 |
+
0xA7D90000A7DA,
|
| 1557 |
+
0xA7DB0000A7DC,
|
| 1558 |
+
0xA7F60000A7F8,
|
| 1559 |
+
0xA7FA0000A828,
|
| 1560 |
+
0xA82C0000A82D,
|
| 1561 |
+
0xA8400000A874,
|
| 1562 |
+
0xA8800000A8C6,
|
| 1563 |
+
0xA8D00000A8DA,
|
| 1564 |
+
0xA8E00000A8F8,
|
| 1565 |
+
0xA8FB0000A8FC,
|
| 1566 |
+
0xA8FD0000A92E,
|
| 1567 |
+
0xA9300000A954,
|
| 1568 |
+
0xA9800000A9C1,
|
| 1569 |
+
0xA9CF0000A9DA,
|
| 1570 |
+
0xA9E00000A9FF,
|
| 1571 |
+
0xAA000000AA37,
|
| 1572 |
+
0xAA400000AA4E,
|
| 1573 |
+
0xAA500000AA5A,
|
| 1574 |
+
0xAA600000AA77,
|
| 1575 |
+
0xAA7A0000AAC3,
|
| 1576 |
+
0xAADB0000AADE,
|
| 1577 |
+
0xAAE00000AAF0,
|
| 1578 |
+
0xAAF20000AAF7,
|
| 1579 |
+
0xAB010000AB07,
|
| 1580 |
+
0xAB090000AB0F,
|
| 1581 |
+
0xAB110000AB17,
|
| 1582 |
+
0xAB200000AB27,
|
| 1583 |
+
0xAB280000AB2F,
|
| 1584 |
+
0xAB300000AB5B,
|
| 1585 |
+
0xAB600000AB69,
|
| 1586 |
+
0xABC00000ABEB,
|
| 1587 |
+
0xABEC0000ABEE,
|
| 1588 |
+
0xABF00000ABFA,
|
| 1589 |
+
0xAC000000D7A4,
|
| 1590 |
+
0xFA0E0000FA10,
|
| 1591 |
+
0xFA110000FA12,
|
| 1592 |
+
0xFA130000FA15,
|
| 1593 |
+
0xFA1F0000FA20,
|
| 1594 |
+
0xFA210000FA22,
|
| 1595 |
+
0xFA230000FA25,
|
| 1596 |
+
0xFA270000FA2A,
|
| 1597 |
+
0xFB1E0000FB1F,
|
| 1598 |
+
0xFE200000FE30,
|
| 1599 |
+
0xFE730000FE74,
|
| 1600 |
+
0x100000001000C,
|
| 1601 |
+
0x1000D00010027,
|
| 1602 |
+
0x100280001003B,
|
| 1603 |
+
0x1003C0001003E,
|
| 1604 |
+
0x1003F0001004E,
|
| 1605 |
+
0x100500001005E,
|
| 1606 |
+
0x10080000100FB,
|
| 1607 |
+
0x101FD000101FE,
|
| 1608 |
+
0x102800001029D,
|
| 1609 |
+
0x102A0000102D1,
|
| 1610 |
+
0x102E0000102E1,
|
| 1611 |
+
0x1030000010320,
|
| 1612 |
+
0x1032D00010341,
|
| 1613 |
+
0x103420001034A,
|
| 1614 |
+
0x103500001037B,
|
| 1615 |
+
0x103800001039E,
|
| 1616 |
+
0x103A0000103C4,
|
| 1617 |
+
0x103C8000103D0,
|
| 1618 |
+
0x104280001049E,
|
| 1619 |
+
0x104A0000104AA,
|
| 1620 |
+
0x104D8000104FC,
|
| 1621 |
+
0x1050000010528,
|
| 1622 |
+
0x1053000010564,
|
| 1623 |
+
0x10597000105A2,
|
| 1624 |
+
0x105A3000105B2,
|
| 1625 |
+
0x105B3000105BA,
|
| 1626 |
+
0x105BB000105BD,
|
| 1627 |
+
0x105C0000105F4,
|
| 1628 |
+
0x1060000010737,
|
| 1629 |
+
0x1074000010756,
|
| 1630 |
+
0x1076000010768,
|
| 1631 |
+
0x1078000010781,
|
| 1632 |
+
0x1080000010806,
|
| 1633 |
+
0x1080800010809,
|
| 1634 |
+
0x1080A00010836,
|
| 1635 |
+
0x1083700010839,
|
| 1636 |
+
0x1083C0001083D,
|
| 1637 |
+
0x1083F00010856,
|
| 1638 |
+
0x1086000010877,
|
| 1639 |
+
0x108800001089F,
|
| 1640 |
+
0x108E0000108F3,
|
| 1641 |
+
0x108F4000108F6,
|
| 1642 |
+
0x1090000010916,
|
| 1643 |
+
0x109200001093A,
|
| 1644 |
+
0x109400001095A,
|
| 1645 |
+
0x10980000109B8,
|
| 1646 |
+
0x109BE000109C0,
|
| 1647 |
+
0x10A0000010A04,
|
| 1648 |
+
0x10A0500010A07,
|
| 1649 |
+
0x10A0C00010A14,
|
| 1650 |
+
0x10A1500010A18,
|
| 1651 |
+
0x10A1900010A36,
|
| 1652 |
+
0x10A3800010A3B,
|
| 1653 |
+
0x10A3F00010A40,
|
| 1654 |
+
0x10A6000010A7D,
|
| 1655 |
+
0x10A8000010A9D,
|
| 1656 |
+
0x10AC000010AC8,
|
| 1657 |
+
0x10AC900010AE7,
|
| 1658 |
+
0x10B0000010B36,
|
| 1659 |
+
0x10B4000010B56,
|
| 1660 |
+
0x10B6000010B73,
|
| 1661 |
+
0x10B8000010B92,
|
| 1662 |
+
0x10C0000010C49,
|
| 1663 |
+
0x10CC000010CF3,
|
| 1664 |
+
0x10D0000010D28,
|
| 1665 |
+
0x10D3000010D3A,
|
| 1666 |
+
0x10D4000010D50,
|
| 1667 |
+
0x10D6900010D6E,
|
| 1668 |
+
0x10D6F00010D86,
|
| 1669 |
+
0x10E8000010EAA,
|
| 1670 |
+
0x10EAB00010EAD,
|
| 1671 |
+
0x10EB000010EB2,
|
| 1672 |
+
0x10EC200010EC8,
|
| 1673 |
+
0x10EFA00010F1D,
|
| 1674 |
+
0x10F2700010F28,
|
| 1675 |
+
0x10F3000010F51,
|
| 1676 |
+
0x10F7000010F86,
|
| 1677 |
+
0x10FB000010FC5,
|
| 1678 |
+
0x10FE000010FF7,
|
| 1679 |
+
0x1100000011047,
|
| 1680 |
+
0x1106600011076,
|
| 1681 |
+
0x1107F000110BB,
|
| 1682 |
+
0x110C2000110C3,
|
| 1683 |
+
0x110D0000110E9,
|
| 1684 |
+
0x110F0000110FA,
|
| 1685 |
+
0x1110000011135,
|
| 1686 |
+
0x1113600011140,
|
| 1687 |
+
0x1114400011148,
|
| 1688 |
+
0x1115000011174,
|
| 1689 |
+
0x1117600011177,
|
| 1690 |
+
0x11180000111C5,
|
| 1691 |
+
0x111C9000111CD,
|
| 1692 |
+
0x111CE000111DB,
|
| 1693 |
+
0x111DC000111DD,
|
| 1694 |
+
0x1120000011212,
|
| 1695 |
+
0x1121300011238,
|
| 1696 |
+
0x1123E00011242,
|
| 1697 |
+
0x1128000011287,
|
| 1698 |
+
0x1128800011289,
|
| 1699 |
+
0x1128A0001128E,
|
| 1700 |
+
0x1128F0001129E,
|
| 1701 |
+
0x1129F000112A9,
|
| 1702 |
+
0x112B0000112EB,
|
| 1703 |
+
0x112F0000112FA,
|
| 1704 |
+
0x1130000011304,
|
| 1705 |
+
0x113050001130D,
|
| 1706 |
+
0x1130F00011311,
|
| 1707 |
+
0x1131300011329,
|
| 1708 |
+
0x1132A00011331,
|
| 1709 |
+
0x1133200011334,
|
| 1710 |
+
0x113350001133A,
|
| 1711 |
+
0x1133B00011345,
|
| 1712 |
+
0x1134700011349,
|
| 1713 |
+
0x1134B0001134E,
|
| 1714 |
+
0x1135000011351,
|
| 1715 |
+
0x1135700011358,
|
| 1716 |
+
0x1135D00011364,
|
| 1717 |
+
0x113660001136D,
|
| 1718 |
+
0x1137000011375,
|
| 1719 |
+
0x113800001138A,
|
| 1720 |
+
0x1138B0001138C,
|
| 1721 |
+
0x1138E0001138F,
|
| 1722 |
+
0x11390000113B6,
|
| 1723 |
+
0x113B7000113C1,
|
| 1724 |
+
0x113C2000113C3,
|
| 1725 |
+
0x113C5000113C6,
|
| 1726 |
+
0x113C7000113CB,
|
| 1727 |
+
0x113CC000113D4,
|
| 1728 |
+
0x113E1000113E3,
|
| 1729 |
+
0x114000001144B,
|
| 1730 |
+
0x114500001145A,
|
| 1731 |
+
0x1145E00011462,
|
| 1732 |
+
0x11480000114C6,
|
| 1733 |
+
0x114C7000114C8,
|
| 1734 |
+
0x114D0000114DA,
|
| 1735 |
+
0x11580000115B6,
|
| 1736 |
+
0x115B8000115C1,
|
| 1737 |
+
0x115D8000115DE,
|
| 1738 |
+
0x1160000011641,
|
| 1739 |
+
0x1164400011645,
|
| 1740 |
+
0x116500001165A,
|
| 1741 |
+
0x11680000116B9,
|
| 1742 |
+
0x116C0000116CA,
|
| 1743 |
+
0x116D0000116E4,
|
| 1744 |
+
0x117000001171B,
|
| 1745 |
+
0x1171D0001172C,
|
| 1746 |
+
0x117300001173A,
|
| 1747 |
+
0x1174000011747,
|
| 1748 |
+
0x118000001183B,
|
| 1749 |
+
0x118C0000118EA,
|
| 1750 |
+
0x118FF00011907,
|
| 1751 |
+
0x119090001190A,
|
| 1752 |
+
0x1190C00011914,
|
| 1753 |
+
0x1191500011917,
|
| 1754 |
+
0x1191800011936,
|
| 1755 |
+
0x1193700011939,
|
| 1756 |
+
0x1193B00011944,
|
| 1757 |
+
0x119500001195A,
|
| 1758 |
+
0x119A0000119A8,
|
| 1759 |
+
0x119AA000119D8,
|
| 1760 |
+
0x119DA000119E2,
|
| 1761 |
+
0x119E3000119E5,
|
| 1762 |
+
0x11A0000011A3F,
|
| 1763 |
+
0x11A4700011A48,
|
| 1764 |
+
0x11A5000011A9A,
|
| 1765 |
+
0x11A9D00011A9E,
|
| 1766 |
+
0x11AB000011AF9,
|
| 1767 |
+
0x11B6000011B68,
|
| 1768 |
+
0x11BC000011BE1,
|
| 1769 |
+
0x11BF000011BFA,
|
| 1770 |
+
0x11C0000011C09,
|
| 1771 |
+
0x11C0A00011C37,
|
| 1772 |
+
0x11C3800011C41,
|
| 1773 |
+
0x11C5000011C5A,
|
| 1774 |
+
0x11C7200011C90,
|
| 1775 |
+
0x11C9200011CA8,
|
| 1776 |
+
0x11CA900011CB7,
|
| 1777 |
+
0x11D0000011D07,
|
| 1778 |
+
0x11D0800011D0A,
|
| 1779 |
+
0x11D0B00011D37,
|
| 1780 |
+
0x11D3A00011D3B,
|
| 1781 |
+
0x11D3C00011D3E,
|
| 1782 |
+
0x11D3F00011D48,
|
| 1783 |
+
0x11D5000011D5A,
|
| 1784 |
+
0x11D6000011D66,
|
| 1785 |
+
0x11D6700011D69,
|
| 1786 |
+
0x11D6A00011D8F,
|
| 1787 |
+
0x11D9000011D92,
|
| 1788 |
+
0x11D9300011D99,
|
| 1789 |
+
0x11DA000011DAA,
|
| 1790 |
+
0x11DB000011DDC,
|
| 1791 |
+
0x11DE000011DEA,
|
| 1792 |
+
0x11EE000011EF7,
|
| 1793 |
+
0x11F0000011F11,
|
| 1794 |
+
0x11F1200011F3B,
|
| 1795 |
+
0x11F3E00011F43,
|
| 1796 |
+
0x11F5000011F5B,
|
| 1797 |
+
0x11FB000011FB1,
|
| 1798 |
+
0x120000001239A,
|
| 1799 |
+
0x1248000012544,
|
| 1800 |
+
0x12F9000012FF1,
|
| 1801 |
+
0x1300000013430,
|
| 1802 |
+
0x1344000013456,
|
| 1803 |
+
0x13460000143FB,
|
| 1804 |
+
0x1440000014647,
|
| 1805 |
+
0x161000001613A,
|
| 1806 |
+
0x1680000016A39,
|
| 1807 |
+
0x16A4000016A5F,
|
| 1808 |
+
0x16A6000016A6A,
|
| 1809 |
+
0x16A7000016ABF,
|
| 1810 |
+
0x16AC000016ACA,
|
| 1811 |
+
0x16AD000016AEE,
|
| 1812 |
+
0x16AF000016AF5,
|
| 1813 |
+
0x16B0000016B37,
|
| 1814 |
+
0x16B4000016B44,
|
| 1815 |
+
0x16B5000016B5A,
|
| 1816 |
+
0x16B6300016B78,
|
| 1817 |
+
0x16B7D00016B90,
|
| 1818 |
+
0x16D4000016D6D,
|
| 1819 |
+
0x16D7000016D7A,
|
| 1820 |
+
0x16E6000016E80,
|
| 1821 |
+
0x16EBB00016ED4,
|
| 1822 |
+
0x16F0000016F4B,
|
| 1823 |
+
0x16F4F00016F88,
|
| 1824 |
+
0x16F8F00016FA0,
|
| 1825 |
+
0x16FE000016FE2,
|
| 1826 |
+
0x16FE300016FE5,
|
| 1827 |
+
0x16FF000016FF4,
|
| 1828 |
+
0x1700000018CD6,
|
| 1829 |
+
0x18CFF00018D1F,
|
| 1830 |
+
0x18D8000018DF3,
|
| 1831 |
+
0x1AFF00001AFF4,
|
| 1832 |
+
0x1AFF50001AFFC,
|
| 1833 |
+
0x1AFFD0001AFFF,
|
| 1834 |
+
0x1B0000001B123,
|
| 1835 |
+
0x1B1320001B133,
|
| 1836 |
+
0x1B1500001B153,
|
| 1837 |
+
0x1B1550001B156,
|
| 1838 |
+
0x1B1640001B168,
|
| 1839 |
+
0x1B1700001B2FC,
|
| 1840 |
+
0x1BC000001BC6B,
|
| 1841 |
+
0x1BC700001BC7D,
|
| 1842 |
+
0x1BC800001BC89,
|
| 1843 |
+
0x1BC900001BC9A,
|
| 1844 |
+
0x1BC9D0001BC9F,
|
| 1845 |
+
0x1CF000001CF2E,
|
| 1846 |
+
0x1CF300001CF47,
|
| 1847 |
+
0x1DA000001DA37,
|
| 1848 |
+
0x1DA3B0001DA6D,
|
| 1849 |
+
0x1DA750001DA76,
|
| 1850 |
+
0x1DA840001DA85,
|
| 1851 |
+
0x1DA9B0001DAA0,
|
| 1852 |
+
0x1DAA10001DAB0,
|
| 1853 |
+
0x1DF000001DF1F,
|
| 1854 |
+
0x1DF250001DF2B,
|
| 1855 |
+
0x1E0000001E007,
|
| 1856 |
+
0x1E0080001E019,
|
| 1857 |
+
0x1E01B0001E022,
|
| 1858 |
+
0x1E0230001E025,
|
| 1859 |
+
0x1E0260001E02B,
|
| 1860 |
+
0x1E08F0001E090,
|
| 1861 |
+
0x1E1000001E12D,
|
| 1862 |
+
0x1E1300001E13E,
|
| 1863 |
+
0x1E1400001E14A,
|
| 1864 |
+
0x1E14E0001E14F,
|
| 1865 |
+
0x1E2900001E2AF,
|
| 1866 |
+
0x1E2C00001E2FA,
|
| 1867 |
+
0x1E4D00001E4FA,
|
| 1868 |
+
0x1E5D00001E5FB,
|
| 1869 |
+
0x1E6C00001E6DF,
|
| 1870 |
+
0x1E6E00001E6F6,
|
| 1871 |
+
0x1E6FE0001E700,
|
| 1872 |
+
0x1E7E00001E7E7,
|
| 1873 |
+
0x1E7E80001E7EC,
|
| 1874 |
+
0x1E7ED0001E7EF,
|
| 1875 |
+
0x1E7F00001E7FF,
|
| 1876 |
+
0x1E8000001E8C5,
|
| 1877 |
+
0x1E8D00001E8D7,
|
| 1878 |
+
0x1E9220001E94C,
|
| 1879 |
+
0x1E9500001E95A,
|
| 1880 |
+
0x200000002A6E0,
|
| 1881 |
+
0x2A7000002B81E,
|
| 1882 |
+
0x2B8200002CEAE,
|
| 1883 |
+
0x2CEB00002EBE1,
|
| 1884 |
+
0x2EBF00002EE5E,
|
| 1885 |
+
0x300000003134B,
|
| 1886 |
+
0x313500003347A,
|
| 1887 |
+
),
|
| 1888 |
+
"CONTEXTJ": (0x200C0000200E,),
|
| 1889 |
+
"CONTEXTO": (
|
| 1890 |
+
0xB7000000B8,
|
| 1891 |
+
0x37500000376,
|
| 1892 |
+
0x5F3000005F5,
|
| 1893 |
+
0x6600000066A,
|
| 1894 |
+
0x6F0000006FA,
|
| 1895 |
+
0x30FB000030FC,
|
| 1896 |
+
),
|
| 1897 |
+
}
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/intranges.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Given a list of integers, made up of (hopefully) a small number of long runs
|
| 3 |
+
of consecutive integers, compute a representation of the form
|
| 4 |
+
((start1, end1), (start2, end2) ...). Then answer the question "was x present
|
| 5 |
+
in the original list?" in time O(log(# runs)).
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import bisect
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def intranges_from_list(list_: list[int]) -> tuple[int, ...]:
|
| 12 |
+
"""Represent a list of integers as a sequence of ranges:
|
| 13 |
+
((start_0, end_0), (start_1, end_1), ...), such that the original
|
| 14 |
+
integers are exactly those x such that start_i <= x < end_i for some i.
|
| 15 |
+
|
| 16 |
+
Ranges are encoded as single integers (start << 32 | end), not as tuples.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
sorted_list = sorted(list_)
|
| 20 |
+
ranges = []
|
| 21 |
+
last_write = -1
|
| 22 |
+
for i in range(len(sorted_list)):
|
| 23 |
+
if i + 1 < len(sorted_list) and sorted_list[i] == sorted_list[i + 1] - 1:
|
| 24 |
+
continue
|
| 25 |
+
current_range = sorted_list[last_write + 1 : i + 1]
|
| 26 |
+
ranges.append(_encode_range(current_range[0], current_range[-1] + 1))
|
| 27 |
+
last_write = i
|
| 28 |
+
|
| 29 |
+
return tuple(ranges)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def _encode_range(start: int, end: int) -> int:
|
| 33 |
+
return (start << 32) | end
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _decode_range(r: int) -> tuple[int, int]:
|
| 37 |
+
return (r >> 32), (r & ((1 << 32) - 1))
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def intranges_contain(int_: int, ranges: tuple[int, ...]) -> bool:
|
| 41 |
+
"""Determine if `int_` falls into one of the ranges in `ranges`."""
|
| 42 |
+
tuple_ = _encode_range(int_, 0)
|
| 43 |
+
pos = bisect.bisect_left(ranges, tuple_)
|
| 44 |
+
# we could be immediately ahead of a tuple (start, end)
|
| 45 |
+
# with start < int_ <= end
|
| 46 |
+
if pos > 0:
|
| 47 |
+
left, right = _decode_range(ranges[pos - 1])
|
| 48 |
+
if left <= int_ < right:
|
| 49 |
+
return True
|
| 50 |
+
# or we could be immediately behind a tuple (int_, end)
|
| 51 |
+
if pos < len(ranges):
|
| 52 |
+
left, _ = _decode_range(ranges[pos])
|
| 53 |
+
if left == int_:
|
| 54 |
+
return True
|
| 55 |
+
return False
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/package_data.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
__version__ = "3.18"
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/py.typed
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/uts46data.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
uv
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: iniconfig
|
| 3 |
+
Version: 2.3.0
|
| 4 |
+
Summary: brain-dead simple config-ini parsing
|
| 5 |
+
Author-email: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>, Holger Krekel <holger.krekel@gmail.com>
|
| 6 |
+
License-Expression: MIT
|
| 7 |
+
Project-URL: Homepage, https://github.com/pytest-dev/iniconfig
|
| 8 |
+
Classifier: Development Status :: 4 - Beta
|
| 9 |
+
Classifier: Intended Audience :: Developers
|
| 10 |
+
Classifier: Operating System :: MacOS :: MacOS X
|
| 11 |
+
Classifier: Operating System :: Microsoft :: Windows
|
| 12 |
+
Classifier: Operating System :: POSIX
|
| 13 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 14 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.14
|
| 19 |
+
Classifier: Topic :: Software Development :: Libraries
|
| 20 |
+
Classifier: Topic :: Utilities
|
| 21 |
+
Requires-Python: >=3.10
|
| 22 |
+
Description-Content-Type: text/x-rst
|
| 23 |
+
License-File: LICENSE
|
| 24 |
+
Dynamic: license-file
|
| 25 |
+
|
| 26 |
+
iniconfig: brain-dead simple parsing of ini files
|
| 27 |
+
=======================================================
|
| 28 |
+
|
| 29 |
+
iniconfig is a small and simple INI-file parser module
|
| 30 |
+
having a unique set of features:
|
| 31 |
+
|
| 32 |
+
* maintains order of sections and entries
|
| 33 |
+
* supports multi-line values with or without line-continuations
|
| 34 |
+
* supports "#" comments everywhere
|
| 35 |
+
* raises errors with proper line-numbers
|
| 36 |
+
* no bells and whistles like automatic substitutions
|
| 37 |
+
* iniconfig raises an Error if two sections have the same name.
|
| 38 |
+
|
| 39 |
+
If you encounter issues or have feature wishes please report them to:
|
| 40 |
+
|
| 41 |
+
https://github.com/RonnyPfannschmidt/iniconfig/issues
|
| 42 |
+
|
| 43 |
+
Basic Example
|
| 44 |
+
===================================
|
| 45 |
+
|
| 46 |
+
If you have an ini file like this:
|
| 47 |
+
|
| 48 |
+
.. code-block:: ini
|
| 49 |
+
|
| 50 |
+
# content of example.ini
|
| 51 |
+
[section1] # comment
|
| 52 |
+
name1=value1 # comment
|
| 53 |
+
name1b=value1,value2 # comment
|
| 54 |
+
|
| 55 |
+
[section2]
|
| 56 |
+
name2=
|
| 57 |
+
line1
|
| 58 |
+
line2
|
| 59 |
+
|
| 60 |
+
then you can do:
|
| 61 |
+
|
| 62 |
+
.. code-block:: pycon
|
| 63 |
+
|
| 64 |
+
>>> import iniconfig
|
| 65 |
+
>>> ini = iniconfig.IniConfig("example.ini")
|
| 66 |
+
>>> ini['section1']['name1'] # raises KeyError if not exists
|
| 67 |
+
'value1'
|
| 68 |
+
>>> ini.get('section1', 'name1b', [], lambda x: x.split(","))
|
| 69 |
+
['value1', 'value2']
|
| 70 |
+
>>> ini.get('section1', 'notexist', [], lambda x: x.split(","))
|
| 71 |
+
[]
|
| 72 |
+
>>> [x.name for x in list(ini)]
|
| 73 |
+
['section1', 'section2']
|
| 74 |
+
>>> list(list(ini)[0].items())
|
| 75 |
+
[('name1', 'value1'), ('name1b', 'value1,value2')]
|
| 76 |
+
>>> 'section1' in ini
|
| 77 |
+
True
|
| 78 |
+
>>> 'inexistendsection' in ini
|
| 79 |
+
False
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
iniconfig-2.3.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
|
| 2 |
+
iniconfig-2.3.0.dist-info/METADATA,sha256=QNdz-E5OES9JW79PG-nL0tRWwK6271MR910b8yLyFls,2526
|
| 3 |
+
iniconfig-2.3.0.dist-info/RECORD,,
|
| 4 |
+
iniconfig-2.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 5 |
+
iniconfig-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
| 6 |
+
iniconfig-2.3.0.dist-info/licenses/LICENSE,sha256=NAn6kfes5VeJRjJnZlbjImT-XvdYFTVyXcmiN3RVG9Q,1098
|
| 7 |
+
iniconfig-2.3.0.dist-info/top_level.txt,sha256=7KfM0fugdlToj9UW7enKXk2HYALQD8qHiyKtjhSzgN8,10
|
| 8 |
+
iniconfig/__init__.py,sha256=XL5eqUYj4mskAOorZ5jfRAinJvJzTI-fJxpP4xfXtaw,7497
|
| 9 |
+
iniconfig/_parse.py,sha256=5ncBl7MAQiaPNnpRrs9FR4t6G6DkgOUs458OY_1CR28,5223
|
| 10 |
+
iniconfig/_version.py,sha256=KNFYe-Vtdt7Z-oHyl8jmDAQ9qXoCNMAEXigj6BR1QUI,704
|
| 11 |
+
iniconfig/exceptions.py,sha256=mipQ_aMxD9CvSvFWN1oTXY4QuRnKAMZ1f3sCdmjDTU0,399
|
| 12 |
+
iniconfig/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/REQUESTED
ADDED
|
File without changes
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (80.9.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2010 - 2023 Holger Krekel and others
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
| 6 |
+
this software and associated documentation files (the "Software"), to deal in
|
| 7 |
+
the Software without restriction, including without limitation the rights to
|
| 8 |
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
| 9 |
+
of the Software, and to permit persons to whom the Software is furnished to do
|
| 10 |
+
so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
iniconfig
|
hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig/__init__.py
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""brain-dead simple parser for ini-style files.
|
| 2 |
+
(C) Ronny Pfannschmidt, Holger Krekel -- MIT licensed
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
from collections.abc import Callable
|
| 7 |
+
from collections.abc import Iterator
|
| 8 |
+
from collections.abc import Mapping
|
| 9 |
+
from typing import Final
|
| 10 |
+
from typing import TypeVar
|
| 11 |
+
from typing import overload
|
| 12 |
+
|
| 13 |
+
__all__ = ["IniConfig", "ParseError", "COMMENTCHARS", "iscommentline"]
|
| 14 |
+
|
| 15 |
+
from . import _parse
|
| 16 |
+
from ._parse import COMMENTCHARS
|
| 17 |
+
from ._parse import iscommentline
|
| 18 |
+
from .exceptions import ParseError
|
| 19 |
+
|
| 20 |
+
_D = TypeVar("_D")
|
| 21 |
+
_T = TypeVar("_T")
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class SectionWrapper:
|
| 25 |
+
config: Final["IniConfig"]
|
| 26 |
+
name: Final[str]
|
| 27 |
+
|
| 28 |
+
def __init__(self, config: "IniConfig", name: str) -> None:
|
| 29 |
+
self.config = config
|
| 30 |
+
self.name = name
|
| 31 |
+
|
| 32 |
+
def lineof(self, name: str) -> int | None:
|
| 33 |
+
return self.config.lineof(self.name, name)
|
| 34 |
+
|
| 35 |
+
@overload
|
| 36 |
+
def get(self, key: str) -> str | None: ...
|
| 37 |
+
|
| 38 |
+
@overload
|
| 39 |
+
def get(
|
| 40 |
+
self,
|
| 41 |
+
key: str,
|
| 42 |
+
convert: Callable[[str], _T],
|
| 43 |
+
) -> _T | None: ...
|
| 44 |
+
|
| 45 |
+
@overload
|
| 46 |
+
def get(
|
| 47 |
+
self,
|
| 48 |
+
key: str,
|
| 49 |
+
default: None,
|
| 50 |
+
convert: Callable[[str], _T],
|
| 51 |
+
) -> _T | None: ...
|
| 52 |
+
|
| 53 |
+
@overload
|
| 54 |
+
def get(self, key: str, default: _D, convert: None = None) -> str | _D: ...
|
| 55 |
+
|
| 56 |
+
@overload
|
| 57 |
+
def get(
|
| 58 |
+
self,
|
| 59 |
+
key: str,
|
| 60 |
+
default: _D,
|
| 61 |
+
convert: Callable[[str], _T],
|
| 62 |
+
) -> _T | _D: ...
|
| 63 |
+
|
| 64 |
+
# TODO: investigate possible mypy bug wrt matching the passed over data
|
| 65 |
+
def get( # type: ignore [misc]
|
| 66 |
+
self,
|
| 67 |
+
key: str,
|
| 68 |
+
default: _D | None = None,
|
| 69 |
+
convert: Callable[[str], _T] | None = None,
|
| 70 |
+
) -> _D | _T | str | None:
|
| 71 |
+
return self.config.get(self.name, key, convert=convert, default=default)
|
| 72 |
+
|
| 73 |
+
def __getitem__(self, key: str) -> str:
|
| 74 |
+
return self.config.sections[self.name][key]
|
| 75 |
+
|
| 76 |
+
def __iter__(self) -> Iterator[str]:
|
| 77 |
+
section: Mapping[str, str] = self.config.sections.get(self.name, {})
|
| 78 |
+
|
| 79 |
+
def lineof(key: str) -> int:
|
| 80 |
+
return self.config.lineof(self.name, key) # type: ignore[return-value]
|
| 81 |
+
|
| 82 |
+
yield from sorted(section, key=lineof)
|
| 83 |
+
|
| 84 |
+
def items(self) -> Iterator[tuple[str, str]]:
|
| 85 |
+
for name in self:
|
| 86 |
+
yield name, self[name]
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class IniConfig:
|
| 90 |
+
path: Final[str]
|
| 91 |
+
sections: Final[Mapping[str, Mapping[str, str]]]
|
| 92 |
+
_sources: Final[Mapping[tuple[str, str | None], int]]
|
| 93 |
+
|
| 94 |
+
def __init__(
|
| 95 |
+
self,
|
| 96 |
+
path: str | os.PathLike[str],
|
| 97 |
+
data: str | None = None,
|
| 98 |
+
encoding: str = "utf-8",
|
| 99 |
+
*,
|
| 100 |
+
_sections: Mapping[str, Mapping[str, str]] | None = None,
|
| 101 |
+
_sources: Mapping[tuple[str, str | None], int] | None = None,
|
| 102 |
+
) -> None:
|
| 103 |
+
self.path = os.fspath(path)
|
| 104 |
+
|
| 105 |
+
# Determine sections and sources
|
| 106 |
+
if _sections is not None and _sources is not None:
|
| 107 |
+
# Use provided pre-parsed data (called from parse())
|
| 108 |
+
sections_data = _sections
|
| 109 |
+
sources = _sources
|
| 110 |
+
else:
|
| 111 |
+
# Parse the data (backward compatible path)
|
| 112 |
+
if data is None:
|
| 113 |
+
with open(self.path, encoding=encoding) as fp:
|
| 114 |
+
data = fp.read()
|
| 115 |
+
|
| 116 |
+
# Use old behavior (no stripping) for backward compatibility
|
| 117 |
+
sections_data, sources = _parse.parse_ini_data(
|
| 118 |
+
self.path, data, strip_inline_comments=False
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
# Assign once to Final attributes
|
| 122 |
+
self._sources = sources
|
| 123 |
+
self.sections = sections_data
|
| 124 |
+
|
| 125 |
+
@classmethod
|
| 126 |
+
def parse(
|
| 127 |
+
cls,
|
| 128 |
+
path: str | os.PathLike[str],
|
| 129 |
+
data: str | None = None,
|
| 130 |
+
encoding: str = "utf-8",
|
| 131 |
+
*,
|
| 132 |
+
strip_inline_comments: bool = True,
|
| 133 |
+
strip_section_whitespace: bool = False,
|
| 134 |
+
) -> "IniConfig":
|
| 135 |
+
"""Parse an INI file.
|
| 136 |
+
|
| 137 |
+
Args:
|
| 138 |
+
path: Path to the INI file (used for error messages)
|
| 139 |
+
data: Optional INI content as string. If None, reads from path.
|
| 140 |
+
encoding: Encoding to use when reading the file (default: utf-8)
|
| 141 |
+
strip_inline_comments: Whether to strip inline comments from values
|
| 142 |
+
(default: True). When True, comments starting with # or ; are
|
| 143 |
+
removed from values, matching the behavior for section comments.
|
| 144 |
+
strip_section_whitespace: Whether to strip whitespace from section and key names
|
| 145 |
+
(default: False). When True, strips Unicode whitespace from section and key names,
|
| 146 |
+
addressing issue #4. When False, preserves existing behavior for backward compatibility.
|
| 147 |
+
|
| 148 |
+
Returns:
|
| 149 |
+
IniConfig instance with parsed configuration
|
| 150 |
+
|
| 151 |
+
Example:
|
| 152 |
+
# With comment stripping (default):
|
| 153 |
+
config = IniConfig.parse("setup.cfg")
|
| 154 |
+
# value = "foo" instead of "foo # comment"
|
| 155 |
+
|
| 156 |
+
# Without comment stripping (old behavior):
|
| 157 |
+
config = IniConfig.parse("setup.cfg", strip_inline_comments=False)
|
| 158 |
+
# value = "foo # comment"
|
| 159 |
+
|
| 160 |
+
# With section name stripping (opt-in for issue #4):
|
| 161 |
+
config = IniConfig.parse("setup.cfg", strip_section_whitespace=True)
|
| 162 |
+
# section names and keys have Unicode whitespace stripped
|
| 163 |
+
"""
|
| 164 |
+
fspath = os.fspath(path)
|
| 165 |
+
|
| 166 |
+
if data is None:
|
| 167 |
+
with open(fspath, encoding=encoding) as fp:
|
| 168 |
+
data = fp.read()
|
| 169 |
+
|
| 170 |
+
sections_data, sources = _parse.parse_ini_data(
|
| 171 |
+
fspath,
|
| 172 |
+
data,
|
| 173 |
+
strip_inline_comments=strip_inline_comments,
|
| 174 |
+
strip_section_whitespace=strip_section_whitespace,
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
# Call constructor with pre-parsed sections and sources
|
| 178 |
+
return cls(path=fspath, _sections=sections_data, _sources=sources)
|
| 179 |
+
|
| 180 |
+
def lineof(self, section: str, name: str | None = None) -> int | None:
|
| 181 |
+
lineno = self._sources.get((section, name))
|
| 182 |
+
return None if lineno is None else lineno + 1
|
| 183 |
+
|
| 184 |
+
@overload
|
| 185 |
+
def get(
|
| 186 |
+
self,
|
| 187 |
+
section: str,
|
| 188 |
+
name: str,
|
| 189 |
+
) -> str | None: ...
|
| 190 |
+
|
| 191 |
+
@overload
|
| 192 |
+
def get(
|
| 193 |
+
self,
|
| 194 |
+
section: str,
|
| 195 |
+
name: str,
|
| 196 |
+
convert: Callable[[str], _T],
|
| 197 |
+
) -> _T | None: ...
|
| 198 |
+
|
| 199 |
+
@overload
|
| 200 |
+
def get(
|
| 201 |
+
self,
|
| 202 |
+
section: str,
|
| 203 |
+
name: str,
|
| 204 |
+
default: None,
|
| 205 |
+
convert: Callable[[str], _T],
|
| 206 |
+
) -> _T | None: ...
|
| 207 |
+
|
| 208 |
+
@overload
|
| 209 |
+
def get(
|
| 210 |
+
self, section: str, name: str, default: _D, convert: None = None
|
| 211 |
+
) -> str | _D: ...
|
| 212 |
+
|
| 213 |
+
@overload
|
| 214 |
+
def get(
|
| 215 |
+
self,
|
| 216 |
+
section: str,
|
| 217 |
+
name: str,
|
| 218 |
+
default: _D,
|
| 219 |
+
convert: Callable[[str], _T],
|
| 220 |
+
) -> _T | _D: ...
|
| 221 |
+
|
| 222 |
+
def get( # type: ignore
|
| 223 |
+
self,
|
| 224 |
+
section: str,
|
| 225 |
+
name: str,
|
| 226 |
+
default: _D | None = None,
|
| 227 |
+
convert: Callable[[str], _T] | None = None,
|
| 228 |
+
) -> _D | _T | str | None:
|
| 229 |
+
try:
|
| 230 |
+
value: str = self.sections[section][name]
|
| 231 |
+
except KeyError:
|
| 232 |
+
return default
|
| 233 |
+
else:
|
| 234 |
+
if convert is not None:
|
| 235 |
+
return convert(value)
|
| 236 |
+
else:
|
| 237 |
+
return value
|
| 238 |
+
|
| 239 |
+
def __getitem__(self, name: str) -> SectionWrapper:
|
| 240 |
+
if name not in self.sections:
|
| 241 |
+
raise KeyError(name)
|
| 242 |
+
return SectionWrapper(self, name)
|
| 243 |
+
|
| 244 |
+
def __iter__(self) -> Iterator[SectionWrapper]:
|
| 245 |
+
for name in sorted(self.sections, key=self.lineof): # type: ignore
|
| 246 |
+
yield SectionWrapper(self, name)
|
| 247 |
+
|
| 248 |
+
def __contains__(self, arg: str) -> bool:
|
| 249 |
+
return arg in self.sections
|