smlflg commited on
Commit
19a9d01
·
verified ·
1 Parent(s): d8355ff

Initial public upload from Projekte/CodingAgent (part 3)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/INSTALLER +1 -0
  2. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/METADATA +203 -0
  3. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/RECORD +32 -0
  4. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/REQUESTED +0 -0
  5. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/WHEEL +4 -0
  6. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/entry_points.txt +2 -0
  7. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/licenses/LICENSE.md +12 -0
  8. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_urlparse.py +527 -0
  9. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_urls.py +641 -0
  10. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/_utils.py +242 -0
  11. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx/py.typed +0 -0
  12. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/INSTALLER +1 -0
  13. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/METADATA +351 -0
  14. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/RECORD +13 -0
  15. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/REQUESTED +0 -0
  16. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/WHEEL +5 -0
  17. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/licenses/LICENSE +21 -0
  18. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse-0.4.3.dist-info/top_level.txt +1 -0
  19. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/__init__.py +14 -0
  20. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_api.py +93 -0
  21. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_decoders.py +139 -0
  22. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_exceptions.py +5 -0
  23. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/_models.py +54 -0
  24. hai-mcp-pr7/.venv/lib/python3.13/site-packages/httpx_sse/py.typed +0 -0
  25. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/INSTALLER +1 -0
  26. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/METADATA +155 -0
  27. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/RECORD +19 -0
  28. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/REQUESTED +0 -0
  29. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/WHEEL +4 -0
  30. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/entry_points.txt +3 -0
  31. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna-3.18.dist-info/licenses/LICENSE.md +31 -0
  32. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/__init__.py +45 -0
  33. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/__main__.py +6 -0
  34. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/cli.py +128 -0
  35. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/codec.py +159 -0
  36. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/compat.py +41 -0
  37. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/core.py +648 -0
  38. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/idnadata.py +1897 -0
  39. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/intranges.py +55 -0
  40. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/package_data.py +1 -0
  41. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/py.typed +0 -0
  42. hai-mcp-pr7/.venv/lib/python3.13/site-packages/idna/uts46data.py +0 -0
  43. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/INSTALLER +1 -0
  44. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/METADATA +79 -0
  45. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/RECORD +12 -0
  46. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/REQUESTED +0 -0
  47. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/WHEEL +5 -0
  48. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE +21 -0
  49. hai-mcp-pr7/.venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/top_level.txt +1 -0
  50. 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/>&mdash; 🦋 &mdash;</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
+ [![Build Status](https://dev.azure.com/florimondmanca/public/_apis/build/status/florimondmanca.httpx-sse?branchName=master)](https://dev.azure.com/florimondmanca/public/_build?definitionId=19)
25
+ [![Coverage](https://codecov.io/gh/florimondmanca/httpx-sse/branch/master/graph/badge.svg)](https://codecov.io/gh/florimondmanca/httpx-sse)
26
+ [![Package version](https://badge.fury.io/py/httpx-sse.svg)](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