From fe38052ac0573c921794b320099a7b63dca9de8e Mon Sep 17 00:00:00 2001 From: Ruslan Piatrovich Date: Wed, 4 Sep 2024 16:42:36 +0300 Subject: [PATCH] added aqua ng script --- .gitignore | 160 + .venv/bin/activate | 4 +- .venv/bin/activate.csh | 2 +- .venv/bin/activate.fish | 2 +- .venv/bin/normalizer | 8 - .venv/bin/pip | 2 +- .venv/bin/pip3 | 2 +- .venv/bin/pip3.12 | 2 +- .venv/bin/python | 2 +- .../certifi-2024.7.4.dist-info/INSTALLER | 1 - .../certifi-2024.7.4.dist-info/LICENSE | 20 - .../certifi-2024.7.4.dist-info/METADATA | 67 - .../certifi-2024.7.4.dist-info/RECORD | 14 - .../certifi-2024.7.4.dist-info/WHEEL | 5 - .../certifi-2024.7.4.dist-info/top_level.txt | 1 - .../site-packages/certifi/__init__.py | 4 - .../site-packages/certifi/__main__.py | 12 - .../__pycache__/__init__.cpython-312.pyc | Bin 305 -> 0 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 620 -> 0 bytes .../certifi/__pycache__/core.cpython-312.pyc | Bin 3190 -> 0 bytes .../site-packages/certifi/cacert.pem | 4798 --------- .../python3.12/site-packages/certifi/core.py | 114 - .../python3.12/site-packages/certifi/py.typed | 0 .../INSTALLER | 1 - .../LICENSE | 21 - .../METADATA | 683 -- .../charset_normalizer-3.3.2.dist-info/RECORD | 35 - .../charset_normalizer-3.3.2.dist-info/WHEEL | 6 - .../entry_points.txt | 2 - .../top_level.txt | 1 - .../charset_normalizer/__init__.py | 46 - .../charset_normalizer/__main__.py | 4 - .../__pycache__/__init__.cpython-312.pyc | Bin 1724 -> 0 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 294 -> 0 bytes .../__pycache__/api.cpython-312.pyc | Bin 17327 -> 0 bytes .../__pycache__/cd.cpython-312.pyc | Bin 13487 -> 0 bytes .../__pycache__/constant.cpython-312.pyc | Bin 38751 -> 0 bytes .../__pycache__/legacy.cpython-312.pyc | Bin 2495 -> 0 bytes .../__pycache__/md.cpython-312.pyc | Bin 24509 -> 0 bytes .../__pycache__/models.cpython-312.pyc | Bin 16599 -> 0 bytes .../__pycache__/utils.cpython-312.pyc | Bin 14362 -> 0 bytes .../__pycache__/version.cpython-312.pyc | Bin 321 -> 0 bytes .../site-packages/charset_normalizer/api.py | 626 -- .../site-packages/charset_normalizer/cd.py | 395 - .../charset_normalizer/cli/__init__.py | 6 - .../charset_normalizer/cli/__main__.py | 296 - .../cli/__pycache__/__init__.cpython-312.pyc | Bin 282 -> 0 bytes .../cli/__pycache__/__main__.cpython-312.pyc | Bin 10386 -> 0 bytes .../charset_normalizer/constant.py | 1995 ---- .../charset_normalizer/legacy.py | 54 - .../md.cpython-312-x86_64-linux-gnu.so | Bin 16064 -> 0 bytes .../site-packages/charset_normalizer/md.py | 615 -- .../md__mypyc.cpython-312-x86_64-linux-gnu.so | Bin 272640 -> 0 bytes .../charset_normalizer/models.py | 340 - .../site-packages/charset_normalizer/py.typed | 0 .../site-packages/charset_normalizer/utils.py | 421 - .../charset_normalizer/version.py | 6 - .../python3.12/site-packages/dns/__init__.py | 70 - .../dns/__pycache__/__init__.cpython-312.pyc | Bin 716 -> 0 bytes .../__pycache__/_asyncbackend.cpython-312.pyc | Bin 4784 -> 0 bytes .../_asyncio_backend.cpython-312.pyc | Bin 14121 -> 0 bytes .../dns/__pycache__/_ddr.cpython-312.pyc | Bin 7833 -> 0 bytes .../dns/__pycache__/_features.cpython-312.pyc | Bin 3282 -> 0 bytes .../_immutable_ctx.cpython-312.pyc | Bin 3229 -> 0 bytes .../__pycache__/_trio_backend.cpython-312.pyc | Bin 13172 -> 0 bytes .../__pycache__/asyncbackend.cpython-312.pyc | Bin 3412 -> 0 bytes .../__pycache__/asyncquery.cpython-312.pyc | Bin 31670 -> 0 bytes .../__pycache__/asyncresolver.cpython-312.pyc | Bin 21326 -> 0 bytes .../dns/__pycache__/dnssec.cpython-312.pyc | Bin 50891 -> 0 bytes .../__pycache__/dnssectypes.cpython-312.pyc | Bin 1970 -> 0 bytes .../dns/__pycache__/e164.cpython-312.pyc | Bin 4903 -> 0 bytes .../dns/__pycache__/edns.cpython-312.pyc | Bin 22032 -> 0 bytes .../dns/__pycache__/entropy.cpython-312.pyc | Bin 5965 -> 0 bytes .../dns/__pycache__/enum.cpython-312.pyc | Bin 4878 -> 0 bytes .../dns/__pycache__/exception.cpython-312.pyc | Bin 7222 -> 0 bytes .../dns/__pycache__/flags.cpython-312.pyc | Bin 3072 -> 0 bytes .../dns/__pycache__/grange.cpython-312.pyc | Bin 1772 -> 0 bytes .../dns/__pycache__/immutable.cpython-312.pyc | Bin 3782 -> 0 bytes .../dns/__pycache__/inet.cpython-312.pyc | Bin 6695 -> 0 bytes .../dns/__pycache__/ipv4.cpython-312.pyc | Bin 2722 -> 0 bytes .../dns/__pycache__/ipv6.cpython-312.pyc | Bin 6858 -> 0 bytes .../dns/__pycache__/message.cpython-312.pyc | Bin 83663 -> 0 bytes .../dns/__pycache__/name.cpython-312.pyc | Bin 49224 -> 0 bytes .../dns/__pycache__/namedict.cpython-312.pyc | Bin 4356 -> 0 bytes .../__pycache__/nameserver.cpython-312.pyc | Bin 14133 -> 0 bytes .../dns/__pycache__/node.cpython-312.pyc | Bin 16639 -> 0 bytes .../dns/__pycache__/opcode.cpython-312.pyc | Bin 3130 -> 0 bytes .../dns/__pycache__/query.cpython-312.pyc | Bin 59067 -> 0 bytes .../dns/__pycache__/rcode.cpython-312.pyc | Bin 4427 -> 0 bytes .../dns/__pycache__/rdata.cpython-312.pyc | Bin 37461 -> 0 bytes .../__pycache__/rdataclass.cpython-312.pyc | Bin 3518 -> 0 bytes .../dns/__pycache__/rdataset.cpython-312.pyc | Bin 22964 -> 0 bytes .../dns/__pycache__/rdatatype.cpython-312.pyc | Bin 10114 -> 0 bytes .../dns/__pycache__/renderer.cpython-312.pyc | Bin 16226 -> 0 bytes .../dns/__pycache__/resolver.cpython-312.pyc | Bin 88375 -> 0 bytes .../__pycache__/reversename.cpython-312.pyc | Bin 4724 -> 0 bytes .../dns/__pycache__/rrset.cpython-312.pyc | Bin 12483 -> 0 bytes .../dns/__pycache__/serial.cpython-312.pyc | Bin 5182 -> 0 bytes .../dns/__pycache__/set.cpython-312.pyc | Bin 12157 -> 0 bytes .../dns/__pycache__/tokenizer.cpython-312.pyc | Bin 26544 -> 0 bytes .../__pycache__/transaction.cpython-312.pyc | Bin 29402 -> 0 bytes .../dns/__pycache__/tsig.cpython-312.pyc | Bin 16756 -> 0 bytes .../__pycache__/tsigkeyring.cpython-312.pyc | Bin 2910 -> 0 bytes .../dns/__pycache__/ttl.cpython-312.pyc | Bin 2443 -> 0 bytes .../dns/__pycache__/update.cpython-312.pyc | Bin 16328 -> 0 bytes .../dns/__pycache__/version.cpython-312.pyc | Bin 775 -> 0 bytes .../dns/__pycache__/versioned.cpython-312.pyc | Bin 14685 -> 0 bytes .../dns/__pycache__/win32util.cpython-312.pyc | Bin 9862 -> 0 bytes .../dns/__pycache__/wire.cpython-312.pyc | Bin 5449 -> 0 bytes .../dns/__pycache__/xfr.cpython-312.pyc | Bin 14488 -> 0 bytes .../dns/__pycache__/zone.cpython-312.pyc | Bin 67615 -> 0 bytes .../dns/__pycache__/zonefile.cpython-312.pyc | Bin 33830 -> 0 bytes .../dns/__pycache__/zonetypes.cpython-312.pyc | Bin 1344 -> 0 bytes .../site-packages/dns/_asyncbackend.py | 99 - .../site-packages/dns/_asyncio_backend.py | 275 - .../lib/python3.12/site-packages/dns/_ddr.py | 154 - .../python3.12/site-packages/dns/_features.py | 92 - .../site-packages/dns/_immutable_ctx.py | 76 - .../site-packages/dns/_trio_backend.py | 250 - .../site-packages/dns/asyncbackend.py | 101 - .../site-packages/dns/asyncquery.py | 780 -- .../site-packages/dns/asyncresolver.py | 475 - .../python3.12/site-packages/dns/dnssec.py | 1223 --- .../site-packages/dns/dnssecalgs/__init__.py | 120 - .../__pycache__/__init__.cpython-312.pyc | Bin 5516 -> 0 bytes .../__pycache__/base.cpython-312.pyc | Bin 4560 -> 0 bytes .../__pycache__/cryptography.cpython-312.pyc | Bin 3825 -> 0 bytes .../__pycache__/dsa.cpython-312.pyc | Bin 6164 -> 0 bytes .../__pycache__/ecdsa.cpython-312.pyc | Bin 5977 -> 0 bytes .../__pycache__/eddsa.cpython-312.pyc | Bin 4140 -> 0 bytes .../__pycache__/rsa.cpython-312.pyc | Bin 7196 -> 0 bytes .../site-packages/dns/dnssecalgs/base.py | 84 - .../dns/dnssecalgs/cryptography.py | 68 - .../site-packages/dns/dnssecalgs/dsa.py | 101 - .../site-packages/dns/dnssecalgs/ecdsa.py | 89 - .../site-packages/dns/dnssecalgs/eddsa.py | 65 - .../site-packages/dns/dnssecalgs/rsa.py | 119 - .../site-packages/dns/dnssectypes.py | 71 - .../lib/python3.12/site-packages/dns/e164.py | 116 - .../lib/python3.12/site-packages/dns/edns.py | 516 - .../python3.12/site-packages/dns/entropy.py | 130 - .../lib/python3.12/site-packages/dns/enum.py | 116 - .../python3.12/site-packages/dns/exception.py | 169 - .../lib/python3.12/site-packages/dns/flags.py | 123 - .../python3.12/site-packages/dns/grange.py | 72 - .../python3.12/site-packages/dns/immutable.py | 68 - .../lib/python3.12/site-packages/dns/inet.py | 197 - .../lib/python3.12/site-packages/dns/ipv4.py | 77 - .../lib/python3.12/site-packages/dns/ipv6.py | 219 - .../python3.12/site-packages/dns/message.py | 1888 ---- .../lib/python3.12/site-packages/dns/name.py | 1283 --- .../python3.12/site-packages/dns/namedict.py | 109 - .../site-packages/dns/nameserver.py | 359 - .../lib/python3.12/site-packages/dns/node.py | 359 - .../python3.12/site-packages/dns/opcode.py | 117 - .../lib/python3.12/site-packages/dns/py.typed | 0 .../lib/python3.12/site-packages/dns/query.py | 1578 --- .../site-packages/dns/quic/__init__.py | 75 - .../quic/__pycache__/__init__.cpython-312.pyc | Bin 3086 -> 0 bytes .../quic/__pycache__/_asyncio.cpython-312.pyc | Bin 16844 -> 0 bytes .../quic/__pycache__/_common.cpython-312.pyc | Bin 10291 -> 0 bytes .../quic/__pycache__/_sync.cpython-312.pyc | Bin 15787 -> 0 bytes .../quic/__pycache__/_trio.cpython-312.pyc | Bin 13679 -> 0 bytes .../site-packages/dns/quic/_asyncio.py | 228 - .../site-packages/dns/quic/_common.py | 224 - .../site-packages/dns/quic/_sync.py | 238 - .../site-packages/dns/quic/_trio.py | 210 - .../lib/python3.12/site-packages/dns/rcode.py | 168 - .../lib/python3.12/site-packages/dns/rdata.py | 884 -- .../site-packages/dns/rdataclass.py | 118 - .../python3.12/site-packages/dns/rdataset.py | 516 - .../python3.12/site-packages/dns/rdatatype.py | 332 - .../site-packages/dns/rdtypes/ANY/AFSDB.py | 45 - .../site-packages/dns/rdtypes/ANY/AMTRELAY.py | 91 - .../site-packages/dns/rdtypes/ANY/AVC.py | 26 - .../site-packages/dns/rdtypes/ANY/CAA.py | 71 - .../site-packages/dns/rdtypes/ANY/CDNSKEY.py | 33 - .../site-packages/dns/rdtypes/ANY/CDS.py | 29 - .../site-packages/dns/rdtypes/ANY/CERT.py | 116 - .../site-packages/dns/rdtypes/ANY/CNAME.py | 28 - .../site-packages/dns/rdtypes/ANY/CSYNC.py | 68 - .../site-packages/dns/rdtypes/ANY/DLV.py | 24 - .../site-packages/dns/rdtypes/ANY/DNAME.py | 27 - .../site-packages/dns/rdtypes/ANY/DNSKEY.py | 33 - .../site-packages/dns/rdtypes/ANY/DS.py | 24 - .../site-packages/dns/rdtypes/ANY/EUI48.py | 30 - .../site-packages/dns/rdtypes/ANY/EUI64.py | 30 - .../site-packages/dns/rdtypes/ANY/GPOS.py | 125 - .../site-packages/dns/rdtypes/ANY/HINFO.py | 66 - .../site-packages/dns/rdtypes/ANY/HIP.py | 85 - .../site-packages/dns/rdtypes/ANY/ISDN.py | 77 - .../site-packages/dns/rdtypes/ANY/L32.py | 41 - .../site-packages/dns/rdtypes/ANY/L64.py | 47 - .../site-packages/dns/rdtypes/ANY/LOC.py | 354 - .../site-packages/dns/rdtypes/ANY/LP.py | 42 - .../site-packages/dns/rdtypes/ANY/MX.py | 24 - .../site-packages/dns/rdtypes/ANY/NID.py | 47 - .../site-packages/dns/rdtypes/ANY/NINFO.py | 26 - .../site-packages/dns/rdtypes/ANY/NS.py | 24 - .../site-packages/dns/rdtypes/ANY/NSEC.py | 67 - .../site-packages/dns/rdtypes/ANY/NSEC3.py | 126 - .../dns/rdtypes/ANY/NSEC3PARAM.py | 69 - .../dns/rdtypes/ANY/OPENPGPKEY.py | 53 - .../site-packages/dns/rdtypes/ANY/OPT.py | 77 - .../site-packages/dns/rdtypes/ANY/PTR.py | 24 - .../site-packages/dns/rdtypes/ANY/RP.py | 58 - .../site-packages/dns/rdtypes/ANY/RRSIG.py | 157 - .../site-packages/dns/rdtypes/ANY/RT.py | 24 - .../site-packages/dns/rdtypes/ANY/SMIMEA.py | 9 - .../site-packages/dns/rdtypes/ANY/SOA.py | 86 - .../site-packages/dns/rdtypes/ANY/SPF.py | 26 - .../site-packages/dns/rdtypes/ANY/SSHFP.py | 68 - .../site-packages/dns/rdtypes/ANY/TKEY.py | 142 - .../site-packages/dns/rdtypes/ANY/TLSA.py | 9 - .../site-packages/dns/rdtypes/ANY/TSIG.py | 160 - .../site-packages/dns/rdtypes/ANY/TXT.py | 24 - .../site-packages/dns/rdtypes/ANY/URI.py | 79 - .../site-packages/dns/rdtypes/ANY/X25.py | 57 - .../site-packages/dns/rdtypes/ANY/ZONEMD.py | 66 - .../site-packages/dns/rdtypes/ANY/__init__.py | 68 - .../ANY/__pycache__/AFSDB.cpython-312.pyc | Bin 1061 -> 0 bytes .../ANY/__pycache__/AMTRELAY.cpython-312.pyc | Bin 4181 -> 0 bytes .../ANY/__pycache__/AVC.cpython-312.pyc | Bin 634 -> 0 bytes .../ANY/__pycache__/CAA.cpython-312.pyc | Bin 3361 -> 0 bytes .../ANY/__pycache__/CDNSKEY.cpython-312.pyc | Bin 721 -> 0 bytes .../ANY/__pycache__/CDS.cpython-312.pyc | Bin 829 -> 0 bytes .../ANY/__pycache__/CERT.cpython-312.pyc | Bin 4449 -> 0 bytes .../ANY/__pycache__/CNAME.cpython-312.pyc | Bin 844 -> 0 bytes .../ANY/__pycache__/CSYNC.cpython-312.pyc | Bin 3330 -> 0 bytes .../ANY/__pycache__/DLV.cpython-312.pyc | Bin 631 -> 0 bytes .../ANY/__pycache__/DNAME.cpython-312.pyc | Bin 907 -> 0 bytes .../ANY/__pycache__/DNSKEY.cpython-312.pyc | Bin 718 -> 0 bytes .../ANY/__pycache__/DS.cpython-312.pyc | Bin 628 -> 0 bytes .../ANY/__pycache__/EUI48.cpython-312.pyc | Bin 716 -> 0 bytes .../ANY/__pycache__/EUI64.cpython-312.pyc | Bin 716 -> 0 bytes .../ANY/__pycache__/GPOS.cpython-312.pyc | Bin 6126 -> 0 bytes .../ANY/__pycache__/HINFO.cpython-312.pyc | Bin 2991 -> 0 bytes .../ANY/__pycache__/HIP.cpython-312.pyc | Bin 4827 -> 0 bytes .../ANY/__pycache__/ISDN.cpython-312.pyc | Bin 3451 -> 0 bytes .../ANY/__pycache__/L32.cpython-312.pyc | Bin 2496 -> 0 bytes .../ANY/__pycache__/L64.cpython-312.pyc | Bin 2953 -> 0 bytes .../ANY/__pycache__/LOC.cpython-312.pyc | Bin 14067 -> 0 bytes .../ANY/__pycache__/LP.cpython-312.pyc | Bin 2468 -> 0 bytes .../ANY/__pycache__/MX.cpython-312.pyc | Bin 628 -> 0 bytes .../ANY/__pycache__/NID.cpython-312.pyc | Bin 2946 -> 0 bytes .../ANY/__pycache__/NINFO.cpython-312.pyc | Bin 640 -> 0 bytes .../ANY/__pycache__/NS.cpython-312.pyc | Bin 628 -> 0 bytes .../ANY/__pycache__/NSEC.cpython-312.pyc | Bin 3111 -> 0 bytes .../ANY/__pycache__/NSEC3.cpython-312.pyc | Bin 6350 -> 0 bytes .../__pycache__/NSEC3PARAM.cpython-312.pyc | Bin 3382 -> 0 bytes .../__pycache__/OPENPGPKEY.cpython-312.pyc | Bin 2306 -> 0 bytes .../ANY/__pycache__/OPT.cpython-312.pyc | Bin 3520 -> 0 bytes .../ANY/__pycache__/PTR.cpython-312.pyc | Bin 631 -> 0 bytes .../ANY/__pycache__/RP.cpython-312.pyc | Bin 2560 -> 0 bytes .../ANY/__pycache__/RRSIG.cpython-312.pyc | Bin 6559 -> 0 bytes .../ANY/__pycache__/RT.cpython-312.pyc | Bin 646 -> 0 bytes .../ANY/__pycache__/SMIMEA.cpython-312.pyc | Bin 646 -> 0 bytes .../ANY/__pycache__/SOA.cpython-312.pyc | Bin 3788 -> 0 bytes .../ANY/__pycache__/SPF.cpython-312.pyc | Bin 634 -> 0 bytes .../ANY/__pycache__/SSHFP.cpython-312.pyc | Bin 3115 -> 0 bytes .../ANY/__pycache__/TKEY.cpython-312.pyc | Bin 5085 -> 0 bytes .../ANY/__pycache__/TLSA.cpython-312.pyc | Bin 640 -> 0 bytes .../ANY/__pycache__/TSIG.cpython-312.pyc | Bin 5900 -> 0 bytes .../ANY/__pycache__/TXT.cpython-312.pyc | Bin 634 -> 0 bytes .../ANY/__pycache__/URI.cpython-312.pyc | Bin 4147 -> 0 bytes .../ANY/__pycache__/X25.cpython-312.pyc | Bin 2344 -> 0 bytes .../ANY/__pycache__/ZONEMD.cpython-312.pyc | Bin 4164 -> 0 bytes .../ANY/__pycache__/__init__.cpython-312.pyc | Bin 558 -> 0 bytes .../site-packages/dns/rdtypes/CH/A.py | 59 - .../site-packages/dns/rdtypes/CH/__init__.py | 22 - .../rdtypes/CH/__pycache__/A.cpython-312.pyc | Bin 2514 -> 0 bytes .../CH/__pycache__/__init__.cpython-312.pyc | Bin 260 -> 0 bytes .../site-packages/dns/rdtypes/IN/A.py | 51 - .../site-packages/dns/rdtypes/IN/AAAA.py | 51 - .../site-packages/dns/rdtypes/IN/APL.py | 150 - .../site-packages/dns/rdtypes/IN/DHCID.py | 54 - .../site-packages/dns/rdtypes/IN/HTTPS.py | 9 - .../site-packages/dns/rdtypes/IN/IPSECKEY.py | 91 - .../site-packages/dns/rdtypes/IN/KX.py | 24 - .../site-packages/dns/rdtypes/IN/NAPTR.py | 110 - .../site-packages/dns/rdtypes/IN/NSAP.py | 60 - .../site-packages/dns/rdtypes/IN/NSAP_PTR.py | 24 - .../site-packages/dns/rdtypes/IN/PX.py | 73 - .../site-packages/dns/rdtypes/IN/SRV.py | 75 - .../site-packages/dns/rdtypes/IN/SVCB.py | 9 - .../site-packages/dns/rdtypes/IN/WKS.py | 100 - .../site-packages/dns/rdtypes/IN/__init__.py | 35 - .../rdtypes/IN/__pycache__/A.cpython-312.pyc | Bin 2108 -> 0 bytes .../IN/__pycache__/AAAA.cpython-312.pyc | Bin 2132 -> 0 bytes .../IN/__pycache__/APL.cpython-312.pyc | Bin 6902 -> 0 bytes .../IN/__pycache__/DHCID.cpython-312.pyc | Bin 2252 -> 0 bytes .../IN/__pycache__/HTTPS.cpython-312.pyc | Bin 642 -> 0 bytes .../IN/__pycache__/IPSECKEY.cpython-312.pyc | Bin 4195 -> 0 bytes .../rdtypes/IN/__pycache__/KX.cpython-312.pyc | Bin 645 -> 0 bytes .../IN/__pycache__/NAPTR.cpython-312.pyc | Bin 5021 -> 0 bytes .../IN/__pycache__/NSAP.cpython-312.pyc | Bin 2697 -> 0 bytes .../IN/__pycache__/NSAP_PTR.cpython-312.pyc | Bin 653 -> 0 bytes .../rdtypes/IN/__pycache__/PX.cpython-312.pyc | Bin 3445 -> 0 bytes .../IN/__pycache__/SRV.cpython-312.pyc | Bin 3694 -> 0 bytes .../IN/__pycache__/SVCB.cpython-312.pyc | Bin 639 -> 0 bytes .../IN/__pycache__/WKS.cpython-312.pyc | Bin 4604 -> 0 bytes .../IN/__pycache__/__init__.cpython-312.pyc | Bin 341 -> 0 bytes .../site-packages/dns/rdtypes/__init__.py | 33 - .../__pycache__/__init__.cpython-312.pyc | Bin 340 -> 0 bytes .../__pycache__/dnskeybase.cpython-312.pyc | Bin 3864 -> 0 bytes .../__pycache__/dsbase.cpython-312.pyc | Bin 4163 -> 0 bytes .../__pycache__/euibase.cpython-312.pyc | Bin 3523 -> 0 bytes .../__pycache__/mxbase.cpython-312.pyc | Bin 4394 -> 0 bytes .../__pycache__/nsbase.cpython-312.pyc | Bin 2825 -> 0 bytes .../__pycache__/svcbbase.cpython-312.pyc | Bin 28482 -> 0 bytes .../__pycache__/tlsabase.cpython-312.pyc | Bin 3361 -> 0 bytes .../__pycache__/txtbase.cpython-312.pyc | Bin 5026 -> 0 bytes .../rdtypes/__pycache__/util.cpython-312.pyc | Bin 12831 -> 0 bytes .../site-packages/dns/rdtypes/dnskeybase.py | 87 - .../site-packages/dns/rdtypes/dsbase.py | 85 - .../site-packages/dns/rdtypes/euibase.py | 70 - .../site-packages/dns/rdtypes/mxbase.py | 87 - .../site-packages/dns/rdtypes/nsbase.py | 63 - .../site-packages/dns/rdtypes/svcbbase.py | 553 -- .../site-packages/dns/rdtypes/tlsabase.py | 71 - .../site-packages/dns/rdtypes/txtbase.py | 104 - .../site-packages/dns/rdtypes/util.py | 257 - .../python3.12/site-packages/dns/renderer.py | 346 - .../python3.12/site-packages/dns/resolver.py | 2054 ---- .../site-packages/dns/reversename.py | 105 - .../lib/python3.12/site-packages/dns/rrset.py | 285 - .../python3.12/site-packages/dns/serial.py | 118 - .venv/lib/python3.12/site-packages/dns/set.py | 307 - .../python3.12/site-packages/dns/tokenizer.py | 708 -- .../site-packages/dns/transaction.py | 651 -- .../lib/python3.12/site-packages/dns/tsig.py | 352 - .../site-packages/dns/tsigkeyring.py | 68 - .venv/lib/python3.12/site-packages/dns/ttl.py | 92 - .../python3.12/site-packages/dns/update.py | 386 - .../python3.12/site-packages/dns/version.py | 58 - .../python3.12/site-packages/dns/versioned.py | 318 - .../python3.12/site-packages/dns/win32util.py | 252 - .../lib/python3.12/site-packages/dns/wire.py | 89 - .venv/lib/python3.12/site-packages/dns/xfr.py | 343 - .../lib/python3.12/site-packages/dns/zone.py | 1434 --- .../python3.12/site-packages/dns/zonefile.py | 746 -- .../python3.12/site-packages/dns/zonetypes.py | 37 - .../dnspython-2.6.1.dist-info/INSTALLER | 1 - .../dnspython-2.6.1.dist-info/METADATA | 147 - .../dnspython-2.6.1.dist-info/RECORD | 291 - .../dnspython-2.6.1.dist-info/REQUESTED | 0 .../dnspython-2.6.1.dist-info/WHEEL | 4 - .../licenses/LICENSE | 35 - .../idna-3.7.dist-info/INSTALLER | 1 - .../idna-3.7.dist-info/LICENSE.md | 31 - .../site-packages/idna-3.7.dist-info/METADATA | 243 - .../site-packages/idna-3.7.dist-info/RECORD | 22 - .../site-packages/idna-3.7.dist-info/WHEEL | 4 - .../python3.12/site-packages/idna/__init__.py | 44 - .../idna/__pycache__/__init__.cpython-312.pyc | Bin 868 -> 0 bytes .../idna/__pycache__/codec.cpython-312.pyc | Bin 4973 -> 0 bytes .../idna/__pycache__/compat.cpython-312.pyc | Bin 874 -> 0 bytes .../idna/__pycache__/core.cpython-312.pyc | Bin 15835 -> 0 bytes .../idna/__pycache__/idnadata.cpython-312.pyc | Bin 99463 -> 0 bytes .../__pycache__/intranges.cpython-312.pyc | Bin 2625 -> 0 bytes .../__pycache__/package_data.cpython-312.pyc | Bin 203 -> 0 bytes .../__pycache__/uts46data.cpython-312.pyc | Bin 158835 -> 0 bytes .../python3.12/site-packages/idna/codec.py | 118 - .../python3.12/site-packages/idna/compat.py | 13 - .../lib/python3.12/site-packages/idna/core.py | 395 - .../python3.12/site-packages/idna/idnadata.py | 4245 -------- .../site-packages/idna/intranges.py | 54 - .../site-packages/idna/package_data.py | 2 - .../python3.12/site-packages/idna/py.typed | 0 .../site-packages/idna/uts46data.py | 8598 ----------------- .../site-packages/pip-24.2.dist-info/RECORD | 6 +- .../pip/__pycache__/__init__.cpython-312.pyc | Bin 683 -> 692 bytes .../pip/__pycache__/__main__.cpython-312.pyc | Bin 839 -> 848 bytes .../__pip-runner__.cpython-312.pyc | Bin 2202 -> 2211 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 785 -> 794 bytes .../__pycache__/build_env.cpython-312.pyc | Bin 14514 -> 14523 bytes .../__pycache__/cache.cpython-312.pyc | Bin 12663 -> 12672 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 17664 -> 17673 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 35577 -> 35586 bytes .../__pycache__/main.cpython-312.pyc | Bin 668 -> 677 bytes .../__pycache__/pyproject.cpython-312.pyc | Bin 5127 -> 5136 bytes .../self_outdated_check.cpython-312.pyc | Bin 10220 -> 10229 bytes .../__pycache__/wheel_builder.cpython-312.pyc | Bin 13636 -> 13645 bytes .../cli/__pycache__/__init__.cpython-312.pyc | Bin 276 -> 285 bytes .../autocompletion.cpython-312.pyc | Bin 8630 -> 8639 bytes .../__pycache__/base_command.cpython-312.pyc | Bin 10203 -> 10212 bytes .../__pycache__/cmdoptions.cpython-312.pyc | Bin 30396 -> 30405 bytes .../command_context.cpython-312.pyc | Bin 1779 -> 1788 bytes .../__pycache__/index_command.cpython-312.pyc | Bin 7133 -> 7142 bytes .../cli/__pycache__/main.cpython-312.pyc | Bin 2298 -> 2307 bytes .../__pycache__/main_parser.cpython-312.pyc | Bin 4903 -> 4912 bytes .../cli/__pycache__/parser.cpython-312.pyc | Bin 15064 -> 15073 bytes .../__pycache__/progress_bars.cpython-312.pyc | Bin 3853 -> 3862 bytes .../__pycache__/req_command.cpython-312.pyc | Bin 12273 -> 12282 bytes .../cli/__pycache__/spinners.cpython-312.pyc | Bin 7838 -> 7847 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 373 -> 382 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 4000 -> 4009 bytes .../__pycache__/cache.cpython-312.pyc | Bin 9709 -> 9718 bytes .../__pycache__/check.cpython-312.pyc | Bin 2614 -> 2623 bytes .../__pycache__/completion.cpython-312.pyc | Bin 5190 -> 5199 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 13210 -> 13219 bytes .../__pycache__/debug.cpython-312.pyc | Bin 10112 -> 10121 bytes .../__pycache__/download.cpython-312.pyc | Bin 7506 -> 7515 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 4388 -> 4397 bytes .../commands/__pycache__/hash.cpython-312.pyc | Bin 2981 -> 2990 bytes .../commands/__pycache__/help.cpython-312.pyc | Bin 1671 -> 1680 bytes .../__pycache__/index.cpython-312.pyc | Bin 6674 -> 6683 bytes .../__pycache__/inspect.cpython-312.pyc | Bin 3974 -> 3983 bytes .../__pycache__/install.cpython-312.pyc | Bin 29161 -> 29170 bytes .../commands/__pycache__/list.cpython-312.pyc | Bin 15766 -> 15775 bytes .../__pycache__/search.cpython-312.pyc | Bin 7543 -> 7552 bytes .../commands/__pycache__/show.cpython-312.pyc | Bin 10490 -> 10499 bytes .../__pycache__/uninstall.cpython-312.pyc | Bin 4731 -> 4740 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 8873 -> 8882 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 939 -> 948 bytes .../__pycache__/base.cpython-312.pyc | Bin 2891 -> 2900 bytes .../__pycache__/installed.cpython-312.pyc | Bin 1698 -> 1707 bytes .../__pycache__/sdist.cpython-312.pyc | Bin 8480 -> 8489 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 2279 -> 2288 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 230 -> 239 bytes .../__pycache__/collector.cpython-312.pyc | Bin 21623 -> 21632 bytes .../package_finder.cpython-312.pyc | Bin 40680 -> 40689 bytes .../index/__pycache__/sources.cpython-312.pyc | Bin 12602 -> 12611 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 16449 -> 16458 bytes .../__pycache__/_distutils.cpython-312.pyc | Bin 6854 -> 6863 bytes .../__pycache__/_sysconfig.cpython-312.pyc | Bin 8031 -> 8040 bytes .../__pycache__/base.cpython-312.pyc | Bin 3779 -> 3788 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 5880 -> 5889 bytes .../__pycache__/_json.cpython-312.pyc | Bin 2928 -> 2937 bytes .../metadata/__pycache__/base.cpython-312.pyc | Bin 35222 -> 35231 bytes .../__pycache__/pkg_resources.cpython-312.pyc | Bin 16103 -> 16112 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 356 -> 365 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 4489 -> 4498 bytes .../__pycache__/_dists.cpython-312.pyc | Bin 12571 -> 12580 bytes .../__pycache__/_envs.cpython-312.pyc | Bin 11141 -> 11150 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 264 -> 273 bytes .../__pycache__/candidate.cpython-312.pyc | Bin 1602 -> 1611 bytes .../__pycache__/direct_url.cpython-312.pyc | Bin 10837 -> 10846 bytes .../format_control.cpython-312.pyc | Bin 4225 -> 4234 bytes .../models/__pycache__/index.cpython-312.pyc | Bin 1692 -> 1701 bytes .../installation_report.cpython-312.pyc | Bin 2270 -> 2279 bytes .../models/__pycache__/link.cpython-312.pyc | Bin 26602 -> 26611 bytes .../models/__pycache__/scheme.cpython-312.pyc | Bin 1021 -> 1030 bytes .../__pycache__/search_scope.cpython-312.pyc | Bin 4994 -> 5003 bytes .../selection_prefs.cpython-312.pyc | Bin 1849 -> 1858 bytes .../__pycache__/target_python.cpython-312.pyc | Bin 4952 -> 4961 bytes .../models/__pycache__/wheel.cpython-312.pyc | Bin 5778 -> 5787 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 252 -> 261 bytes .../network/__pycache__/auth.cpython-312.pyc | Bin 22106 -> 22115 bytes .../network/__pycache__/cache.cpython-312.pyc | Bin 6516 -> 6525 bytes .../__pycache__/download.cpython-312.pyc | Bin 8506 -> 8515 bytes .../__pycache__/lazy_wheel.cpython-312.pyc | Bin 11661 -> 11670 bytes .../__pycache__/session.cpython-312.pyc | Bin 18880 -> 18889 bytes .../network/__pycache__/utils.cpython-312.pyc | Bin 2258 -> 2267 bytes .../__pycache__/xmlrpc.cpython-312.pyc | Bin 2947 -> 2956 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 195 -> 204 bytes .../__pycache__/check.cpython-312.pyc | Bin 7151 -> 7160 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 10179 -> 10188 bytes .../__pycache__/prepare.cpython-312.pyc | Bin 25868 -> 25877 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 201 -> 210 bytes .../__pycache__/build_tracker.cpython-312.pyc | Bin 7717 -> 7726 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 1878 -> 1887 bytes .../metadata_editable.cpython-312.pyc | Bin 1912 -> 1921 bytes .../metadata_legacy.cpython-312.pyc | Bin 3025 -> 3034 bytes .../build/__pycache__/wheel.cpython-312.pyc | Bin 1682 -> 1691 bytes .../wheel_editable.cpython-312.pyc | Bin 2023 -> 2032 bytes .../__pycache__/wheel_legacy.cpython-312.pyc | Bin 3855 -> 3864 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 264 -> 273 bytes .../editable_legacy.cpython-312.pyc | Bin 1817 -> 1826 bytes .../install/__pycache__/wheel.cpython-312.pyc | Bin 34244 -> 34253 bytes .../req/__pycache__/__init__.cpython-312.pyc | Bin 3474 -> 3483 bytes .../__pycache__/constructors.cpython-312.pyc | Bin 21237 -> 21246 bytes .../req/__pycache__/req_file.cpython-312.pyc | Bin 21479 -> 21488 bytes .../__pycache__/req_install.cpython-312.pyc | Bin 38537 -> 38546 bytes .../req/__pycache__/req_set.cpython-312.pyc | Bin 5473 -> 5482 bytes .../__pycache__/req_uninstall.cpython-312.pyc | Bin 32248 -> 32257 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 195 -> 204 bytes .../__pycache__/base.cpython-312.pyc | Bin 1183 -> 1192 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 202 -> 211 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 22615 -> 22624 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 206 -> 215 bytes .../__pycache__/base.cpython-312.pyc | Bin 8146 -> 8155 bytes .../__pycache__/candidates.cpython-312.pyc | Bin 29191 -> 29200 bytes .../__pycache__/factory.cpython-312.pyc | Bin 32339 -> 32348 bytes .../found_candidates.cpython-312.pyc | Bin 6805 -> 6814 bytes .../__pycache__/provider.cpython-312.pyc | Bin 10521 -> 10530 bytes .../__pycache__/reporter.cpython-312.pyc | Bin 5043 -> 5052 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 15348 -> 15357 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 12349 -> 12358 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 190 -> 199 bytes .../__pycache__/_jaraco_text.cpython-312.pyc | Bin 4530 -> 4539 bytes .../utils/__pycache__/_log.cpython-312.pyc | Bin 1861 -> 1870 bytes .../utils/__pycache__/appdirs.cpython-312.pyc | Bin 2405 -> 2414 bytes .../utils/__pycache__/compat.cpython-312.pyc | Bin 2902 -> 2911 bytes .../compatibility_tags.cpython-312.pyc | Bin 5556 -> 5565 bytes .../__pycache__/datetime.cpython-312.pyc | Bin 679 -> 688 bytes .../__pycache__/deprecation.cpython-312.pyc | Bin 4185 -> 4194 bytes .../direct_url_helpers.cpython-312.pyc | Bin 3531 -> 3540 bytes .../__pycache__/egg_link.cpython-312.pyc | Bin 3221 -> 3230 bytes .../__pycache__/encoding.cpython-312.pyc | Bin 2153 -> 2162 bytes .../__pycache__/entrypoints.cpython-312.pyc | Bin 3988 -> 3997 bytes .../__pycache__/filesystem.cpython-312.pyc | Bin 7347 -> 7356 bytes .../__pycache__/filetypes.cpython-312.pyc | Bin 1159 -> 1168 bytes .../utils/__pycache__/glibc.cpython-312.pyc | Bin 2414 -> 2423 bytes .../utils/__pycache__/hashes.cpython-312.pyc | Bin 7644 -> 7653 bytes .../utils/__pycache__/logging.cpython-312.pyc | Bin 13552 -> 13561 bytes .../utils/__pycache__/misc.cpython-312.pyc | Bin 33554 -> 33563 bytes .../__pycache__/packaging.cpython-312.pyc | Bin 2578 -> 2587 bytes .../utils/__pycache__/retry.cpython-312.pyc | Bin 2103 -> 2112 bytes .../setuptools_build.cpython-312.pyc | Bin 4545 -> 4554 bytes .../__pycache__/subprocess.cpython-312.pyc | Bin 8659 -> 8668 bytes .../__pycache__/temp_dir.cpython-312.pyc | Bin 12057 -> 12066 bytes .../__pycache__/unpacking.cpython-312.pyc | Bin 13526 -> 13535 bytes .../utils/__pycache__/urls.cpython-312.pyc | Bin 2072 -> 2081 bytes .../__pycache__/virtualenv.cpython-312.pyc | Bin 4475 -> 4484 bytes .../utils/__pycache__/wheel.cpython-312.pyc | Bin 5894 -> 5903 bytes .../vcs/__pycache__/__init__.cpython-312.pyc | Bin 529 -> 538 bytes .../vcs/__pycache__/bazaar.cpython-312.pyc | Bin 5059 -> 5068 bytes .../vcs/__pycache__/git.cpython-312.pyc | Bin 19025 -> 19034 bytes .../vcs/__pycache__/mercurial.cpython-312.pyc | Bin 7610 -> 7619 bytes .../__pycache__/subversion.cpython-312.pyc | Bin 12523 -> 12532 bytes .../versioncontrol.cpython-312.pyc | Bin 29006 -> 29015 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 4548 -> 4557 bytes .../typing_extensions.cpython-312.pyc | Bin 139544 -> 139553 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 901 -> 910 bytes .../__pycache__/_cmd.cpython-312.pyc | Bin 2645 -> 2654 bytes .../__pycache__/adapter.cpython-312.pyc | Bin 6463 -> 6472 bytes .../__pycache__/cache.cpython-312.pyc | Bin 3808 -> 3817 bytes .../__pycache__/controller.cpython-312.pyc | Bin 16223 -> 16232 bytes .../__pycache__/filewrapper.cpython-312.pyc | Bin 4346 -> 4355 bytes .../__pycache__/heuristics.cpython-312.pyc | Bin 6693 -> 6702 bytes .../__pycache__/serialize.cpython-312.pyc | Bin 5264 -> 5273 bytes .../__pycache__/wrapper.cpython-312.pyc | Bin 1673 -> 1682 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 434 -> 443 bytes .../__pycache__/file_cache.cpython-312.pyc | Bin 7787 -> 7796 bytes .../__pycache__/redis_cache.cpython-312.pyc | Bin 2737 -> 2746 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 317 -> 326 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 644 -> 653 bytes .../certifi/__pycache__/core.cpython-312.pyc | Bin 3219 -> 3228 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 1268 -> 1277 bytes .../__pycache__/compat.cpython-312.pyc | Bin 45604 -> 45613 bytes .../__pycache__/database.cpython-312.pyc | Bin 66026 -> 66035 bytes .../distlib/__pycache__/index.cpython-312.pyc | Bin 24365 -> 24374 bytes .../__pycache__/locators.cpython-312.pyc | Bin 60157 -> 60166 bytes .../__pycache__/manifest.cpython-312.pyc | Bin 15124 -> 15133 bytes .../__pycache__/markers.cpython-312.pyc | Bin 7681 -> 7690 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 41798 -> 41807 bytes .../__pycache__/resources.cpython-312.pyc | Bin 17324 -> 17333 bytes .../__pycache__/scripts.cpython-312.pyc | Bin 19791 -> 19800 bytes .../distlib/__pycache__/util.cpython-312.pyc | Bin 88255 -> 88264 bytes .../__pycache__/version.cpython-312.pyc | Bin 30365 -> 30374 bytes .../distlib/__pycache__/wheel.cpython-312.pyc | Bin 51860 -> 51869 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 959 -> 968 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 291 -> 300 bytes .../distro/__pycache__/distro.cpython-312.pyc | Bin 53844 -> 53853 bytes .../idna/__pycache__/__init__.cpython-312.pyc | Bin 880 -> 889 bytes .../idna/__pycache__/codec.cpython-312.pyc | Bin 4985 -> 4994 bytes .../idna/__pycache__/compat.cpython-312.pyc | Bin 886 -> 895 bytes .../idna/__pycache__/core.cpython-312.pyc | Bin 15847 -> 15856 bytes .../idna/__pycache__/idnadata.cpython-312.pyc | Bin 99475 -> 99484 bytes .../__pycache__/intranges.cpython-312.pyc | Bin 2637 -> 2646 bytes .../__pycache__/package_data.cpython-312.pyc | Bin 215 -> 224 bytes .../__pycache__/uts46data.cpython-312.pyc | Bin 158847 -> 158856 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 1740 -> 1749 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 2024 -> 2033 bytes .../msgpack/__pycache__/ext.cpython-312.pyc | Bin 8169 -> 8178 bytes .../__pycache__/fallback.cpython-312.pyc | Bin 42073 -> 42082 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 557 -> 566 bytes .../__pycache__/_elffile.cpython-312.pyc | Bin 4973 -> 4982 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 9729 -> 9738 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 4568 -> 4577 bytes .../__pycache__/_parser.cpython-312.pyc | Bin 13999 -> 14008 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 3240 -> 3249 bytes .../__pycache__/_tokenizer.cpython-312.pyc | Bin 7910 -> 7919 bytes .../__pycache__/markers.cpython-312.pyc | Bin 11012 -> 11021 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 24991 -> 25000 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 4409 -> 4418 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 38763 -> 38772 bytes .../__pycache__/tags.cpython-312.pyc | Bin 21452 -> 21461 bytes .../__pycache__/utils.cpython-312.pyc | Bin 7338 -> 7347 bytes .../__pycache__/version.cpython-312.pyc | Bin 19501 -> 19510 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 161524 -> 161533 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 19828 -> 19837 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 1967 -> 1976 bytes .../__pycache__/android.cpython-312.pyc | Bin 10706 -> 10715 bytes .../__pycache__/api.cpython-312.pyc | Bin 12930 -> 12939 bytes .../__pycache__/macos.cpython-312.pyc | Bin 8006 -> 8015 bytes .../__pycache__/unix.cpython-312.pyc | Bin 15031 -> 15040 bytes .../__pycache__/version.cpython-312.pyc | Bin 596 -> 605 bytes .../__pycache__/windows.cpython-312.pyc | Bin 13672 -> 13681 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 3488 -> 3497 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 734 -> 743 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 26668 -> 26677 bytes .../__pycache__/console.cpython-312.pyc | Bin 2633 -> 2642 bytes .../__pycache__/filter.cpython-312.pyc | Bin 3226 -> 3235 bytes .../__pycache__/formatter.cpython-312.pyc | Bin 4720 -> 4729 bytes .../__pycache__/lexer.cpython-312.pyc | Bin 38465 -> 38474 bytes .../__pycache__/modeline.cpython-312.pyc | Bin 1569 -> 1578 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 2628 -> 2637 bytes .../__pycache__/regexopt.cpython-312.pyc | Bin 4081 -> 4090 bytes .../__pycache__/scanner.cpython-312.pyc | Bin 4756 -> 4765 bytes .../__pycache__/sphinxext.cpython-312.pyc | Bin 12141 -> 12150 bytes .../__pycache__/style.cpython-312.pyc | Bin 6717 -> 6726 bytes .../__pycache__/token.cpython-312.pyc | Bin 8194 -> 8203 bytes .../__pycache__/unistring.cpython-312.pyc | Bin 33011 -> 33020 bytes .../pygments/__pycache__/util.cpython-312.pyc | Bin 14083 -> 14092 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 37979 -> 37988 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 6953 -> 6962 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 4215 -> 4224 bytes .../__pycache__/bbcode.cpython-312.pyc | Bin 4232 -> 4241 bytes .../__pycache__/groff.cpython-312.pyc | Bin 7332 -> 7341 bytes .../__pycache__/html.cpython-312.pyc | Bin 41097 -> 41106 bytes .../__pycache__/img.cpython-312.pyc | Bin 28653 -> 28662 bytes .../__pycache__/irc.cpython-312.pyc | Bin 6065 -> 6074 bytes .../__pycache__/latex.cpython-312.pyc | Bin 20171 -> 20180 bytes .../__pycache__/other.cpython-312.pyc | Bin 6899 -> 6908 bytes .../__pycache__/pangomarkup.cpython-312.pyc | Bin 2968 -> 2977 bytes .../__pycache__/rtf.cpython-312.pyc | Bin 13824 -> 13833 bytes .../__pycache__/svg.cpython-312.pyc | Bin 9151 -> 9160 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 5829 -> 5838 bytes .../__pycache__/terminal256.cpython-312.pyc | Bin 15157 -> 15166 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 14735 -> 14744 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 68263 -> 68272 bytes .../lexers/__pycache__/python.cpython-312.pyc | Bin 42969 -> 42978 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 2671 -> 2680 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 3648 -> 3657 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 613 -> 622 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 374 -> 383 bytes .../__pycache__/_impl.cpython-312.pyc | Bin 14725 -> 14734 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 1080 -> 1089 bytes .../__pycache__/_in_process.cpython-312.pyc | Bin 14397 -> 14406 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 5253 -> 5262 bytes .../__pycache__/__version__.cpython-312.pyc | Bin 584 -> 593 bytes .../_internal_utils.cpython-312.pyc | Bin 2024 -> 2033 bytes .../__pycache__/adapters.cpython-312.pyc | Bin 28437 -> 28446 bytes .../requests/__pycache__/api.cpython-312.pyc | Bin 7204 -> 7213 bytes .../requests/__pycache__/auth.cpython-312.pyc | Bin 13923 -> 13932 bytes .../__pycache__/certs.cpython-312.pyc | Bin 922 -> 931 bytes .../__pycache__/compat.cpython-312.pyc | Bin 1677 -> 1686 bytes .../__pycache__/cookies.cpython-312.pyc | Bin 25276 -> 25285 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 7598 -> 7607 bytes .../requests/__pycache__/help.cpython-312.pyc | Bin 4228 -> 4237 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 1052 -> 1061 bytes .../__pycache__/models.cpython-312.pyc | Bin 35478 -> 35487 bytes .../__pycache__/packages.cpython-312.pyc | Bin 1287 -> 1296 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 27882 -> 27891 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 6031 -> 6040 bytes .../__pycache__/structures.cpython-312.pyc | Bin 5617 -> 5626 bytes .../__pycache__/utils.cpython-312.pyc | Bin 36442 -> 36451 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 634 -> 643 bytes .../__pycache__/providers.cpython-312.pyc | Bin 6851 -> 6860 bytes .../__pycache__/reporters.cpython-312.pyc | Bin 2654 -> 2663 bytes .../__pycache__/resolvers.cpython-312.pyc | Bin 25897 -> 25906 bytes .../__pycache__/structs.cpython-312.pyc | Bin 10506 -> 10515 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 200 -> 209 bytes .../collections_abc.cpython-312.pyc | Bin 420 -> 429 bytes .../rich/__pycache__/__init__.cpython-312.pyc | Bin 7015 -> 7024 bytes .../rich/__pycache__/__main__.cpython-312.pyc | Bin 10304 -> 10313 bytes .../__pycache__/_cell_widths.cpython-312.pyc | Bin 7872 -> 7881 bytes .../__pycache__/_emoji_codes.cpython-312.pyc | Bin 205976 -> 205985 bytes .../_emoji_replace.cpython-312.pyc | Bin 1729 -> 1738 bytes .../_export_format.cpython-312.pyc | Bin 2349 -> 2358 bytes .../__pycache__/_extension.cpython-312.pyc | Bin 537 -> 546 bytes .../rich/__pycache__/_fileno.cpython-312.pyc | Bin 855 -> 864 bytes .../rich/__pycache__/_inspect.cpython-312.pyc | Bin 12077 -> 12086 bytes .../__pycache__/_log_render.cpython-312.pyc | Bin 4147 -> 4156 bytes .../rich/__pycache__/_loop.cpython-312.pyc | Bin 1885 -> 1894 bytes .../__pycache__/_null_file.cpython-312.pyc | Bin 3620 -> 3629 bytes .../__pycache__/_palettes.cpython-312.pyc | Bin 5160 -> 5169 bytes .../rich/__pycache__/_pick.cpython-312.pyc | Bin 726 -> 735 bytes .../rich/__pycache__/_ratio.cpython-312.pyc | Bin 6577 -> 6586 bytes .../__pycache__/_spinners.cpython-312.pyc | Bin 13179 -> 13188 bytes .../rich/__pycache__/_stack.cpython-312.pyc | Bin 965 -> 974 bytes .../rich/__pycache__/_timer.cpython-312.pyc | Bin 865 -> 874 bytes .../_win32_console.cpython-312.pyc | Bin 28976 -> 28985 bytes .../rich/__pycache__/_windows.cpython-312.pyc | Bin 2490 -> 2499 bytes .../_windows_renderer.cpython-312.pyc | Bin 3573 -> 3582 bytes .../rich/__pycache__/_wrap.cpython-312.pyc | Bin 3336 -> 3345 bytes .../rich/__pycache__/abc.cpython-312.pyc | Bin 1608 -> 1617 bytes .../rich/__pycache__/align.cpython-312.pyc | Bin 12322 -> 12331 bytes .../rich/__pycache__/ansi.cpython-312.pyc | Bin 9106 -> 9115 bytes .../rich/__pycache__/bar.cpython-312.pyc | Bin 4272 -> 4281 bytes .../rich/__pycache__/box.cpython-312.pyc | Bin 11858 -> 11867 bytes .../rich/__pycache__/cells.cpython-312.pyc | Bin 5819 -> 5828 bytes .../rich/__pycache__/color.cpython-312.pyc | Bin 26570 -> 26579 bytes .../__pycache__/color_triplet.cpython-312.pyc | Bin 1701 -> 1710 bytes .../rich/__pycache__/columns.cpython-312.pyc | Bin 8587 -> 8596 bytes .../rich/__pycache__/console.cpython-312.pyc | Bin 113694 -> 113703 bytes .../__pycache__/constrain.cpython-312.pyc | Bin 2258 -> 2267 bytes .../__pycache__/containers.cpython-312.pyc | Bin 9231 -> 9240 bytes .../rich/__pycache__/control.cpython-312.pyc | Bin 10929 -> 10938 bytes .../default_styles.cpython-312.pyc | Bin 10373 -> 10382 bytes .../rich/__pycache__/diagnose.cpython-312.pyc | Bin 1487 -> 1496 bytes .../rich/__pycache__/emoji.cpython-312.pyc | Bin 4209 -> 4218 bytes .../rich/__pycache__/errors.cpython-312.pyc | Bin 1845 -> 1854 bytes .../__pycache__/file_proxy.cpython-312.pyc | Bin 3577 -> 3586 bytes .../rich/__pycache__/filesize.cpython-312.pyc | Bin 3082 -> 3091 bytes .../__pycache__/highlighter.cpython-312.pyc | Bin 9899 -> 9908 bytes .../rich/__pycache__/json.cpython-312.pyc | Bin 6035 -> 6044 bytes .../rich/__pycache__/jupyter.cpython-312.pyc | Bin 5209 -> 5218 bytes .../rich/__pycache__/layout.cpython-312.pyc | Bin 20220 -> 20229 bytes .../rich/__pycache__/live.cpython-312.pyc | Bin 19143 -> 19152 bytes .../__pycache__/live_render.cpython-312.pyc | Bin 4894 -> 4903 bytes .../rich/__pycache__/logging.cpython-312.pyc | Bin 13554 -> 13563 bytes .../rich/__pycache__/markup.cpython-312.pyc | Bin 9590 -> 9599 bytes .../rich/__pycache__/measure.cpython-312.pyc | Bin 6376 -> 6385 bytes .../rich/__pycache__/padding.cpython-312.pyc | Bin 7134 -> 7143 bytes .../rich/__pycache__/pager.cpython-312.pyc | Bin 1820 -> 1829 bytes .../rich/__pycache__/palette.cpython-312.pyc | Bin 5314 -> 5323 bytes .../rich/__pycache__/panel.cpython-312.pyc | Bin 12188 -> 12197 bytes .../rich/__pycache__/pretty.cpython-312.pyc | Bin 40165 -> 40174 bytes .../rich/__pycache__/progress.cpython-312.pyc | Bin 75119 -> 75128 bytes .../__pycache__/progress_bar.cpython-312.pyc | Bin 10389 -> 10398 bytes .../rich/__pycache__/prompt.cpython-312.pyc | Bin 14787 -> 14796 bytes .../rich/__pycache__/protocol.cpython-312.pyc | Bin 1792 -> 1801 bytes .../rich/__pycache__/region.cpython-312.pyc | Bin 567 -> 576 bytes .../rich/__pycache__/repr.cpython-312.pyc | Bin 6624 -> 6633 bytes .../rich/__pycache__/rule.cpython-312.pyc | Bin 6568 -> 6577 bytes .../rich/__pycache__/scope.cpython-312.pyc | Bin 3830 -> 3839 bytes .../rich/__pycache__/screen.cpython-312.pyc | Bin 2484 -> 2493 bytes .../rich/__pycache__/segment.cpython-312.pyc | Bin 28161 -> 28170 bytes .../rich/__pycache__/spinner.cpython-312.pyc | Bin 6064 -> 6073 bytes .../rich/__pycache__/status.cpython-312.pyc | Bin 6068 -> 6077 bytes .../rich/__pycache__/style.cpython-312.pyc | Bin 33514 -> 33523 bytes .../rich/__pycache__/styled.cpython-312.pyc | Bin 2139 -> 2148 bytes .../rich/__pycache__/syntax.cpython-312.pyc | Bin 39975 -> 39984 bytes .../rich/__pycache__/table.cpython-312.pyc | Bin 43584 -> 43593 bytes .../terminal_theme.cpython-312.pyc | Bin 3348 -> 3357 bytes .../rich/__pycache__/text.cpython-312.pyc | Bin 60893 -> 60902 bytes .../rich/__pycache__/theme.cpython-312.pyc | Bin 6340 -> 6349 bytes .../rich/__pycache__/themes.cpython-312.pyc | Bin 314 -> 323 bytes .../__pycache__/traceback.cpython-312.pyc | Bin 31548 -> 31557 bytes .../rich/__pycache__/tree.cpython-312.pyc | Bin 11439 -> 11448 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 384 -> 393 bytes .../tomli/__pycache__/_parser.cpython-312.pyc | Bin 26927 -> 26936 bytes .../tomli/__pycache__/_re.cpython-312.pyc | Bin 3908 -> 3917 bytes .../tomli/__pycache__/_types.cpython-312.pyc | Bin 366 -> 375 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 618 -> 627 bytes .../__pycache__/_api.cpython-312.pyc | Bin 16541 -> 16550 bytes .../__pycache__/_macos.cpython-312.pyc | Bin 16577 -> 16586 bytes .../__pycache__/_openssl.cpython-312.pyc | Bin 2215 -> 2224 bytes .../_ssl_constants.cpython-312.pyc | Bin 1099 -> 1108 bytes .../__pycache__/_windows.cpython-312.pyc | Bin 15750 -> 15759 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 3405 -> 3414 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 16488 -> 16497 bytes .../__pycache__/_version.cpython-312.pyc | Bin 218 -> 227 bytes .../__pycache__/connection.cpython-312.pyc | Bin 20407 -> 20416 bytes .../connectionpool.cpython-312.pyc | Bin 36442 -> 36451 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 13493 -> 13502 bytes .../__pycache__/fields.cpython-312.pyc | Bin 10413 -> 10422 bytes .../__pycache__/filepost.cpython-312.pyc | Bin 4018 -> 4027 bytes .../__pycache__/poolmanager.cpython-312.pyc | Bin 20472 -> 20481 bytes .../__pycache__/request.cpython-312.pyc | Bin 7294 -> 7303 bytes .../__pycache__/response.cpython-312.pyc | Bin 33968 -> 33977 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 198 -> 207 bytes .../_appengine_environ.cpython-312.pyc | Bin 1848 -> 1857 bytes .../__pycache__/appengine.cpython-312.pyc | Bin 11564 -> 11573 bytes .../__pycache__/ntlmpool.cpython-312.pyc | Bin 5719 -> 5728 bytes .../__pycache__/pyopenssl.cpython-312.pyc | Bin 24450 -> 24459 bytes .../securetransport.cpython-312.pyc | Bin 35553 -> 35562 bytes .../contrib/__pycache__/socks.cpython-312.pyc | Bin 7511 -> 7520 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 215 -> 224 bytes .../__pycache__/bindings.cpython-312.pyc | Bin 17427 -> 17436 bytes .../__pycache__/low_level.cpython-312.pyc | Bin 14801 -> 14810 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 199 -> 208 bytes .../packages/__pycache__/six.cpython-312.pyc | Bin 41319 -> 41328 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 209 -> 218 bytes .../__pycache__/makefile.cpython-312.pyc | Bin 1825 -> 1834 bytes .../weakref_finalize.cpython-312.pyc | Bin 7331 -> 7340 bytes .../util/__pycache__/__init__.cpython-312.pyc | Bin 1146 -> 1155 bytes .../__pycache__/connection.cpython-312.pyc | Bin 4756 -> 4765 bytes .../util/__pycache__/proxy.cpython-312.pyc | Bin 1552 -> 1561 bytes .../util/__pycache__/queue.cpython-312.pyc | Bin 1352 -> 1361 bytes .../util/__pycache__/request.cpython-312.pyc | Bin 4183 -> 4192 bytes .../util/__pycache__/response.cpython-312.pyc | Bin 2989 -> 2998 bytes .../util/__pycache__/retry.cpython-312.pyc | Bin 21697 -> 21706 bytes .../util/__pycache__/ssl_.cpython-312.pyc | Bin 15103 -> 15112 bytes .../ssl_match_hostname.cpython-312.pyc | Bin 5071 -> 5080 bytes .../__pycache__/ssltransport.cpython-312.pyc | Bin 10772 -> 10781 bytes .../util/__pycache__/timeout.cpython-312.pyc | Bin 11139 -> 11148 bytes .../util/__pycache__/url.cpython-312.pyc | Bin 15795 -> 15804 bytes .../util/__pycache__/wait.cpython-312.pyc | Bin 4403 -> 4412 bytes .../requests-2.32.3.dist-info/INSTALLER | 1 - .../requests-2.32.3.dist-info/LICENSE | 175 - .../requests-2.32.3.dist-info/METADATA | 119 - .../requests-2.32.3.dist-info/RECORD | 43 - .../requests-2.32.3.dist-info/REQUESTED | 0 .../requests-2.32.3.dist-info/WHEEL | 5 - .../requests-2.32.3.dist-info/top_level.txt | 1 - .../site-packages/requests/__init__.py | 184 - .../__pycache__/__init__.cpython-312.pyc | Bin 5408 -> 0 bytes .../__pycache__/__version__.cpython-312.pyc | Bin 572 -> 0 bytes .../_internal_utils.cpython-312.pyc | Bin 2012 -> 0 bytes .../__pycache__/adapters.cpython-312.pyc | Bin 28352 -> 0 bytes .../requests/__pycache__/api.cpython-312.pyc | Bin 7192 -> 0 bytes .../requests/__pycache__/auth.cpython-312.pyc | Bin 13911 -> 0 bytes .../__pycache__/certs.cpython-312.pyc | Bin 654 -> 0 bytes .../__pycache__/compat.cpython-312.pyc | Bin 2073 -> 0 bytes .../__pycache__/cookies.cpython-312.pyc | Bin 25264 -> 0 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 7573 -> 0 bytes .../requests/__pycache__/help.cpython-312.pyc | Bin 4315 -> 0 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 1040 -> 0 bytes .../__pycache__/models.cpython-312.pyc | Bin 35394 -> 0 bytes .../__pycache__/packages.cpython-312.pyc | Bin 1127 -> 0 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 27870 -> 0 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 6019 -> 0 bytes .../__pycache__/structures.cpython-312.pyc | Bin 5605 -> 0 bytes .../__pycache__/utils.cpython-312.pyc | Bin 36418 -> 0 bytes .../site-packages/requests/__version__.py | 14 - .../site-packages/requests/_internal_utils.py | 50 - .../site-packages/requests/adapters.py | 719 -- .../python3.12/site-packages/requests/api.py | 157 - .../python3.12/site-packages/requests/auth.py | 314 - .../site-packages/requests/certs.py | 17 - .../site-packages/requests/compat.py | 94 - .../site-packages/requests/cookies.py | 561 -- .../site-packages/requests/exceptions.py | 151 - .../python3.12/site-packages/requests/help.py | 134 - .../site-packages/requests/hooks.py | 33 - .../site-packages/requests/models.py | 1037 -- .../site-packages/requests/packages.py | 23 - .../site-packages/requests/sessions.py | 831 -- .../site-packages/requests/status_codes.py | 128 - .../site-packages/requests/structures.py | 99 - .../site-packages/requests/utils.py | 1096 --- .../urllib3-2.2.2.dist-info/INSTALLER | 1 - .../urllib3-2.2.2.dist-info/METADATA | 154 - .../urllib3-2.2.2.dist-info/RECORD | 75 - .../urllib3-2.2.2.dist-info/WHEEL | 4 - .../licenses/LICENSE.txt | 21 - .../site-packages/urllib3/__init__.py | 211 - .../__pycache__/__init__.cpython-312.pyc | Bin 7303 -> 0 bytes .../_base_connection.cpython-312.pyc | Bin 6850 -> 0 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 22702 -> 0 bytes .../_request_methods.cpython-312.pyc | Bin 10665 -> 0 bytes .../__pycache__/_version.cpython-312.pyc | Bin 256 -> 0 bytes .../__pycache__/connection.cpython-312.pyc | Bin 32253 -> 0 bytes .../connectionpool.cpython-312.pyc | Bin 39736 -> 0 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 15825 -> 0 bytes .../__pycache__/fields.cpython-312.pyc | Bin 12065 -> 0 bytes .../__pycache__/filepost.cpython-312.pyc | Bin 3506 -> 0 bytes .../urllib3/__pycache__/http2.cpython-312.pyc | Bin 11028 -> 0 bytes .../__pycache__/poolmanager.cpython-312.pyc | Bin 24067 -> 0 bytes .../__pycache__/response.cpython-312.pyc | Bin 50453 -> 0 bytes .../site-packages/urllib3/_base_connection.py | 172 - .../site-packages/urllib3/_collections.py | 483 - .../site-packages/urllib3/_request_methods.py | 279 - .../site-packages/urllib3/_version.py | 4 - .../site-packages/urllib3/connection.py | 929 -- .../site-packages/urllib3/connectionpool.py | 1182 --- .../site-packages/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 186 -> 0 bytes .../__pycache__/pyopenssl.cpython-312.pyc | Bin 26923 -> 0 bytes .../contrib/__pycache__/socks.cpython-312.pyc | Bin 8164 -> 0 bytes .../urllib3/contrib/emscripten/__init__.py | 16 - .../__pycache__/__init__.cpython-312.pyc | Bin 894 -> 0 bytes .../__pycache__/connection.cpython-312.pyc | Bin 10233 -> 0 bytes .../__pycache__/fetch.cpython-312.pyc | Bin 18366 -> 0 bytes .../__pycache__/request.cpython-312.pyc | Bin 1414 -> 0 bytes .../__pycache__/response.cpython-312.pyc | Bin 12699 -> 0 bytes .../urllib3/contrib/emscripten/connection.py | 254 - .../emscripten/emscripten_fetch_worker.js | 110 - .../urllib3/contrib/emscripten/fetch.py | 418 - .../urllib3/contrib/emscripten/request.py | 22 - .../urllib3/contrib/emscripten/response.py | 285 - .../urllib3/contrib/pyopenssl.py | 548 -- .../site-packages/urllib3/contrib/socks.py | 228 - .../site-packages/urllib3/exceptions.py | 321 - .../site-packages/urllib3/fields.py | 341 - .../site-packages/urllib3/filepost.py | 89 - .../python3.12/site-packages/urllib3/http2.py | 230 - .../site-packages/urllib3/poolmanager.py | 637 -- .../python3.12/site-packages/urllib3/py.typed | 2 - .../site-packages/urllib3/response.py | 1265 --- .../site-packages/urllib3/util/__init__.py | 42 - .../util/__pycache__/__init__.cpython-312.pyc | Bin 999 -> 0 bytes .../__pycache__/connection.cpython-312.pyc | Bin 4742 -> 0 bytes .../util/__pycache__/proxy.cpython-312.pyc | Bin 1211 -> 0 bytes .../util/__pycache__/request.cpython-312.pyc | Bin 8030 -> 0 bytes .../util/__pycache__/response.cpython-312.pyc | Bin 2867 -> 0 bytes .../util/__pycache__/retry.cpython-312.pyc | Bin 20280 -> 0 bytes .../util/__pycache__/ssl_.cpython-312.pyc | Bin 16568 -> 0 bytes .../ssl_match_hostname.cpython-312.pyc | Bin 5551 -> 0 bytes .../__pycache__/ssltransport.cpython-312.pyc | Bin 13545 -> 0 bytes .../util/__pycache__/timeout.cpython-312.pyc | Bin 11683 -> 0 bytes .../util/__pycache__/url.cpython-312.pyc | Bin 16223 -> 0 bytes .../util/__pycache__/util.cpython-312.pyc | Bin 1988 -> 0 bytes .../util/__pycache__/wait.cpython-312.pyc | Bin 3434 -> 0 bytes .../site-packages/urllib3/util/connection.py | 137 - .../site-packages/urllib3/util/proxy.py | 43 - .../site-packages/urllib3/util/request.py | 256 - .../site-packages/urllib3/util/response.py | 101 - .../site-packages/urllib3/util/retry.py | 533 - .../site-packages/urllib3/util/ssl_.py | 509 - .../urllib3/util/ssl_match_hostname.py | 159 - .../urllib3/util/ssltransport.py | 279 - .../site-packages/urllib3/util/timeout.py | 275 - .../site-packages/urllib3/util/url.py | 471 - .../site-packages/urllib3/util/util.py | 42 - .../site-packages/urllib3/util/wait.py | 124 - .venv/pyvenv.cfg | 4 +- .vscode/settings.json | 3 + blocked_ips.txt | 280 + ng_labels_with_aqua.py | 186 + salt/init.sls | 36 +- 905 files changed, 648 insertions(+), 71165 deletions(-) create mode 100644 .gitignore delete mode 100755 .venv/bin/normalizer delete mode 100644 .venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/LICENSE delete mode 100644 .venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/METADATA delete mode 100644 .venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/RECORD delete mode 100644 .venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/WHEEL delete mode 100644 .venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.12/site-packages/certifi/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/certifi/__main__.py delete mode 100644 .venv/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/certifi/__pycache__/__main__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/certifi/cacert.pem delete mode 100644 .venv/lib/python3.12/site-packages/certifi/core.py delete mode 100644 .venv/lib/python3.12/site-packages/certifi/py.typed delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/METADATA delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/RECORD delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__main__.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/__main__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/api.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/cd.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/constant.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/legacy.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/md.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/models.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/utils.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/version.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/api.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cd.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/constant.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/legacy.py delete mode 100755 .venv/lib/python3.12/site-packages/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/md.py delete mode 100755 .venv/lib/python3.12/site-packages/charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/models.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/py.typed delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/utils.py delete mode 100644 .venv/lib/python3.12/site-packages/charset_normalizer/version.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_asyncbackend.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_asyncio_backend.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_ddr.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_features.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_immutable_ctx.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_trio_backend.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/asyncbackend.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/asyncquery.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/asyncresolver.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/dnssec.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/dnssectypes.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/e164.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/edns.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/entropy.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/enum.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/exception.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/flags.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/grange.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/immutable.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/inet.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/ipv4.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/ipv6.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/message.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/name.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/namedict.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/nameserver.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/node.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/opcode.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/query.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rcode.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdata.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdataclass.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdataset.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdatatype.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/renderer.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/resolver.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/reversename.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rrset.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/serial.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/set.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/tokenizer.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/transaction.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/tsig.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/tsigkeyring.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/ttl.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/update.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/version.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/versioned.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/win32util.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/wire.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/xfr.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/zone.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/zonefile.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/zonetypes.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/_asyncbackend.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/_asyncio_backend.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/_ddr.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/_features.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/_immutable_ctx.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/_trio_backend.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/asyncbackend.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/asyncquery.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/asyncresolver.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssec.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/base.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/dsa.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/eddsa.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/base.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/cryptography.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/dsa.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/ecdsa.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/eddsa.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/rsa.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/dnssectypes.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/e164.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/edns.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/entropy.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/enum.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/exception.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/flags.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/grange.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/immutable.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/inet.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/ipv4.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/ipv6.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/message.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/name.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/namedict.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/nameserver.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/node.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/opcode.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/py.typed delete mode 100644 .venv/lib/python3.12/site-packages/dns/query.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_asyncio.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_common.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_sync.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_trio.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_asyncio.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_common.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_sync.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_trio.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rcode.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdata.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdataclass.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdataset.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdatatype.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AVC.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/EUI64.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/GPOS.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NID.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/X25.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NAPTR.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/euibase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/util.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/util.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/renderer.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/resolver.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/reversename.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/rrset.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/serial.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/set.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/tokenizer.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/transaction.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/tsig.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/tsigkeyring.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/ttl.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/update.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/version.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/versioned.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/win32util.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/wire.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/xfr.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/zone.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/zonefile.py delete mode 100644 .venv/lib/python3.12/site-packages/dns/zonetypes.py delete mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/METADATA delete mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/RECORD delete mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/REQUESTED delete mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/WHEEL delete mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/licenses/LICENSE delete mode 100644 .venv/lib/python3.12/site-packages/idna-3.7.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.12/site-packages/idna-3.7.dist-info/LICENSE.md delete mode 100644 .venv/lib/python3.12/site-packages/idna-3.7.dist-info/METADATA delete mode 100644 .venv/lib/python3.12/site-packages/idna-3.7.dist-info/RECORD delete mode 100644 .venv/lib/python3.12/site-packages/idna-3.7.dist-info/WHEEL delete mode 100644 .venv/lib/python3.12/site-packages/idna/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/compat.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/__pycache__/uts46data.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/idna/codec.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/compat.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/core.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/idnadata.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/intranges.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/package_data.py delete mode 100644 .venv/lib/python3.12/site-packages/idna/py.typed delete mode 100644 .venv/lib/python3.12/site-packages/idna/uts46data.py delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/LICENSE delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/METADATA delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/REQUESTED delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL delete mode 100644 .venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt delete mode 100644 .venv/lib/python3.12/site-packages/requests/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/__version__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/_internal_utils.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/adapters.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/api.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/auth.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/certs.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/compat.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/cookies.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/exceptions.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/help.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/hooks.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/models.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/packages.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/sessions.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/status_codes.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/structures.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__pycache__/utils.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/requests/__version__.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/_internal_utils.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/adapters.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/api.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/auth.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/certs.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/compat.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/cookies.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/exceptions.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/help.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/hooks.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/models.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/packages.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/sessions.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/status_codes.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/structures.py delete mode 100644 .venv/lib/python3.12/site-packages/requests/utils.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/INSTALLER delete mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/METADATA delete mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/RECORD delete mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/WHEEL delete mode 100644 .venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/licenses/LICENSE.txt delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/_base_connection.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/_collections.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/_request_methods.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/_version.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/connection.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/connectionpool.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/exceptions.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/fields.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/filepost.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/http2.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/poolmanager.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/__pycache__/response.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/_base_connection.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/_collections.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/_request_methods.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/_version.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/connection.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/connectionpool.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/__pycache__/socks.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/connection.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/fetch.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/request.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/response.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/pyopenssl.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/contrib/socks.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/exceptions.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/fields.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/filepost.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/http2.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/poolmanager.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/py.typed delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/response.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__init__.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/__init__.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/connection.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/proxy.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/request.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/response.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/retry.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/ssl_.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/ssltransport.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/timeout.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/url.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/util.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/__pycache__/wait.cpython-312.pyc delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/connection.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/proxy.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/request.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/response.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/retry.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/ssl_.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/ssl_match_hostname.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/ssltransport.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/timeout.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/url.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/util.py delete mode 100644 .venv/lib/python3.12/site-packages/urllib3/util/wait.py create mode 100644 .vscode/settings.json create mode 100644 blocked_ips.txt create mode 100644 ng_labels_with_aqua.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6769e21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/.venv/bin/activate b/.venv/bin/activate index b8a3313..9d5eb0d 100644 --- a/.venv/bin/activate +++ b/.venv/bin/activate @@ -39,10 +39,10 @@ deactivate nondestructive if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then # transform D:\path\to\venv to /d/path/to/venv on MSYS # and to /cygdrive/d/path/to/venv on Cygwin - export VIRTUAL_ENV=$(cygpath "/home/rpetrovich/code/Misc/.venv") + export VIRTUAL_ENV=$(cygpath "/home/rpetrovich/code/miscellaneous/.venv") else # use the path as-is - export VIRTUAL_ENV="/home/rpetrovich/code/Misc/.venv" + export VIRTUAL_ENV="/home/rpetrovich/code/miscellaneous/.venv" fi _OLD_VIRTUAL_PATH="$PATH" diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh index 36652e1..edba1f3 100644 --- a/.venv/bin/activate.csh +++ b/.venv/bin/activate.csh @@ -9,7 +9,7 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA # Unset irrelevant variables. deactivate nondestructive -setenv VIRTUAL_ENV "/home/rpetrovich/code/Misc/.venv" +setenv VIRTUAL_ENV "/home/rpetrovich/code/miscellaneous/.venv" set _OLD_VIRTUAL_PATH="$PATH" setenv PATH "$VIRTUAL_ENV/bin:$PATH" diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish index 6215454..3c2f55c 100644 --- a/.venv/bin/activate.fish +++ b/.venv/bin/activate.fish @@ -33,7 +33,7 @@ end # Unset irrelevant variables. deactivate nondestructive -set -gx VIRTUAL_ENV "/home/rpetrovich/code/Misc/.venv" +set -gx VIRTUAL_ENV "/home/rpetrovich/code/miscellaneous/.venv" set -gx _OLD_VIRTUAL_PATH $PATH set -gx PATH "$VIRTUAL_ENV/bin" $PATH diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer deleted file mode 100755 index d749250..0000000 --- a/.venv/bin/normalizer +++ /dev/null @@ -1,8 +0,0 @@ -#!/home/rpetrovich/code/Misc/.venv/bin/python -# -*- coding: utf-8 -*- -import re -import sys -from charset_normalizer.cli import cli_detect -if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) - sys.exit(cli_detect()) diff --git a/.venv/bin/pip b/.venv/bin/pip index 67bfcbe..a67c04d 100755 --- a/.venv/bin/pip +++ b/.venv/bin/pip @@ -1,4 +1,4 @@ -#!/home/rpetrovich/code/Misc/.venv/bin/python +#!/home/rpetrovich/code/miscellaneous/.venv/bin/python # -*- coding: utf-8 -*- import re import sys diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 index 67bfcbe..a67c04d 100755 --- a/.venv/bin/pip3 +++ b/.venv/bin/pip3 @@ -1,4 +1,4 @@ -#!/home/rpetrovich/code/Misc/.venv/bin/python +#!/home/rpetrovich/code/miscellaneous/.venv/bin/python # -*- coding: utf-8 -*- import re import sys diff --git a/.venv/bin/pip3.12 b/.venv/bin/pip3.12 index 67bfcbe..a67c04d 100755 --- a/.venv/bin/pip3.12 +++ b/.venv/bin/pip3.12 @@ -1,4 +1,4 @@ -#!/home/rpetrovich/code/Misc/.venv/bin/python +#!/home/rpetrovich/code/miscellaneous/.venv/bin/python # -*- coding: utf-8 -*- import re import sys diff --git a/.venv/bin/python b/.venv/bin/python index f140426..acd4152 120000 --- a/.venv/bin/python +++ b/.venv/bin/python @@ -1 +1 @@ -/bin/python \ No newline at end of file +/usr/bin/python \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/LICENSE deleted file mode 100644 index 62b076c..0000000 --- a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -This package contains a modified version of ca-bundle.crt: - -ca-bundle.crt -- Bundle of CA Root Certificates - -This is a bundle of X.509 certificates of public Certificate Authorities -(CA). These were automatically extracted from Mozilla's root certificates -file (certdata.txt). This file can be found in the mozilla source tree: -https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt -It contains the certificates in PEM format and therefore -can be directly used with curl / libcurl / php_curl, or with -an Apache+mod_ssl webserver for SSL client authentication. -Just configure this file as the SSLCACertificateFile.# - -***** BEGIN LICENSE BLOCK ***** -This Source Code Form is subject to the terms of the Mozilla Public License, -v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain -one at http://mozilla.org/MPL/2.0/. - -***** END LICENSE BLOCK ***** -@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/METADATA b/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/METADATA deleted file mode 100644 index 5b357f5..0000000 --- a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/METADATA +++ /dev/null @@ -1,67 +0,0 @@ -Metadata-Version: 2.1 -Name: certifi -Version: 2024.7.4 -Summary: Python package for providing Mozilla's CA Bundle. -Home-page: https://github.com/certifi/python-certifi -Author: Kenneth Reitz -Author-email: me@kennethreitz.com -License: MPL-2.0 -Project-URL: Source, https://github.com/certifi/python-certifi -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) -Classifier: Natural Language :: English -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Requires-Python: >=3.6 -License-File: LICENSE - -Certifi: Python SSL Certificates -================================ - -Certifi provides Mozilla's carefully curated collection of Root Certificates for -validating the trustworthiness of SSL certificates while verifying the identity -of TLS hosts. It has been extracted from the `Requests`_ project. - -Installation ------------- - -``certifi`` is available on PyPI. Simply install it with ``pip``:: - - $ pip install certifi - -Usage ------ - -To reference the installed certificate authority (CA) bundle, you can use the -built-in function:: - - >>> import certifi - - >>> certifi.where() - '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' - -Or from the command line:: - - $ python -m certifi - /usr/local/lib/python3.7/site-packages/certifi/cacert.pem - -Enjoy! - -.. _`Requests`: https://requests.readthedocs.io/en/master/ - -Addition/Removal of Certificates --------------------------------- - -Certifi does not support any addition/removal or other modification of the -CA trust store content. This project is intended to provide a reliable and -highly portable root of trust to python deployments. Look to upstream projects -for methods to use alternate trust. diff --git a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/RECORD b/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/RECORD deleted file mode 100644 index fea659e..0000000 --- a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/RECORD +++ /dev/null @@ -1,14 +0,0 @@ -certifi-2024.7.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -certifi-2024.7.4.dist-info/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 -certifi-2024.7.4.dist-info/METADATA,sha256=L9_EuPoQQvHFzxu03_ctaEZxhEty7inz569jGWjlLGo,2221 -certifi-2024.7.4.dist-info/RECORD,, -certifi-2024.7.4.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91 -certifi-2024.7.4.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 -certifi/__init__.py,sha256=LHXz7E80YJYBzCBv6ZyidQ5-ciYSkSebpY2E5OM0l7o,94 -certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 -certifi/__pycache__/__init__.cpython-312.pyc,, -certifi/__pycache__/__main__.cpython-312.pyc,, -certifi/__pycache__/core.cpython-312.pyc,, -certifi/cacert.pem,sha256=SIupYGAr8HzGP073rsEIaS_sQYIPwzKKjj894DgUmu4,291528 -certifi/core.py,sha256=qRDDFyXVJwTB_EmoGppaXU_R9qCZvhl-EzxPMuV3nTA,4426 -certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/WHEEL deleted file mode 100644 index 564c672..0000000 --- a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: setuptools (70.2.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/top_level.txt deleted file mode 100644 index 963eac5..0000000 --- a/.venv/lib/python3.12/site-packages/certifi-2024.7.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -certifi diff --git a/.venv/lib/python3.12/site-packages/certifi/__init__.py b/.venv/lib/python3.12/site-packages/certifi/__init__.py deleted file mode 100644 index d321f1b..0000000 --- a/.venv/lib/python3.12/site-packages/certifi/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .core import contents, where - -__all__ = ["contents", "where"] -__version__ = "2024.07.04" diff --git a/.venv/lib/python3.12/site-packages/certifi/__main__.py b/.venv/lib/python3.12/site-packages/certifi/__main__.py deleted file mode 100644 index 8945b5d..0000000 --- a/.venv/lib/python3.12/site-packages/certifi/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import argparse - -from certifi import contents, where - -parser = argparse.ArgumentParser() -parser.add_argument("-c", "--contents", action="store_true") -args = parser.parse_args() - -if args.contents: - print(contents()) -else: - print(where()) diff --git a/.venv/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/certifi/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index b599de714cae88091b6869be8f650d291c3c1251..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 305 zcmYjLJx{|h5Vi9`OQNzNBo<)IKpa9P7Q{aQ3C0Xq!M-*Yi5=M{Rhi%)u<;uZKZS)6 zWdVta4XIlvTnQxJ@b2Bcce;BSjUwcbTs%)MUHx1&fAD_AV%LdB#1Y3eYH)!YukbM9 z9`~nS;q#yf=3%n-gG?|$F!R#cG;-LtlrI+6H zPf+52;>FNP>UgPm5N|?nJvozRn~MW8@0;)Y-kUcwUuv}g)CiwWwog_3r8m{E7vS_r zf%iawpaFDrGzKU&I!0=COaul{=zCStGENq-V^h0dTk;hmCb5Y9*_oK<3@fFp9uclEsC+J*5Fh&c_{GS%Of3$HcG^Cg0nV^{{rTwx;Io&Z@$xhH} zUC==(a!w<`hcraei4(!{EDWR<^KKFIl1ewBT(E7%^?kT*RIY6D?l4tlA8QAf&fLl6 zrI?UNCC)V&%vrrJr8Qseg0oC~0{ta$_5}Cxlww{`!SfMIdN|1m#gAB-;P!}SBRpWw zaWNLXJiFh%dk>dP&{h#AFXApO@uDppMQO~kC~6nuGjBc3$#6g)@GI&B{aocUwfr>^ zLO)>jH(WpQo9`Mkzd7}rhyJx)=NOzB)TdBCc=+MY$5&sWegx~sZf)i^rf%cVy}E0i gnC{z+*Bjq0fA7}*?QhoFPlaym-+Z&hFRSAJ072Q7fdBvi diff --git a/.venv/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc b/.venv/lib/python3.12/site-packages/certifi/__pycache__/core.cpython-312.pyc deleted file mode 100644 index 0fdf9e97d52ce860660a013ac6133a8fb4df453e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3190 zcmbVO&u<&W7oXW5_S$i>N$faoQ)(-;v@T6dL-|paA1#4z3r*8135Z%q*0Q^4*0sH6 zcG@&0pd2`~rv`y&4hZGIp;b6=o_KG)>o_4C;92wD z+u51%%;$aHoBdyRca(r+r`BfQ>LcW5d1CUaYmAx8L4;uV?%6ePFvw{pEN*PKg;e`-%rQcO7`Rw_Wg% zAAk{2qsMElWwTBkRF@~tqe_s)*J(Mi*V2_DPr$7F(B*)S@YjevOuzQ zGAp=Uh4GW)Z%oY;#%JDjl|sS1YVkth4r${2$7geMcG=9as>zvs#nR_;x^0-bvzDXh zMz5HaE4h;Oey+N}=j_U}qesSaj>XN9s-|DkE}Blx2M8uH6HszhZ}rx33p&5bkh9pF z1gIr)Us5-uR82~)Bv$z9%n#BbhHT6V4{_fQk;ky*u;vtIt&-`=s2v@8+tSA5|IpQm z$cel=cfWQ0%>ALgUw*ieK3Yp3t*4)_54~`G;(o0E=D8c^ zzC3p~cK8={Xk8id{%K1DF-q8)?|^~``tc!9vI1mwhl}wiJE%izX#UU6LocJU77NU&5%wH5py86~X!D9-d4KhvG5STlZVNAB~GlPAorl2*EPZ;^s%C6Ok?_Rj8j;|}@tsqlZe!BuS z|5}&rjGl*loUksLZrHDqn17CanL%#je%ufwqRkw=9XIF}gQ0WN2J-5bg|$S5R6|BAyyF=NV8C-WciW|9t;v2Ubtt z?H*|m8cBbfxECM1b?nn)pB?{0gy%o7^LJE_9W1QBhsD({Qjt;3h%he4q3H`waWj;X-Mozuh3*=SFjy649mM=!(tV!Y~qcJml%{| z39EIMx^&jTlINp^b=L#B1df9s1Ic1NH&5L-Aw7d{SJKFZ0=v7u^utHr&=X&NVsZ~(5KrPGamvMX~hx%an7SWe&T vOEpBW5BhgEWU#P>c(M^jPa%DY299Y&NwmA!g None: - _CACERT_CTX.__exit__(None, None, None) # type: ignore[union-attr] - - -if sys.version_info >= (3, 11): - - from importlib.resources import as_file, files - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the file - # in cases where we're inside of a zipimport situation until someone - # actually calls where(), but we don't want to re-extract the file - # on every call of where(), so we'll do it once then store it in a - # global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you to - # manage the cleanup of this file, so it doesn't actually return a - # path, it returns a context manager that will give you the path - # when you enter it and will do any cleanup when you leave it. In - # the common case of not needing a temporary file, it will just - # return the file system location and the __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = as_file(files("certifi").joinpath("cacert.pem")) - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - atexit.register(exit_cacert_ctx) - - return _CACERT_PATH - - def contents() -> str: - return files("certifi").joinpath("cacert.pem").read_text(encoding="ascii") - -elif sys.version_info >= (3, 7): - - from importlib.resources import path as get_path, read_text - - _CACERT_CTX = None - _CACERT_PATH = None - - def where() -> str: - # This is slightly terrible, but we want to delay extracting the - # file in cases where we're inside of a zipimport situation until - # someone actually calls where(), but we don't want to re-extract - # the file on every call of where(), so we'll do it once then store - # it in a global variable. - global _CACERT_CTX - global _CACERT_PATH - if _CACERT_PATH is None: - # This is slightly janky, the importlib.resources API wants you - # to manage the cleanup of this file, so it doesn't actually - # return a path, it returns a context manager that will give - # you the path when you enter it and will do any cleanup when - # you leave it. In the common case of not needing a temporary - # file, it will just return the file system location and the - # __exit__() is a no-op. - # - # We also have to hold onto the actual context manager, because - # it will do the cleanup whenever it gets garbage collected, so - # we will also store that at the global level as well. - _CACERT_CTX = get_path("certifi", "cacert.pem") - _CACERT_PATH = str(_CACERT_CTX.__enter__()) - atexit.register(exit_cacert_ctx) - - return _CACERT_PATH - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") - -else: - import os - import types - from typing import Union - - Package = Union[types.ModuleType, str] - Resource = Union[str, "os.PathLike"] - - # This fallback will work for Python versions prior to 3.7 that lack the - # importlib.resources module but relies on the existing `where` function - # so won't address issues with environments like PyOxidizer that don't set - # __file__ on modules. - def read_text( - package: Package, - resource: Resource, - encoding: str = 'utf-8', - errors: str = 'strict' - ) -> str: - with open(where(), encoding=encoding) as data: - return data.read() - - # If we don't have importlib.resources, then we will just do the old logic - # of assuming we're on the filesystem and munge the path directly. - def where() -> str: - f = os.path.dirname(__file__) - - return os.path.join(f, "cacert.pem") - - def contents() -> str: - return read_text("certifi", "cacert.pem", encoding="ascii") diff --git a/.venv/lib/python3.12/site-packages/certifi/py.typed b/.venv/lib/python3.12/site-packages/certifi/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE deleted file mode 100644 index ad82355..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 TAHRI Ahmed R. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/METADATA deleted file mode 100644 index 822550e..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/METADATA +++ /dev/null @@ -1,683 +0,0 @@ -Metadata-Version: 2.1 -Name: charset-normalizer -Version: 3.3.2 -Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. -Home-page: https://github.com/Ousret/charset_normalizer -Author: Ahmed TAHRI -Author-email: ahmed.tahri@cloudnursery.dev -License: MIT -Project-URL: Bug Reports, https://github.com/Ousret/charset_normalizer/issues -Project-URL: Documentation, https://charset-normalizer.readthedocs.io/en/latest -Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: MIT License -Classifier: Intended Audience :: Developers -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Text Processing :: Linguistic -Classifier: Topic :: Utilities -Classifier: Typing :: Typed -Requires-Python: >=3.7.0 -Description-Content-Type: text/markdown -License-File: LICENSE -Provides-Extra: unicode_backport - -

Charset Detection, for Everyone 👋

- -

- The Real First Universal Charset Detector
- - - - - Download Count Total - - - - -

-

- Featured Packages
- - Static Badge - - - Static Badge - -

-

- In other language (unofficial port - by the community)
- - Static Badge - -

- -> A library that helps you read text from an unknown charset encoding.
Motivated by `chardet`, -> I'm trying to resolve the issue by taking a new approach. -> All IANA character set names for which the Python core library provides codecs are supported. - -

- >>>>> 👉 Try Me Online Now, Then Adopt Me 👈 <<<<< -

- -This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. - -| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | -|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:| -| `Fast` | ❌ | ✅ | ✅ | -| `Universal**` | ❌ | ✅ | ❌ | -| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ | -| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | -| `License` | LGPL-2.1
_restrictive_ | MIT | MPL-1.1
_restrictive_ | -| `Native Python` | ✅ | ✅ | ❌ | -| `Detect spoken language` | ❌ | ✅ | N/A | -| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ | -| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB | -| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | - -

-Reading Normalized TextCat Reading Text -

- -*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
-Did you got there because of the logs? See [https://charset-normalizer.readthedocs.io/en/latest/user/miscellaneous.html](https://charset-normalizer.readthedocs.io/en/latest/user/miscellaneous.html) - -## ⚡ Performance - -This package offer better performance than its counterpart Chardet. Here are some numbers. - -| Package | Accuracy | Mean per file (ms) | File per sec (est) | -|-----------------------------------------------|:--------:|:------------------:|:------------------:| -| [chardet](https://github.com/chardet/chardet) | 86 % | 200 ms | 5 file/sec | -| charset-normalizer | **98 %** | **10 ms** | 100 file/sec | - -| Package | 99th percentile | 95th percentile | 50th percentile | -|-----------------------------------------------|:---------------:|:---------------:|:---------------:| -| [chardet](https://github.com/chardet/chardet) | 1200 ms | 287 ms | 23 ms | -| charset-normalizer | 100 ms | 50 ms | 5 ms | - -Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. - -> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. -> And yes, these results might change at any time. The dataset can be updated to include more files. -> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. -> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability -> (eg. Supported Encoding) Challenge-them if you want. - -## ✨ Installation - -Using pip: - -```sh -pip install charset-normalizer -U -``` - -## 🚀 Basic Usage - -### CLI -This package comes with a CLI. - -``` -usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] - file [file ...] - -The Real First Universal Charset Detector. Discover originating encoding used -on text file. Normalize text to unicode. - -positional arguments: - files File(s) to be analysed - -optional arguments: - -h, --help show this help message and exit - -v, --verbose Display complementary information about file if any. - Stdout will contain logs about the detection process. - -a, --with-alternative - Output complementary possibilities if any. Top-level - JSON WILL be a list. - -n, --normalize Permit to normalize input file. If not set, program - does not write anything. - -m, --minimal Only output the charset detected to STDOUT. Disabling - JSON output. - -r, --replace Replace file when trying to normalize it instead of - creating a new one. - -f, --force Replace file without asking if you are sure, use this - flag with caution. - -t THRESHOLD, --threshold THRESHOLD - Define a custom maximum amount of chaos allowed in - decoded content. 0. <= chaos <= 1. - --version Show version information and exit. -``` - -```bash -normalizer ./data/sample.1.fr.srt -``` - -or - -```bash -python -m charset_normalizer ./data/sample.1.fr.srt -``` - -🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. - -```json -{ - "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", - "encoding": "cp1252", - "encoding_aliases": [ - "1252", - "windows_1252" - ], - "alternative_encodings": [ - "cp1254", - "cp1256", - "cp1258", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - "mbcs" - ], - "language": "French", - "alphabets": [ - "Basic Latin", - "Latin-1 Supplement" - ], - "has_sig_or_bom": false, - "chaos": 0.149, - "coherence": 97.152, - "unicode_path": null, - "is_preferred": true -} -``` - -### Python -*Just print out normalized text* -```python -from charset_normalizer import from_path - -results = from_path('./my_subtitle.srt') - -print(str(results.best())) -``` - -*Upgrade your code without effort* -```python -from charset_normalizer import detect -``` - -The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. - -See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) - -## 😇 Why - -When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a -reliable alternative using a completely different method. Also! I never back down on a good challenge! - -I **don't care** about the **originating charset** encoding, because **two different tables** can -produce **two identical rendered string.** -What I want is to get readable text, the best I can. - -In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 - -Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. - -## 🍰 How - - - Discard all charset encoding table that could not fit the binary content. - - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. - - Extract matches with the lowest mess detected. - - Additionally, we measure coherence / probe for a language. - -**Wait a minute**, what is noise/mess and coherence according to **YOU ?** - -*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then -**I established** some ground rules about **what is obvious** when **it seems like** a mess. - I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to - improve or rewrite it. - -*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought -that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. - -## ⚡ Known limitations - - - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) - - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. - -## ⚠️ About Python EOLs - -**If you are running:** - -- Python >=2.7,<3.5: Unsupported -- Python 3.5: charset-normalizer < 2.1 -- Python 3.6: charset-normalizer < 3.1 -- Python 3.7: charset-normalizer < 4.0 - -Upgrade your Python interpreter as soon as possible. - -## 👤 Contributing - -Contributions, issues and feature requests are very much welcome.
-Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. - -## 📝 License - -Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
-This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. - -Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) - -## 💼 For Enterprise - -Professional support for charset-normalizer is available as part of the [Tidelift -Subscription][1]. Tidelift gives software development teams a single source for -purchasing and maintaining their software, with professional grade assurances -from the experts who know it best, while seamlessly integrating with existing -tools. - -[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme - -# Changelog -All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - -## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) - -### Fixed -- Unintentional memory usage regression when using large payload that match several encoding (#376) -- Regression on some detection case showcased in the documentation (#371) - -### Added -- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) - -## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) - -### Changed -- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 -- Improved the general detection reliability based on reports from the community - -## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) - -### Added -- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` -- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) - -### Removed -- (internal) Redundant utils.is_ascii function and unused function is_private_use_only -- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant - -### Changed -- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection -- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 - -### Fixed -- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) - -## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) - -### Changed -- Typehint for function `from_path` no longer enforce `PathLike` as its first argument -- Minor improvement over the global detection reliability - -### Added -- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries -- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) -- Explicit support for Python 3.12 - -### Fixed -- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) - -## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) - -### Added -- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) - -### Removed -- Support for Python 3.6 (PR #260) - -### Changed -- Optional speedup provided by mypy/c 1.0.1 - -## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) - -### Fixed -- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) - -### Changed -- Speedup provided by mypy/c 0.990 on Python >= 3.7 - -## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) - -### Added -- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results -- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES -- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio -- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) - -### Changed -- Build with static metadata using 'build' frontend -- Make the language detection stricter -- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 - -### Fixed -- CLI with opt --normalize fail when using full path for files -- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it -- Sphinx warnings when generating the documentation - -### Removed -- Coherence detector no longer return 'Simple English' instead return 'English' -- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' -- Breaking: Method `first()` and `best()` from CharsetMatch -- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) -- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches -- Breaking: Top-level function `normalize` -- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch -- Support for the backport `unicodedata2` - -## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) - -### Added -- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results -- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES -- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio - -### Changed -- Build with static metadata using 'build' frontend -- Make the language detection stricter - -### Fixed -- CLI with opt --normalize fail when using full path for files -- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it - -### Removed -- Coherence detector no longer return 'Simple English' instead return 'English' -- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' - -## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) - -### Added -- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) - -### Removed -- Breaking: Method `first()` and `best()` from CharsetMatch -- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) - -### Fixed -- Sphinx warnings when generating the documentation - -## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) - -### Changed -- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 - -### Removed -- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches -- Breaking: Top-level function `normalize` -- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch -- Support for the backport `unicodedata2` - -## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) - -### Deprecated -- Function `normalize` scheduled for removal in 3.0 - -### Changed -- Removed useless call to decode in fn is_unprintable (#206) - -### Fixed -- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) - -## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) - -### Added -- Output the Unicode table version when running the CLI with `--version` (PR #194) - -### Changed -- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) -- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) - -### Fixed -- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) -- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) - -### Removed -- Support for Python 3.5 (PR #192) - -### Deprecated -- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) - -## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) - -### Fixed -- ASCII miss-detection on rare cases (PR #170) - -## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) - -### Added -- Explicit support for Python 3.11 (PR #164) - -### Changed -- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) - -## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) - -### Fixed -- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) - -### Changed -- Skipping the language-detection (CD) on ASCII (PR #155) - -## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) - -### Changed -- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) - -### Fixed -- Wrong logging level applied when setting kwarg `explain` to True (PR #146) - -## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) -### Changed -- Improvement over Vietnamese detection (PR #126) -- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) -- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) -- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) -- Code style as refactored by Sourcery-AI (PR #131) -- Minor adjustment on the MD around european words (PR #133) -- Remove and replace SRTs from assets / tests (PR #139) -- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) -- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) - -### Fixed -- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) -- Avoid using too insignificant chunk (PR #137) - -### Added -- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) -- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) - -## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) -### Added -- Add support for Kazakh (Cyrillic) language detection (PR #109) - -### Changed -- Further, improve inferring the language from a given single-byte code page (PR #112) -- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) -- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) -- Various detection improvement (MD+CD) (PR #117) - -### Removed -- Remove redundant logging entry about detected language(s) (PR #115) - -### Fixed -- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) - -## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) -### Fixed -- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) -- Fix CLI crash when using --minimal output in certain cases (PR #103) - -### Changed -- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) - -## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) -### Changed -- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) -- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) -- The Unicode detection is slightly improved (PR #93) -- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) - -### Removed -- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) - -### Fixed -- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) -- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) -- The MANIFEST.in was not exhaustive (PR #78) - -## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) -### Fixed -- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) -- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) -- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) -- Submatch factoring could be wrong in rare edge cases (PR #72) -- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) -- Fix line endings from CRLF to LF for certain project files (PR #67) - -### Changed -- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) -- Allow fallback on specified encoding if any (PR #71) - -## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) -### Changed -- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) -- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) - -## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) -### Fixed -- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) - -### Changed -- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) - -## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) -### Fixed -- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) -- Using explain=False permanently disable the verbose output in the current runtime (PR #47) -- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) -- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) - -### Changed -- Public function normalize default args values were not aligned with from_bytes (PR #53) - -### Added -- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) - -## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) -### Changed -- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. -- Accent has been made on UTF-8 detection, should perform rather instantaneous. -- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. -- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) -- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ -- utf_7 detection has been reinstated. - -### Removed -- This package no longer require anything when used with Python 3.5 (Dropped cached_property) -- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. -- The exception hook on UnicodeDecodeError has been removed. - -### Deprecated -- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 - -### Fixed -- The CLI output used the relative path of the file(s). Should be absolute. - -## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) -### Fixed -- Logger configuration/usage no longer conflict with others (PR #44) - -## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) -### Removed -- Using standard logging instead of using the package loguru. -- Dropping nose test framework in favor of the maintained pytest. -- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. -- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. -- Stop support for UTF-7 that does not contain a SIG. -- Dropping PrettyTable, replaced with pure JSON output in CLI. - -### Fixed -- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. -- Not searching properly for the BOM when trying utf32/16 parent codec. - -### Changed -- Improving the package final size by compressing frequencies.json. -- Huge improvement over the larges payload. - -### Added -- CLI now produces JSON consumable output. -- Return ASCII if given sequences fit. Given reasonable confidence. - -## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) - -### Fixed -- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) - -## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) - -### Fixed -- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) - -## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) - -### Fixed -- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) - -## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) - -### Changed -- Amend the previous release to allow prettytable 2.0 (PR #35) - -## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) - -### Fixed -- Fix error while using the package with a python pre-release interpreter (PR #33) - -### Changed -- Dependencies refactoring, constraints revised. - -### Added -- Add python 3.9 and 3.10 to the supported interpreters - -MIT License - -Copyright (c) 2019 TAHRI Ahmed R. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/RECORD deleted file mode 100644 index 134cd2f..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/RECORD +++ /dev/null @@ -1,35 +0,0 @@ -../../../bin/normalizer,sha256=TGGzGCiTm-yLw6gQOEfPQonWoeOkPWp5W4b1RAbka20,260 -charset_normalizer-3.3.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -charset_normalizer-3.3.2.dist-info/LICENSE,sha256=6zGgxaT7Cbik4yBV0lweX5w1iidS_vPNcgIT0cz-4kE,1070 -charset_normalizer-3.3.2.dist-info/METADATA,sha256=cfLhl5A6SI-F0oclm8w8ux9wshL1nipdeCdVnYb4AaA,33550 -charset_normalizer-3.3.2.dist-info/RECORD,, -charset_normalizer-3.3.2.dist-info/WHEEL,sha256=4ZiCdXIWMxJyEClivrQv1QAHZpQh8kVYU92_ZAVwaok,152 -charset_normalizer-3.3.2.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65 -charset_normalizer-3.3.2.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 -charset_normalizer/__init__.py,sha256=UzI3xC8PhmcLRMzSgPb6minTmRq0kWznnCBJ8ZCc2XI,1577 -charset_normalizer/__main__.py,sha256=JxY8bleaENOFlLRb9HfoeZCzAMnn2A1oGR5Xm2eyqg0,73 -charset_normalizer/__pycache__/__init__.cpython-312.pyc,, -charset_normalizer/__pycache__/__main__.cpython-312.pyc,, -charset_normalizer/__pycache__/api.cpython-312.pyc,, -charset_normalizer/__pycache__/cd.cpython-312.pyc,, -charset_normalizer/__pycache__/constant.cpython-312.pyc,, -charset_normalizer/__pycache__/legacy.cpython-312.pyc,, -charset_normalizer/__pycache__/md.cpython-312.pyc,, -charset_normalizer/__pycache__/models.cpython-312.pyc,, -charset_normalizer/__pycache__/utils.cpython-312.pyc,, -charset_normalizer/__pycache__/version.cpython-312.pyc,, -charset_normalizer/api.py,sha256=WOlWjy6wT8SeMYFpaGbXZFN1TMXa-s8vZYfkL4G29iQ,21097 -charset_normalizer/cd.py,sha256=xwZliZcTQFA3jU0c00PRiu9MNxXTFxQkFLWmMW24ZzI,12560 -charset_normalizer/cli/__init__.py,sha256=D5ERp8P62llm2FuoMzydZ7d9rs8cvvLXqE-1_6oViPc,100 -charset_normalizer/cli/__main__.py,sha256=2F-xURZJzo063Ye-2RLJ2wcmURpbKeAzKwpiws65dAs,9744 -charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc,, -charset_normalizer/cli/__pycache__/__main__.cpython-312.pyc,, -charset_normalizer/constant.py,sha256=p0IsOVcEbPWYPOdWhnhRbjK1YVBy6fs05C5vKC-zoxU,40481 -charset_normalizer/legacy.py,sha256=T-QuVMsMeDiQEk8WSszMrzVJg_14AMeSkmHdRYhdl1k,2071 -charset_normalizer/md.cpython-312-x86_64-linux-gnu.so,sha256=W654QTU3QZI6eWJ0fanScAr0_O6sL0I61fyRSdC-39Y,16064 -charset_normalizer/md.py,sha256=NkSuVLK13_a8c7BxZ4cGIQ5vOtGIWOdh22WZEvjp-7U,19624 -charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so,sha256=IlObIV4dmRhFV8V7H-zK4rTxPzTSi9JmrWZD26JQfxI,272640 -charset_normalizer/models.py,sha256=I5i0s4aKCCgLPY2tUY3pwkgFA-BUbbNxQ7hVkVTt62s,11624 -charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -charset_normalizer/utils.py,sha256=teiosMqzKjXyAHXnGdjSBOgnBZwx-SkBbCLrx0UXy8M,11894 -charset_normalizer/version.py,sha256=iHKUfHD3kDRSyrh_BN2ojh43TA5-UZQjvbVIEFfpHDs,79 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL deleted file mode 100644 index d1b3f1d..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.41.2) -Root-Is-Purelib: false -Tag: cp312-cp312-manylinux_2_17_x86_64 -Tag: cp312-cp312-manylinux2014_x86_64 - diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt deleted file mode 100644 index 65619e7..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -normalizer = charset_normalizer.cli:cli_detect diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt deleted file mode 100644 index 66958f0..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer-3.3.2.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -charset_normalizer diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__init__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/__init__.py deleted file mode 100644 index 55991fc..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Charset-Normalizer -~~~~~~~~~~~~~~ -The Real First Universal Charset Detector. -A library that helps you read text from an unknown charset encoding. -Motivated by chardet, This package is trying to resolve the issue by taking a new approach. -All IANA character set names for which the Python core library provides codecs are supported. - -Basic usage: - >>> from charset_normalizer import from_bytes - >>> results = from_bytes('Bсеки човек има право на образование. Oбразованието!'.encode('utf_8')) - >>> best_guess = results.best() - >>> str(best_guess) - 'Bсеки човек има право на образование. Oбразованието!' - -Others methods and usages are available - see the full documentation -at . -:copyright: (c) 2021 by Ahmed TAHRI -:license: MIT, see LICENSE for more details. -""" -import logging - -from .api import from_bytes, from_fp, from_path, is_binary -from .legacy import detect -from .models import CharsetMatch, CharsetMatches -from .utils import set_logging_handler -from .version import VERSION, __version__ - -__all__ = ( - "from_fp", - "from_path", - "from_bytes", - "is_binary", - "detect", - "CharsetMatch", - "CharsetMatches", - "__version__", - "VERSION", - "set_logging_handler", -) - -# Attach a NullHandler to the top level logger by default -# https://docs.python.org/3.3/howto/logging.html#configuring-logging-for-a-library - -logging.getLogger("charset_normalizer").addHandler(logging.NullHandler()) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__main__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/__main__.py deleted file mode 100644 index beae2ef..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/__main__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .cli import cli_detect - -if __name__ == "__main__": - cli_detect() diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index a97259a42b55dae028622ded8027e4eceb7bc2c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1724 zcmcIk&1)M+6rYu3$y(WQ?9fX~VG1RZAl^;vLyJfaPHJO8Y!}DvVJR%5-H~?V*$-xB z<*J9`=F&f*e?#%bErgoVx|iO1Sz(G{A>`0Q$;~CVobqNRSt-f2dwBcan|bg3-q-v& zKd&SBntR9HW*MQsl`#2AW9G#lz#x9FBwAt+kKpk$ST8EYmeTV)M_ zeuhs`jpT6%=yhjPc zn?4s3e;)dM$_2<{Q~W8F)R7T4^mWYq4kvtorAH+8D2oLiL<#1UxLDG!B<}Jkz$C;; zco0U1A$CUbF%6x_^~0W_Z%5Ma6G>g%8RW+vZyZEckBI7>UcsxtNi zWtJb?oq?ocVgUIhObYzTYiEu6z2Ud%lk}JL6c4{k&(fcifYa0T*YpIZzYmYn6VRRE z^fx7+r9bAHpYxk1pgc{V7>fz(`?1Q?I*Pk}(iR{`R*PG1=nhq7x?j=+xKx1fz zQZ}xx2+14MYq|RWhO6tl&~7epK%q-6bRu*|bssf_^oh?%hf%x?4Ia%#H-YB6k&^^8 zltlVbsKboj^`wkN%QSnw^pcL@M1i@R2u`K>X4BRTz2!vlfcrg9ws6B~;+5MgchtbG zdjU-B{`%HlTW>Mnp`oBHyxrcvl>>OtzQ3{aa3i0wK#dd(H9!=G{#-5R&U3Za%~GbT z{TR(dR>}EpoKNMe8FbQ(js&&rJUh%?B;WaR+%i-~EtY$lOVtx>GX z80sjz0-^#D#vo9&N96;^lk!X_uDdl#HLLAF^T)yu=mnUdmtUG*6i}1LRI0UhJX3A8 zx$O(bH2O5`oA8*KasCXf8h2Jq;Y+$aeuBhXro*&tKlG(-8}Z=f>|MwqVRVhZ55ZJ- z#U7B$qNZv8mTqXJ7Y(E>{DWHOsPz=R`wZQFif%qb&C61)Rye9$Vx%n{RW8ut5G|%l hH_uW10<8?uO1ipv^3lcW=5TfMe0A$D)aLJj+gBQ1Ov3;G diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/__main__.cpython-312.pyc deleted file mode 100644 index 0b24403b26113e3b02d0c1ec9b31fdbdfe1b6378..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmXv}Jxjzu5S_`U2q$On`3H>PEov+Ff?8OLu&leISuUFmn>CQ@1^jX2$nO?US26z%#SY1qtdApk2Mroauy~IMl;~u31mLp- z2p3x;LdQCmr+*k5Irbw}8lfqW3VkMom8KHnHfKGHf@je*v@2;zh!0o~Q#KF0aF)2Q zDR|c!RdD4r@f*`9K5eOLd1)Sb-G#zc=hM?O-k3lqwNx|tObu6s^bG}3Id5fY=H&Tq u@wDzX*@bobrKC$gygi)ROk!9uMErrnCEI(Of3bt_bpQ2w#UP#dEXw~ub5802 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/api.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/api.cpython-312.pyc deleted file mode 100644 index 4ccbcdb45eb5cae5901403c94fdd57a9e6df51d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17327 zcmd6OTW}j!mRRFOyc$Fk1o(b59}o#XKv7RivaA;+nPZ8vMae32Y-5|~CJ7M_y1QYC zbYoZcOzlcF+L(@$8qUU*;+9plO35Eet*hev=_8L#rD_Wh>0ztGsiiC0%qU-!#9e3Y zkK~-&Xf&jOZE50Er6qRXzJ2ex=Y7vT_u{|$d>#s}$k|&{iL(^--|@w`>WhWPc`HTz zlHw`elB6c^X-!&E)(NWx%eJH~WuLI8921UI)kIawIpIvXCS35%&O4IslxM<|@=kbD z^aP#qP54NCRkAwepYW%c2_{uDQIiTx1W3IzS(^$@1WDPI45jKO>QeO+_2k{1Y)Cau zG^Uy+no`XZ&8e1&mQ?FRYpQLcE!95JZlSD{KwUg+Aa6c-&n=7oHqk-ajF{hy(yd}) zqD$zyIBL|IPtxxv=DShiy#{RKX`$S|_m6MmX~89I6MCksjnI|%3B7zZ{P~3l&j?k5 zL+HbL@~!5n_khsP*TP>A+J@j+C)7{X@%2AUsX?&+2=E&L{$NG;O+ZN>&|;mk z{YbPl!&uP@W3}@wFqRcYvkTj%>_5V2{lK|a7;R|Y5@8>p5XT}G&GjlL&zwrk3K56q zI+{px;{50t&3-B&$(rlToSev{xuj+r7i7(GJ`HaVP@f`p%^%Oq2%?aV3o(&{W?BHs z89tGojwQMDbe5YIBrTMhj8&+vO9|q%5PJ%O1W>C-kBl9Ojh}z@)iY<$9Usw{b7#)P zj*gy;jURvQ{PD44$F%^~jGsPo>XcdMICu8QvEx9ECnZRdj!(p@(Z^;uQ4-|SoE)Fg zs>|O5DdN=Xxb%EXniJxQsf56n5Y=kqSve*N?_>#e@tJITR?^%FF3rW#TuRVt6TFa? z6I0MRF&)c@vB^wI^ChHMd=6?-i6kd#b?}uXT%hjOe_fELL}VFNpU%m5+V|-u;JdM@UDIZ5*n z#9@d9L37J9q5vZ&d5s1n6H+FLGE0*;;c`5gmGCV%CkjFeg#NA&n-pfacN3Ycs5ynp zb4f0d)&hoLVoiEBkIgNueg8~i=~*Fkiqa?vGG zltRhiUiC}18>K1htzxazB|D8;-g41xl+CB?HOdNQxS^+?A6OKpcvMC#$aS&WD4EZk zr|f%;S#Ol&fKf7_Td1-gA>*t0lha+%oXyo#5%&h}E2_)ZzbW}~I~sGUsQF?xi|HO0IA~8T$n*rk@Yn05ViG$o_)XHI_q>!nPubQTK=dCKecG_a} z8K)Hc`y*v|_Za=mr^z?o&3i6J%r8bsu^aGip;p0V0k|X7hjiRJO~tKmfqZkc+^>HF zEu;A=L!&;jUT5r7&Yq*w&^BjR9M6G|_dD=$RKUmaT=+PO_&5}=VpS{)4vLy>0*&Z+ zW?s>X1$ugKK~KCOZ1CkQiZ=()c;75c`(hoywk&|XDArl9FR@x4)Z0Q$@ryx6&MH^X zR5@z2$No8A#WveZZQg`7)rwE-duAG{D_JB#NG1AQkzy%Qw`qPWm-L7l717F zu7kPQwE2=;E4NmfK5Hzq#`fPE$*NFkUuB5AtBES_pKIo;a;#E2LcIlZY_3B-UBbFa z=`co-EY}&nImd$STkMp=ifzx#{}!d`PdZHPT(n&8l+TpLXpyYEHSMWHyW%yef7(MD z63ulgZJ;r*Y*U(fSJndMYS12&lg);vDPH0=yOb`m`I&JCjWb39s3m`02EWsQ&$pp( zH{NpH@@pj;yZLs5kGXE%nhOKRw}DsbfxlkRrSVcrkc`p^^GJ)*sx&HXO7pE^%mvb! z2I)kUo{GLNVvGxX=&jU3K3^ImqSzEC-<+j@SB*GNl~Gf&{J8b96=y(b!3Wrem9Wx{b&?A#)^dGPA$97pKOle)oTK!y?v zwo}Go-sCIj>%|KO_U04pxa+fSgRe-pSTDa}d{=r6{K}{+)uE(dK5^vwls+A639#02 z6=#*&N*px(&~wZ$+d!US`KKkU`uHAvn^hMprD8>~qE z5a?l%M&k*(UtbBQr!=PzyvmTNIR;kH8#D;Dj_BN*N*^Dcg%QLHWpb*t8Y;sCU^vBS zS^FPpJ!Il&##~Ch(uCf|T-Bggf|X5^(tvs|Sr_fs$IEc3g!^`ri~m314*FuwSoH?g z#Z~ElQbLkl2})L_PHZZp>-!_y;WF*06L*yLtHh5TO5H`?&{*?X2BV^eDRl;};Lk-R zBwCf=`|a>-R)S)q61q5L!ZJ!quu|@dx1T1}F)GTJveF6)N$$qV#6RH@g6HGduSEyy^Hs%(@}IY|d>fjc>pH`45bLj0}~Iazr|U?&AhX6G`Jlz>F2#1`|oY*xa=C@W{6 zZJHNE7E-=!X7ZvCm!&9yKRSi|MS)F7Y#N(MkT8}8*o2HV9Cpa0r`bfBPsBMnBSLFV zCPPY)myMHtlO$z7l+M5h;j*j)K043x!W0M421xTcF%ySOF?%VIOtR2Q{zE~>d$DWgwNL#-j0&t*kWQv;22GoWNs*>sT+s2XHbf?z#9 zdJ^e(Y%j5V z2CM|hfUOx+lk_}01C%9!ql7SM3_L{Mjb)R`m-SB|pef`Lm{$gM1o_VA|}KZrEEeb z3W%cSg~{ynG;DO~R30M=O29%Lay$i{P*et0aUf?>AozkFWMQXdUhlfF>-c5ZegeHI zItDZed43Lj3UN0u7#28D5ROeeQ$eC#PECJ#pA{8wlZx7IpFrqyxx5PEjgP) zq_Xl<>;({LQM;m=gVanLGua7^8e90ICsYZP0~j17IwliUNpgfblYZ7vC^iYZUPwg} zwhfU6W6zHTS~Hj6!+tg_MHj3C>;ea)nELtSj6&e?rD5I%+Y?3DqyvS9oiMgHoQUYA zfm?ZGN32)}-a12=Vk+en7&LSsEbNscx~q$zD}!w}&><-*VoGG;rE}suvKACkcPA#% zQ(`6ssw5{sv*$A40zodol-~Dp5d&;=Vf4I&-ILtHJee7ILEk+EO^I`8*u;70wujTf zJD`G~yc}$t4m<_w@Cfl|KtBM17O=Gk&L_o*vuMMH-A370b^kWc_74|5II1%WX0!wW zEC5U&s*j#It%CsMrxGH_)nv-|fYKbWW0^?2ggUBKp_UEr(VshaEWBCL*)U8-nRJhg zb^#MB>JBFj0+|N&AO#V2U=uI{aEMEky?`D{2LD0)qLiH+Bw7ZheCqhn(c{sVaF;+= z?7a(TpM4~AGJxT;X;DZfh^b#n$gou_Za+oD2hMpc(4QzZg9T$ZZIB+gG zg3^RdZZxnfKW{sNJM9Erkom&CVgr8+ek+=i#32?k@iBuvUdL zU0epKRDq|+h|7Mu}EEH#%!8A9NcE_MJ}g{RnXIQjrr&<|{I1HzmTHBKjF zY2hG@JcltIh^+EJrx8{u64S9TfG%u%FURU<79idM=Yvz*Joq8~hzEK|py0jSH-@iY{cH zt3CJ<_EH%x#DkzyL*IsWT?}m|1(?RusFXkhj#cuN3une47Qrtt>$2zuiY-d$qMR_O zjzaV#Rbu=hJCAkhhUL2&u#1HtT!KHz@g+{(iKN*(nH z{SWI)9JC&)f8V%oRT}q1CJUyI-UXB@3G)=LdDz}baN&ax=I{xglZ6NgJBu?bI&OIe zCl3;WrOD(3Q_jIk;Lnr8B*vU7LhOQ$6@K5uBr-v}Z=;}nMpe~y8!jZFMqhlvUIO#%(wR*SAv}V7UNu;GsF4)YWfRjyQw8jvmS;3;d`=6&aVnVXViFvUN|w%Mm8wZtR90=SR4n^~7AU=x z#?+j}1-?e(u`i(*zH455o6`e5&1Y1{@VJ@g*QLRiz6ar`8J;-9KHR&2B8Dm&gVWg) zr^%3CWyjBSFrsD=w8mn*5GyAphSY1p(%_|U0f*(pPJyadFrqhw4zlE#$20Zf>_cnJ@vV0Pzuu5T_3&A@7WNSp)ngm^DL zGlT{cbX#R-`-Zr=M09Rm z^Wb8EoDr17p8;lQDbk;Ton4`}UZm){#l(%sXXo=BJ6FBC)~oAP`sf-xTA)YQnc(6Z zANwEMtiI~UHoM=mN!i_=hcxB#uZR2p>dn7=Gw*6$cU7;s8Vjz*Pli@qeem|7|C;|3 z$EvICejv1%_&9py#D>$iSo6Vq_>I0s=R@PGu5#fTW*Kjvq@7gj97kMKXN(oS@^PHAYVUn zl`hneY}7Yir7MBi^M`=(E%q&C3xV#t&ajD;N+(siMy2aiIKWV-}zvE@%1I)qqmnY-VJSE4>c^+Er);Awi4>S>bc(;UQT=(UEO)S(0M#>Z(J|F z?;0s|j==j@&X)3#POZ`71$ta%>WI+(!2#rhygX$*Qt1n8^xFmcZKKf^HUgY_`vBg< zDk^%^0>WFGUv)+9$eT9GQNQV;TupgrSar3oxw;Fk?tFOfs_TXFfyY$3ZOaMTu|-o2 z%^zp4oKPeEd3&>d-xwUeGN$%Lue_oT@6Oxn?zauBv_-F+ywC2uJ-WiaRIs;RIkniY z%b_&N@|&x!9p!^uSf~Aq-W9qJ*Hl_J6^o%yXUQ6I(@sP&~i|vgJ0px%{L1z``~56+pu(^;O)esu2qW% zS6v+&k-@8eH9UO#twQ){-qoUZ?0!U9J3XuWUsLHuVD$k{-qiqXq8sxq+Y8?9g?*<{ zpMP@gC+kc|Wzs6s3f%EkZ+a+SNQFU03*K-cdbGR||3srryUQI~?>l^_b*1l2p6=KT zG`K_e{N4G;OLxMB$jNo4cB8)M=I%oMc92o1ZE5y~ZF%HVcOlrf(b#!o=kj|iT{}OI zf6@KdiG1T>wYBdb-Hw{TRyEbN&FJ?K`^YnKw(xbg+8iYq1zuj?bV*ER?tFUCvesmRhjl>InVSz0^bw8&(toS zSz)@D-z_kMn@-Bz2q5Veb3Cy7n!yd=F%Abbc7|_^-wb?u;pRJk`O}q-;k>KyeoK3v z9lkxh(y|-gRJH@;rLtl0UmNWmSKVr`?y7s!-s6s}Hx8&wBf&nPGEG2(%CIWatTJK1 z>2a;6srnJs>}y(dY}QdVO=}EWVA$o*D$}#UH0p2b!8Wy_Rc+`{8`_t5E&1;@oV
{;Zv?yZ zz5DJ4_i4f2{NTYmp}T`8z6v&O?uF4GzHXsvw%udG_kxj4yT=CzH-6H!#Q&?vQh%P= zw#Ezsr*5Y1yqsqSSDDif>!{NRl};m zj^*i_m+~+CBp-gI5Paoc!{Gf6ps95!cQf%t=#KTy88DX4z+&}g6&38rckj7dyGM=e zEd;lzq0Z&$yP-ifyz`^6jn3_huiT@9_e-8#rC$L{0~fzIw0x`(=(%09O7A2-;9)hz zgxC9C{Nk;ZzE=u#%l-CUx8GW6KbEIkzVbGk_^ZqdYs{ena|n>Uu`}PZcctqEKn85W z0ZId!7peypKoed%dDq*eHi9BLeLEH%>z%`Duu%~GQ*pG=(iQ1T6WxytTev}n)VbTJJy(e1!f<}dO7t33U21Z>TA{Obc3OS z?S}rfKSm~R+1%i&8z^5>zHR&Mo-g+1TaOj!V`wa0cbV{ga5<;0ol=>@cLuj`(gttN z)UPvjYfN{6>An$LWp=~cFa00+^X%?bX3u(U$MW8r2Mb;M@}2uvY7g9{51<}pTuASLP!u5 zM>u&7d2|Mo|e8=BX)E^#-SS8E4RzMhXf>!`73K-JfS^-GqIPk^a_s{u#c zU1r?Bp@ZLU)5Rm?dp<2G% z{vY?&@)7&rc6s@z^>2rcGzsn2759-{(^l*1OLn+_X|v(o*FIyl-jDVPEsno$w&1

Cehrvu8Gb!x8i zuSiY+N>BcZ686Zsf zly;v=dt+vZ&f?~up2mbcO#)IMbFi$}cb4>>qGE0plGm7FC9p~28uz>6NwRYVd8YEN zdZUPyg#VfyJ4QU>&jG*ZT187pl)0u6KY&-wNl>4f6aNg}{t*)75*}QKr3DL13)SWM z(0R@I3%Ba>sjh(Ps!?4*^1nrO`Bhhw>Z*Sn^3qkCl-E)97&=m#eGYyu_JmY08-Uz@ zoQB(zQsFJe$%ow({Jy3nBgOOGGKs@!T>NMB#FJHB)G#GkF2Q+^H{f>-7HZzApT&sS z7XIaPOAph3Bu7p5!O0F$lsyB<7nD8bwNU_A)>HAiq=0DLO3^plAw$W-CMxn8H+w}c z0lPJ$XGm&AuZA`AyiGZ>o)TP6z^Y@}C9I2`dAYZU!MUp091t_-pL( z2yfp=E`ayv!G7B<$1bN9nwpD2+5tz@pJY$?2}LOV$m@SX$0rrx`+wvmHkd#1QmEfo zjn6EG!R9=QhX#cxJ7z=4h$p$^qFGav`2#Y5jS5PxXM~MOx#wsfv2qbGETRtjE*kDs zV=`G+phOxH_G~Ze(SWfp3z=x-B5v5s<> zC59b2lB`Cbm4YAUu`@zegq_z!9CEDFc!XglrSJa~Pl2$rmmqZyRmSNNa9sM3h=W zNfILN=Q4tbOWY6A5aIXPl!zXLD7^S0mJZ4%9N9S}Fa`^DSvY9~^ zO}BaRBLKY<&Fnuz0nDtEas~2%p3k~IyL_{K)wypy(5%uOYM@@N?@;U7)y6RX@7S#d zo7MJVwWWV6X?JUiFXpDv@g^mbRVVp%-0e+31*XvT95yD{_53;JxJlNW|C89 zCqKl)@I~{Dmml;Hk6|4R$@dfF_y)}An!8ji)3#IC&H*QeIGLy3*9zoYhkN$vgG?yy)NQ*is5 zrYvVIE7WVhp_=}V+WxiG1$7kM9^wtw8J^~=^=s~?g1af-JhbB8ex*tc^epzQ1$tl! zwi1Y3@vD@7jcO@SEvh|q<>Z<@RIrEgbw^h0M_~a1Fsi9Qtye3cz$U#9|{-z+cs@jwc)MabYKBii8aAZCn>rp$Thf0(L>cXFYQ{k z{c2w!5Z?5XI{Jw^AE~RRn%g)1q{vX!fz29H3{Z`2o3*4Er0SYB2^O%JuL?e{C$;Gk z(#@5SZnjjZE@smPPl6~si+Ey5M-xj=VTn~wA&FHwj#$!B#FCC7mUINMq~nLB!xn?5 z713+4cvO#n(FRO~)qi)v6W+8z9YGbIMO3k*ql%@cP{pdJP{k@8RV?YKVo668OFF7p z(ox0I(eFXE<6-EKrCx1jmm6;GyzTlTobNbPXg<7YgIaOk;RgZES7X+u>{C^w(6loEb0FM@95^) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/cd.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/cd.cpython-312.pyc deleted file mode 100644 index 19dd40585b67ee893ac9f0dc3bfd5e8b4ad48ee0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13487 zcmd6NTW}m#dS3TjdoT^m;7Z)lxMM)#M(`%yZ{Sq~r6qJBY#BS8ZU95h1?nD<1a^>x zwWd@Iqmst9)YrI)3+mRbUZi|DFJRh0k`$oOsyDsb9 zxbU4N%Zp03l9E#JTDuG{bxwN3I*g%9tlu%R2IO{&jVN0~zJ|7HFbhq{?Llsh`P_`>5b3MfQs^ss z(m~r3q)xF_!c5vNaAMoLZhJm@=h#r^mt=sWAy%)rn25+yTuP+Ev9nSnDM~VOy=RiM zi4;xxV)E=nBpjKRLT=fGTDt3OG?LO?FGrP>&Rv~JMU#neOm~mX&cviI$;^e^y8qnw zMqYhwWc19%k!w14`Rc^zsVhj;zIgAUwt{F8)bZ-2DQ4c*5qRNCatIR|r(d4WWo5MpSA}LDrwlpD!6H^k(1kWU= zC0R;Dq$}YRI;@~0WvCZM$wX>4oRUPH!*eX0iYD|Lq~f!&RCMCTTuPciwWJtLOzHK= zpG`!`R2VH!D43Uo7?$Vs(rqtvUo@NuPb9)|N%zQ7YF17}%$-A~m+|7?iT|zNAol|% zWxq=^)((^XDN=%7B5UQo!=~I;?oE%CD*TF$o4&%6l`6DqwR6*wd#eb37gz_AW<}@6 zF00gnBg!l|MfZX$RcaL^lKl~|f?Q{rx86WKciM3?VB{3vQ{~oE+C62Dz;4@<_9*PV z)>Nfk%45ItroD^oy@6D<{myH>6S-Li^^T>zY3FTLc8l(`GwnT!g5>wE<1Up((KnAC zN1PD{ed98o7hLzQyO`82yH)2|<|p2WV~UA5-ogkL&}Q08q$BN`_e07`Zv8I9$d0u4 zeaF3W7fLPI{kVoFmgd}HLjL)4B?7MR(WMr`ijs^($*vO2Z6VN|s0h)75Ec|{rkEs< z%_We{B+P`Tq<-4KeE+%GM1%;AkVvKkWp-vJDU)`PLAPYz_FD72wL6vmA1vse=u{#p zlO6AoWH~7-GFhoFPmpnG1ufIMK;^itl>)wl3M@VsO+c^1NEP6vPHIftY zq&OSHE}l%v@o-8eds27P(dZtL!h-H5eNyBaypqX^=^ll4I>gCjs&%hXPS)8O-LFVe z!jK+C4@9QJaySAwnTQZr(b=f(j!e%cZWX^WrjA2PWQxi4_3H!E$+$Ei&qygbc{>`J z9w75La3!il2KsMHiQ5Ah!obX2YC4%1?jPDaphQzr-%L1i3p1?@kk2YoY65c=567bO zk~|O*`)B6#8f!MU%emZ-kCSboK(#T8%yW+6nwO(%-kv9A)oRVTY}t90JO8w7*Lqo} zR@Ry0YSy_XjcZztX1Sg%rz7C`Z=PDsQ=4})ZvGj^G<9SStu^jjJe6zfSiGETXk5Jd zw6x;>zU8WHY4avi>h4@(bJaB;y!rl{Ka*BoT{-j9o7w8nlKb!d!6$u(AMMZfom#rO z5olHeUAbWG(?BRQkPRGuBs~tC$yK&xc59XUbHn@YUtS4lRfD-;!}8T^aL+2M1&8pP z3%0BbX~B;71NjDg>3K85SL8iR$G|_hUA~fyU`sAgzkEUq?9L>$z_CxO9tTD?-ArlY zrW3EXoF3kjXFP7tSDOwzD+Gi7nyot1$myjsax@MsG*)*T-Vw|qJ7?<;ar=Oo0@f5T z@1}_+cx7LfP6ER8G0g9x#DZhNnTEW+)sD2QfP!fot~@4mkhjs zWMH4g4N=fN;h7mJAwoG0kXJstF`cuLdWkIoita0<$$ZhBE@SkJzH6IuftE6RB_G7w zllWJD2e(Bg&oPb7ix-~?yVr#wO&H1w!<&rDU88Z0OWgh5TwVJnp8^FO02bB$uAfqs^d z+RI}DE^xD7xCUq_;HnUoC1F|$-;T!Sgjh6j3+tFl0&?Y4G%_0t%XTY0O2}BuvFWHH zOjR@9~3@qEWgo?2x z0?LdOiB3i((L_XIMUizkmc7CnXwixvM~SQWSFYd&B;uLYgR65{;nd=2uDyHl%0@#7 zYwWIA3grA1%L9-7pz7?+yFvljIUyyYXs;lzq!VB z<@{Ca{ua&Ovcj+V`wCCqHGdDDmc5VtEgQl5rL@X5l7d=|vY3RSDd6lc%@Ru%Lq+1xqk2fB^s}Qxmk94hg(~Uls`2Z5|KtCSW2lf6x42Wqit2f(blNEO-dMV1<=?F0bt zbrB_#c1$|3v7GZ$bYq>BVD!YXC_+3;Y(fGADS(cgj0@o+v(JKhAv!5UQ-C0NW6^{n z7>)~m6C!=32s5+5ISN7sh@Be?IjK&PD=;#dSlPfLG9h{;mU{~3&vY0rlG&18Zf25p z3_nOdfWiuyu>vGjl6q#zNhu7+28JKOqr0T+8@OQulrXJbs%uxSxlMIdKdpvd(W-li zBI;W5eGzDT@Gn-QYuzVvZCxtgqWc>k)Mi52)_srMk6VxBgieib%KK23=$;l;7*Y%9 zB=5%u^q^tn!g@;>9gW!HkJrRbq$|9Lb*H9=qhM-??7IMzvy5e4SYWW=EUfZPGU6SV zRUZ3d+B8R83+|L{WE6}LYN~kPNWe8odqu}ABeVA!(jJ_RW5C&Xu@6ry@^)4g2KlXe?W)T-uxC8y+&HiA_3w)Xvshfnp_-J2v+a2=Vu^b~F zjuAIMLihVO9V2UJV-FX+B`D@8`0WXyU=cfM6 zpOWiuPHYTD zS?U*PPa(eFBD2b_wtnJTWGGmUJ#^^KThg5Fm8IJfoFeKlhP=8noR~A<1B@X&F0FPec3GLx;7{;cV#WCR5=a)VR(iPOa#CGIaLSH?u>d8rKh1r8f2d zXMY3mY4tTtIGp3FHvG*iotnQ}+j9a>Pg~m5_5<0LgG+&@ojtkQ-G9fQxIdJ6d-c-4 z{o#*aeaxSLTi8{ia!s(=Yx^=k)OH@>3`L6qOA0{4&Yr!*%7dMWd|2zK$wfit|bcN0GZIAKfy71LzHB)+${c5v{;V-fZ zf&X8(_)Z@v`E|&J`)|7W(??u-MN$T%2g}o*b7dQj5^SS5`U>u3Qx24PFCw|pHX#I*b{n+OLP-(z#idK;F+cX`{Llq1 zXoZR*;>(}*%|ke?GNPM|0h;0p%6d??{@cr9#9o5@w*Vrt2k6Za;tN*Ct)0Ec*nRcD z?w$E*pGfjcLH#+>98zVlRydj$D4KJ7-O^=NKa2DqZ{^KH>sslB;0x;qtoKFrORc$BrhF(qbp`GUXaFG+OX96rNfCznLv$kNgFjfH zjfZcCqp>j5gu#Adk1*%Hbm2bsC9sU4Ds|LU#b2BVl^E1b$?M_RtVDTGxfR89Cn!Ss z`!qElosru}${mIGe^+-#K~_33L7@t&;AB{Zj9zO)=>!%9#3Vsi%4^mZ1wCxth3G*_ zul*(rn{@=!%(hfr5m1{Y%D`Ss!Gbp}6l%xlWra>)Q{2+)w?9uriP3B*PrVK{4rUkK zL~m~z^9&|}DBHRn)d~fQhN(>4kiZK|6Nci~ciEK?TA3uYuKA+8=!H>54;E$7vU-Kt zSh4CaH<~HRqWa9%_IAQh`%OiS&Ln3Dy%SB%VY*;A$dddHR_SB>D<^@LumFM#7yRQN zXx_FbWwmPE?)AF8THW5&L)p5c*|K9QcWk4hQ*|{yX=!_KBy(45Ka_2G3C~YEy49Y; z*^VRR-JXG8`X61;21l}c&Z(}BjfNwSZfFfB7e|TK=jwkpveK${9?Lc!C;HyA#T;^1 zYh3G+@BVRcuHe=h_i6rp#9rv#zdEh;o>=c4(RxQdjb?jaQMtAaevdkEUgOW_os6&T zxrgcP%lw{t{0*(|jcn*mm1{*Kr47rH#fj}<>QXj%`Lpoj;MEPj$|xXe zeA|u!xw8J%a;(Ha-M=fgCMU|_4+Sd7m52(Rbm?saesi9NZ z&eIxy8ne-ks`X&f%>qJ}Z+&n^?KzR{IH~a`QJ@V4>QDgH1KnAFPv+EPf1kSd>}Txb zy%%$QM{$4sW#GcI{_f1sWB(p?==7)I$3y40mmyk_5azdM z2{Lc9>5+l9PzOZkPt4) zuU4TwcM%bRn3!eWm{deX8c{rxb9F6(iA7D+g(+7BH_=w^#<$7c>IaMg7m1*0fZr#V1Xnm0tyJkB@8_{a=^_G zYzSSOjK^1_@{Lb}gX++!Pfuin*K(D$1e)p~wp|C*U>o8R`BIbuX!Fggzcbf5sPfGl z{-%}JHUI8hXz#yodDxN-9bP)8R)Rz7BF%F^h1M+Bwi5kC^221d>!8LRT;tmE`_akg zhZsJ1-}6D~`=x&r$od7j|;UK0AYMW~Xbh-?2M!7p$@AS-&T;1rk_m&v^B#up&81wbegc(YwgfQl^Z9dD@B z%39zs=>UU+K-hw(*uNrd6*wSr5P*ysm(A|D!0EW>|A21j?O~F*1Y5-1_+TSJCm6>u zOhO0NE7{w&9T>3=WEl2Fu}w;uW31I56>K3L}mHGXil`g8u^Ge7F)ooFgAFm-$Wq1;#M zfpnHaPH}s}ms5+QJHu&Jh&%87MOtd* z#~!M5L1#n#vUV`pans!0XxF|(c)^x*5NUhQLk^itG4FW{HAsG`1iy&$u=#&T@FA!@ zTdgj6CULOlUG6>4H@RoI7vXR>dGl5)MHBZ6`II7W5`CbDTr6qd#^$!e^K_H28I-7N z_RldeDy(lnjHbg-S)*6SEVb2UYPr8({NpyFxpp3sf;SimBk)Zk55z}=l8r%AC7`u# zOSDyJTdR?_eqlQxBolBPZ%NXOA;dS}mRepZf)+Sg2DL-QTZ;FIVP%^1Aeu6OV3w>5 zx}|TIY%=GR_&tflzh-Uf7tCw%fr9rCbS7(L&5y_sKE5CvNQel%{!^8Z*WRy$-3>?vf-~@eoOQB=6ZUU zt}ORzeCGyNeg7srpmnZC<9agHpK}BG0GfGT!EpX8CMLdCa&*mmG}ku(?_r}B76t1Y zWtT4G_{Md<9gfb*n;$Ey$3CsqhA(9YE~_0cf6iaYJ4qp!3PAz>$cDdSag-|aC%H=RbT){ishe}sN~r^2n(5XitK_5CN_?@{eC$NUd+PIOSC0E z!J-D3y|f3RwtO0_!Py z5b5%L6Kxg{D@yU!I4FO{-aAZ6{=|sr3>WAbC)G&pi(4E zOs{llfl)=@PowW;MeJu;y3|I~Ksu0SZ$Uf5t*K52L@zyDM>t!9vxL)fv(jp}@S83x zItCaJeO7zLsE0P_Q9VSxP6K;?4(sUw@RyvtpjV2(0lnZDnd>O}X`GASVdFR#_ys?# zoYMIWg*ZabfvGw*dfh%K0@MPl6#*XJ05;+@NQ$1}AOL1WuPo<;Kq7G0bX5$O0ab7` zG&V=h6Tv7ltaJnQP8m3{2Lr%z%1A!=Qc`?d>VXT|(0p+>AX7|WPFvhWM z>XB?|3YoA^3$Ze6cDlgie2&8eM!yvwldqvDHIqzClq1N{*`$et#%TZp3EOrUOGUPl z1%7W6^ninE#>U!?J*>)(m}$}(7G0_Crj8G>(1_lm4v=x?*-uDV!Z5{?3Hg0=P%b04 zDRO&<+-Sh;K1p?w+lRP?g49Qoe@-&~jNFJ#NF!-_jWRnqiL=buRojAK<;BP~K|MY< z-8_1gwZSGPWn-&F5tJvOKm%)Z_2ggRQLi&Ou>zGiVbYHU9ikA=(zGw_A45|FcFdyu z35t%BiG$D)3$udh>0LaZYeL+=YNM@Pb=7YSpHPP{FZ1iQy;^N=W_Yc3Kz;QZjs@hr zRcdWW^M-N_Edcr)SGUdy8YiqYt#JcS%RAK0{n_#Z#GR~Jaz7b5^r-sJn^mqcSJS&* zb3m&(kgYki11U4II`n5rwc)@Te{dtvnJLc(=vlfJ zw4C3CVGxJ2;tAKW@xc*2o7Bfg&#n_8J>Ru(2F-0b(lJ0>Q1rkE{kHMQdV;tO< ziGwc_%Ty+&vrP1!&ViV~A)rLc2>BBIAR~-q?qve@x+5;iWNYXioCucig>v%Xo=ruO zCNV~O#DtzFriXwjO#$a8B^M6E$>j8!e2G+a-9R^KSec57?w*V#alDvH%soAPoLX1Q{!B1vvwf*7sO_DUr zA@!^W<1j`nKK{G84nxok}WsM&41s~!M0{F z9Q>?ytSX;`$RxC466)9^D`(ZvOIpX_Jc9@I#MLeGn?F>`R;sYh1}qBh(A0h{J%wg3PC diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/constant.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/constant.cpython-312.pyc deleted file mode 100644 index 90edd1693d95524116090caa695d0619db1776b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38751 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 Dkx4`v diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/legacy.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/legacy.cpython-312.pyc deleted file mode 100644 index d575cee79efb9c9ffe6bdd45dba72f53a709bb20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2495 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 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/models.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/models.cpython-312.pyc deleted file mode 100644 index bbf4542892d551579444dbfc898fd74788ec873c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16599 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 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/utils.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/utils.cpython-312.pyc deleted file mode 100644 index f28a8cabf4ec4a51212c18bcf8751fa40adb1398..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14362 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 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/version.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/__pycache__/version.cpython-312.pyc deleted file mode 100644 index 5c92f3f287665079451ee59dac2210e7fefc2339..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 321 zcmX@j%ge<81e!ss)BJ(-V-N=hn4yf%dO*f>h7^Vf zp;qb@RQ}?y$<0qG%}KQ@0tE%g6UB-^;sY}yBjZOVCPtPI91Q$o4cs@lM6Yv6U*wXW P;e44(p^?3a1E>lBZ`D=K diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/api.py b/.venv/lib/python3.12/site-packages/charset_normalizer/api.py deleted file mode 100644 index 0ba08e3..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/api.py +++ /dev/null @@ -1,626 +0,0 @@ -import logging -from os import PathLike -from typing import BinaryIO, List, Optional, Set, Union - -from .cd import ( - coherence_ratio, - encoding_languages, - mb_encoding_languages, - merge_coherence_ratios, -) -from .constant import IANA_SUPPORTED, TOO_BIG_SEQUENCE, TOO_SMALL_SEQUENCE, TRACE -from .md import mess_ratio -from .models import CharsetMatch, CharsetMatches -from .utils import ( - any_specified_encoding, - cut_sequence_chunks, - iana_name, - identify_sig_or_bom, - is_cp_similar, - is_multi_byte_encoding, - should_strip_sig_or_bom, -) - -# Will most likely be controversial -# logging.addLevelName(TRACE, "TRACE") -logger = logging.getLogger("charset_normalizer") -explain_handler = logging.StreamHandler() -explain_handler.setFormatter( - logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") -) - - -def from_bytes( - sequences: Union[bytes, bytearray], - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.2, - cp_isolation: Optional[List[str]] = None, - cp_exclusion: Optional[List[str]] = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Given a raw bytes sequence, return the best possibles charset usable to render str objects. - If there is no results, it is a strong indicator that the source is binary/not text. - By default, the process will extract 5 blocks of 512o each to assess the mess and coherence of a given sequence. - And will give up a particular code page after 20% of measured mess. Those criteria are customizable at will. - - The preemptive behavior DOES NOT replace the traditional detection workflow, it prioritize a particular code page - but never take it for granted. Can improve the performance. - - You may want to focus your attention to some code page or/and not others, use cp_isolation and cp_exclusion for that - purpose. - - This function will strip the SIG in the payload/sequence every time except on UTF-16, UTF-32. - By default the library does not setup any handler other than the NullHandler, if you choose to set the 'explain' - toggle to True it will alter the logger configuration to add a StreamHandler that is suitable for debugging. - Custom logging format and handler can be set manually. - """ - - if not isinstance(sequences, (bytearray, bytes)): - raise TypeError( - "Expected object of type bytes or bytearray, got: {0}".format( - type(sequences) - ) - ) - - if explain: - previous_logger_level: int = logger.level - logger.addHandler(explain_handler) - logger.setLevel(TRACE) - - length: int = len(sequences) - - if length == 0: - logger.debug("Encoding detection on empty bytes, assuming utf_8 intention.") - if explain: - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level or logging.WARNING) - return CharsetMatches([CharsetMatch(sequences, "utf_8", 0.0, False, [], "")]) - - if cp_isolation is not None: - logger.log( - TRACE, - "cp_isolation is set. use this flag for debugging purpose. " - "limited list of encoding allowed : %s.", - ", ".join(cp_isolation), - ) - cp_isolation = [iana_name(cp, False) for cp in cp_isolation] - else: - cp_isolation = [] - - if cp_exclusion is not None: - logger.log( - TRACE, - "cp_exclusion is set. use this flag for debugging purpose. " - "limited list of encoding excluded : %s.", - ", ".join(cp_exclusion), - ) - cp_exclusion = [iana_name(cp, False) for cp in cp_exclusion] - else: - cp_exclusion = [] - - if length <= (chunk_size * steps): - logger.log( - TRACE, - "override steps (%i) and chunk_size (%i) as content does not fit (%i byte(s) given) parameters.", - steps, - chunk_size, - length, - ) - steps = 1 - chunk_size = length - - if steps > 1 and length / steps < chunk_size: - chunk_size = int(length / steps) - - is_too_small_sequence: bool = len(sequences) < TOO_SMALL_SEQUENCE - is_too_large_sequence: bool = len(sequences) >= TOO_BIG_SEQUENCE - - if is_too_small_sequence: - logger.log( - TRACE, - "Trying to detect encoding from a tiny portion of ({}) byte(s).".format( - length - ), - ) - elif is_too_large_sequence: - logger.log( - TRACE, - "Using lazy str decoding because the payload is quite large, ({}) byte(s).".format( - length - ), - ) - - prioritized_encodings: List[str] = [] - - specified_encoding: Optional[str] = ( - any_specified_encoding(sequences) if preemptive_behaviour else None - ) - - if specified_encoding is not None: - prioritized_encodings.append(specified_encoding) - logger.log( - TRACE, - "Detected declarative mark in sequence. Priority +1 given for %s.", - specified_encoding, - ) - - tested: Set[str] = set() - tested_but_hard_failure: List[str] = [] - tested_but_soft_failure: List[str] = [] - - fallback_ascii: Optional[CharsetMatch] = None - fallback_u8: Optional[CharsetMatch] = None - fallback_specified: Optional[CharsetMatch] = None - - results: CharsetMatches = CharsetMatches() - - sig_encoding, sig_payload = identify_sig_or_bom(sequences) - - if sig_encoding is not None: - prioritized_encodings.append(sig_encoding) - logger.log( - TRACE, - "Detected a SIG or BOM mark on first %i byte(s). Priority +1 given for %s.", - len(sig_payload), - sig_encoding, - ) - - prioritized_encodings.append("ascii") - - if "utf_8" not in prioritized_encodings: - prioritized_encodings.append("utf_8") - - for encoding_iana in prioritized_encodings + IANA_SUPPORTED: - if cp_isolation and encoding_iana not in cp_isolation: - continue - - if cp_exclusion and encoding_iana in cp_exclusion: - continue - - if encoding_iana in tested: - continue - - tested.add(encoding_iana) - - decoded_payload: Optional[str] = None - bom_or_sig_available: bool = sig_encoding == encoding_iana - strip_sig_or_bom: bool = bom_or_sig_available and should_strip_sig_or_bom( - encoding_iana - ) - - if encoding_iana in {"utf_16", "utf_32"} and not bom_or_sig_available: - logger.log( - TRACE, - "Encoding %s won't be tested as-is because it require a BOM. Will try some sub-encoder LE/BE.", - encoding_iana, - ) - continue - if encoding_iana in {"utf_7"} and not bom_or_sig_available: - logger.log( - TRACE, - "Encoding %s won't be tested as-is because detection is unreliable without BOM/SIG.", - encoding_iana, - ) - continue - - try: - is_multi_byte_decoder: bool = is_multi_byte_encoding(encoding_iana) - except (ModuleNotFoundError, ImportError): - logger.log( - TRACE, - "Encoding %s does not provide an IncrementalDecoder", - encoding_iana, - ) - continue - - try: - if is_too_large_sequence and is_multi_byte_decoder is False: - str( - sequences[: int(50e4)] - if strip_sig_or_bom is False - else sequences[len(sig_payload) : int(50e4)], - encoding=encoding_iana, - ) - else: - decoded_payload = str( - sequences - if strip_sig_or_bom is False - else sequences[len(sig_payload) :], - encoding=encoding_iana, - ) - except (UnicodeDecodeError, LookupError) as e: - if not isinstance(e, LookupError): - logger.log( - TRACE, - "Code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - similar_soft_failure_test: bool = False - - for encoding_soft_failed in tested_but_soft_failure: - if is_cp_similar(encoding_iana, encoding_soft_failed): - similar_soft_failure_test = True - break - - if similar_soft_failure_test: - logger.log( - TRACE, - "%s is deemed too similar to code page %s and was consider unsuited already. Continuing!", - encoding_iana, - encoding_soft_failed, - ) - continue - - r_ = range( - 0 if not bom_or_sig_available else len(sig_payload), - length, - int(length / steps), - ) - - multi_byte_bonus: bool = ( - is_multi_byte_decoder - and decoded_payload is not None - and len(decoded_payload) < length - ) - - if multi_byte_bonus: - logger.log( - TRACE, - "Code page %s is a multi byte encoding table and it appear that at least one character " - "was encoded using n-bytes.", - encoding_iana, - ) - - max_chunk_gave_up: int = int(len(r_) / 4) - - max_chunk_gave_up = max(max_chunk_gave_up, 2) - early_stop_count: int = 0 - lazy_str_hard_failure = False - - md_chunks: List[str] = [] - md_ratios = [] - - try: - for chunk in cut_sequence_chunks( - sequences, - encoding_iana, - r_, - chunk_size, - bom_or_sig_available, - strip_sig_or_bom, - sig_payload, - is_multi_byte_decoder, - decoded_payload, - ): - md_chunks.append(chunk) - - md_ratios.append( - mess_ratio( - chunk, - threshold, - explain is True and 1 <= len(cp_isolation) <= 2, - ) - ) - - if md_ratios[-1] >= threshold: - early_stop_count += 1 - - if (early_stop_count >= max_chunk_gave_up) or ( - bom_or_sig_available and strip_sig_or_bom is False - ): - break - except ( - UnicodeDecodeError - ) as e: # Lazy str loading may have missed something there - logger.log( - TRACE, - "LazyStr Loading: After MD chunk decode, code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - early_stop_count = max_chunk_gave_up - lazy_str_hard_failure = True - - # We might want to check the sequence again with the whole content - # Only if initial MD tests passes - if ( - not lazy_str_hard_failure - and is_too_large_sequence - and not is_multi_byte_decoder - ): - try: - sequences[int(50e3) :].decode(encoding_iana, errors="strict") - except UnicodeDecodeError as e: - logger.log( - TRACE, - "LazyStr Loading: After final lookup, code page %s does not fit given bytes sequence at ALL. %s", - encoding_iana, - str(e), - ) - tested_but_hard_failure.append(encoding_iana) - continue - - mean_mess_ratio: float = sum(md_ratios) / len(md_ratios) if md_ratios else 0.0 - if mean_mess_ratio >= threshold or early_stop_count >= max_chunk_gave_up: - tested_but_soft_failure.append(encoding_iana) - logger.log( - TRACE, - "%s was excluded because of initial chaos probing. Gave up %i time(s). " - "Computed mean chaos is %f %%.", - encoding_iana, - early_stop_count, - round(mean_mess_ratio * 100, ndigits=3), - ) - # Preparing those fallbacks in case we got nothing. - if ( - enable_fallback - and encoding_iana in ["ascii", "utf_8", specified_encoding] - and not lazy_str_hard_failure - ): - fallback_entry = CharsetMatch( - sequences, encoding_iana, threshold, False, [], decoded_payload - ) - if encoding_iana == specified_encoding: - fallback_specified = fallback_entry - elif encoding_iana == "ascii": - fallback_ascii = fallback_entry - else: - fallback_u8 = fallback_entry - continue - - logger.log( - TRACE, - "%s passed initial chaos probing. Mean measured chaos is %f %%", - encoding_iana, - round(mean_mess_ratio * 100, ndigits=3), - ) - - if not is_multi_byte_decoder: - target_languages: List[str] = encoding_languages(encoding_iana) - else: - target_languages = mb_encoding_languages(encoding_iana) - - if target_languages: - logger.log( - TRACE, - "{} should target any language(s) of {}".format( - encoding_iana, str(target_languages) - ), - ) - - cd_ratios = [] - - # We shall skip the CD when its about ASCII - # Most of the time its not relevant to run "language-detection" on it. - if encoding_iana != "ascii": - for chunk in md_chunks: - chunk_languages = coherence_ratio( - chunk, - language_threshold, - ",".join(target_languages) if target_languages else None, - ) - - cd_ratios.append(chunk_languages) - - cd_ratios_merged = merge_coherence_ratios(cd_ratios) - - if cd_ratios_merged: - logger.log( - TRACE, - "We detected language {} using {}".format( - cd_ratios_merged, encoding_iana - ), - ) - - results.append( - CharsetMatch( - sequences, - encoding_iana, - mean_mess_ratio, - bom_or_sig_available, - cd_ratios_merged, - decoded_payload, - ) - ) - - if ( - encoding_iana in [specified_encoding, "ascii", "utf_8"] - and mean_mess_ratio < 0.1 - ): - logger.debug( - "Encoding detection: %s is most likely the one.", encoding_iana - ) - if explain: - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - return CharsetMatches([results[encoding_iana]]) - - if encoding_iana == sig_encoding: - logger.debug( - "Encoding detection: %s is most likely the one as we detected a BOM or SIG within " - "the beginning of the sequence.", - encoding_iana, - ) - if explain: - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - return CharsetMatches([results[encoding_iana]]) - - if len(results) == 0: - if fallback_u8 or fallback_ascii or fallback_specified: - logger.log( - TRACE, - "Nothing got out of the detection process. Using ASCII/UTF-8/Specified fallback.", - ) - - if fallback_specified: - logger.debug( - "Encoding detection: %s will be used as a fallback match", - fallback_specified.encoding, - ) - results.append(fallback_specified) - elif ( - (fallback_u8 and fallback_ascii is None) - or ( - fallback_u8 - and fallback_ascii - and fallback_u8.fingerprint != fallback_ascii.fingerprint - ) - or (fallback_u8 is not None) - ): - logger.debug("Encoding detection: utf_8 will be used as a fallback match") - results.append(fallback_u8) - elif fallback_ascii: - logger.debug("Encoding detection: ascii will be used as a fallback match") - results.append(fallback_ascii) - - if results: - logger.debug( - "Encoding detection: Found %s as plausible (best-candidate) for content. With %i alternatives.", - results.best().encoding, # type: ignore - len(results) - 1, - ) - else: - logger.debug("Encoding detection: Unable to determine any suitable charset.") - - if explain: - logger.removeHandler(explain_handler) - logger.setLevel(previous_logger_level) - - return results - - -def from_fp( - fp: BinaryIO, - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: Optional[List[str]] = None, - cp_exclusion: Optional[List[str]] = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but using a file pointer that is already ready. - Will not close the file pointer. - """ - return from_bytes( - fp.read(), - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - language_threshold, - enable_fallback, - ) - - -def from_path( - path: Union[str, bytes, PathLike], # type: ignore[type-arg] - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: Optional[List[str]] = None, - cp_exclusion: Optional[List[str]] = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = True, -) -> CharsetMatches: - """ - Same thing than the function from_bytes but with one extra step. Opening and reading given file path in binary mode. - Can raise IOError. - """ - with open(path, "rb") as fp: - return from_fp( - fp, - steps, - chunk_size, - threshold, - cp_isolation, - cp_exclusion, - preemptive_behaviour, - explain, - language_threshold, - enable_fallback, - ) - - -def is_binary( - fp_or_path_or_payload: Union[PathLike, str, BinaryIO, bytes], # type: ignore[type-arg] - steps: int = 5, - chunk_size: int = 512, - threshold: float = 0.20, - cp_isolation: Optional[List[str]] = None, - cp_exclusion: Optional[List[str]] = None, - preemptive_behaviour: bool = True, - explain: bool = False, - language_threshold: float = 0.1, - enable_fallback: bool = False, -) -> bool: - """ - Detect if the given input (file, bytes, or path) points to a binary file. aka. not a string. - Based on the same main heuristic algorithms and default kwargs at the sole exception that fallbacks match - are disabled to be stricter around ASCII-compatible but unlikely to be a string. - """ - if isinstance(fp_or_path_or_payload, (str, PathLike)): - guesses = from_path( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - elif isinstance( - fp_or_path_or_payload, - ( - bytes, - bytearray, - ), - ): - guesses = from_bytes( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - else: - guesses = from_fp( - fp_or_path_or_payload, - steps=steps, - chunk_size=chunk_size, - threshold=threshold, - cp_isolation=cp_isolation, - cp_exclusion=cp_exclusion, - preemptive_behaviour=preemptive_behaviour, - explain=explain, - language_threshold=language_threshold, - enable_fallback=enable_fallback, - ) - - return not guesses diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cd.py b/.venv/lib/python3.12/site-packages/charset_normalizer/cd.py deleted file mode 100644 index 4ea6760..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/cd.py +++ /dev/null @@ -1,395 +0,0 @@ -import importlib -from codecs import IncrementalDecoder -from collections import Counter -from functools import lru_cache -from typing import Counter as TypeCounter, Dict, List, Optional, Tuple - -from .constant import ( - FREQUENCIES, - KO_NAMES, - LANGUAGE_SUPPORTED_COUNT, - TOO_SMALL_SEQUENCE, - ZH_NAMES, -) -from .md import is_suspiciously_successive_range -from .models import CoherenceMatches -from .utils import ( - is_accentuated, - is_latin, - is_multi_byte_encoding, - is_unicode_range_secondary, - unicode_range, -) - - -def encoding_unicode_range(iana_name: str) -> List[str]: - """ - Return associated unicode ranges in a single byte code page. - """ - if is_multi_byte_encoding(iana_name): - raise IOError("Function not supported on multi-byte code page") - - decoder = importlib.import_module( - "encodings.{}".format(iana_name) - ).IncrementalDecoder - - p: IncrementalDecoder = decoder(errors="ignore") - seen_ranges: Dict[str, int] = {} - character_count: int = 0 - - for i in range(0x40, 0xFF): - chunk: str = p.decode(bytes([i])) - - if chunk: - character_range: Optional[str] = unicode_range(chunk) - - if character_range is None: - continue - - if is_unicode_range_secondary(character_range) is False: - if character_range not in seen_ranges: - seen_ranges[character_range] = 0 - seen_ranges[character_range] += 1 - character_count += 1 - - return sorted( - [ - character_range - for character_range in seen_ranges - if seen_ranges[character_range] / character_count >= 0.15 - ] - ) - - -def unicode_range_languages(primary_range: str) -> List[str]: - """ - Return inferred languages used with a unicode range. - """ - languages: List[str] = [] - - for language, characters in FREQUENCIES.items(): - for character in characters: - if unicode_range(character) == primary_range: - languages.append(language) - break - - return languages - - -@lru_cache() -def encoding_languages(iana_name: str) -> List[str]: - """ - Single-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - unicode_ranges: List[str] = encoding_unicode_range(iana_name) - primary_range: Optional[str] = None - - for specified_range in unicode_ranges: - if "Latin" not in specified_range: - primary_range = specified_range - break - - if primary_range is None: - return ["Latin Based"] - - return unicode_range_languages(primary_range) - - -@lru_cache() -def mb_encoding_languages(iana_name: str) -> List[str]: - """ - Multi-byte encoding language association. Some code page are heavily linked to particular language(s). - This function does the correspondence. - """ - if ( - iana_name.startswith("shift_") - or iana_name.startswith("iso2022_jp") - or iana_name.startswith("euc_j") - or iana_name == "cp932" - ): - return ["Japanese"] - if iana_name.startswith("gb") or iana_name in ZH_NAMES: - return ["Chinese"] - if iana_name.startswith("iso2022_kr") or iana_name in KO_NAMES: - return ["Korean"] - - return [] - - -@lru_cache(maxsize=LANGUAGE_SUPPORTED_COUNT) -def get_target_features(language: str) -> Tuple[bool, bool]: - """ - Determine main aspects from a supported language if it contains accents and if is pure Latin. - """ - target_have_accents: bool = False - target_pure_latin: bool = True - - for character in FREQUENCIES[language]: - if not target_have_accents and is_accentuated(character): - target_have_accents = True - if target_pure_latin and is_latin(character) is False: - target_pure_latin = False - - return target_have_accents, target_pure_latin - - -def alphabet_languages( - characters: List[str], ignore_non_latin: bool = False -) -> List[str]: - """ - Return associated languages associated to given characters. - """ - languages: List[Tuple[str, float]] = [] - - source_have_accents = any(is_accentuated(character) for character in characters) - - for language, language_characters in FREQUENCIES.items(): - target_have_accents, target_pure_latin = get_target_features(language) - - if ignore_non_latin and target_pure_latin is False: - continue - - if target_have_accents is False and source_have_accents: - continue - - character_count: int = len(language_characters) - - character_match_count: int = len( - [c for c in language_characters if c in characters] - ) - - ratio: float = character_match_count / character_count - - if ratio >= 0.2: - languages.append((language, ratio)) - - languages = sorted(languages, key=lambda x: x[1], reverse=True) - - return [compatible_language[0] for compatible_language in languages] - - -def characters_popularity_compare( - language: str, ordered_characters: List[str] -) -> float: - """ - Determine if a ordered characters list (by occurrence from most appearance to rarest) match a particular language. - The result is a ratio between 0. (absolutely no correspondence) and 1. (near perfect fit). - Beware that is function is not strict on the match in order to ease the detection. (Meaning close match is 1.) - """ - if language not in FREQUENCIES: - raise ValueError("{} not available".format(language)) - - character_approved_count: int = 0 - FREQUENCIES_language_set = set(FREQUENCIES[language]) - - ordered_characters_count: int = len(ordered_characters) - target_language_characters_count: int = len(FREQUENCIES[language]) - - large_alphabet: bool = target_language_characters_count > 26 - - for character, character_rank in zip( - ordered_characters, range(0, ordered_characters_count) - ): - if character not in FREQUENCIES_language_set: - continue - - character_rank_in_language: int = FREQUENCIES[language].index(character) - expected_projection_ratio: float = ( - target_language_characters_count / ordered_characters_count - ) - character_rank_projection: int = int(character_rank * expected_projection_ratio) - - if ( - large_alphabet is False - and abs(character_rank_projection - character_rank_in_language) > 4 - ): - continue - - if ( - large_alphabet is True - and abs(character_rank_projection - character_rank_in_language) - < target_language_characters_count / 3 - ): - character_approved_count += 1 - continue - - characters_before_source: List[str] = FREQUENCIES[language][ - 0:character_rank_in_language - ] - characters_after_source: List[str] = FREQUENCIES[language][ - character_rank_in_language: - ] - characters_before: List[str] = ordered_characters[0:character_rank] - characters_after: List[str] = ordered_characters[character_rank:] - - before_match_count: int = len( - set(characters_before) & set(characters_before_source) - ) - - after_match_count: int = len( - set(characters_after) & set(characters_after_source) - ) - - if len(characters_before_source) == 0 and before_match_count <= 4: - character_approved_count += 1 - continue - - if len(characters_after_source) == 0 and after_match_count <= 4: - character_approved_count += 1 - continue - - if ( - before_match_count / len(characters_before_source) >= 0.4 - or after_match_count / len(characters_after_source) >= 0.4 - ): - character_approved_count += 1 - continue - - return character_approved_count / len(ordered_characters) - - -def alpha_unicode_split(decoded_sequence: str) -> List[str]: - """ - Given a decoded text sequence, return a list of str. Unicode range / alphabet separation. - Ex. a text containing English/Latin with a bit a Hebrew will return two items in the resulting list; - One containing the latin letters and the other hebrew. - """ - layers: Dict[str, str] = {} - - for character in decoded_sequence: - if character.isalpha() is False: - continue - - character_range: Optional[str] = unicode_range(character) - - if character_range is None: - continue - - layer_target_range: Optional[str] = None - - for discovered_range in layers: - if ( - is_suspiciously_successive_range(discovered_range, character_range) - is False - ): - layer_target_range = discovered_range - break - - if layer_target_range is None: - layer_target_range = character_range - - if layer_target_range not in layers: - layers[layer_target_range] = character.lower() - continue - - layers[layer_target_range] += character.lower() - - return list(layers.values()) - - -def merge_coherence_ratios(results: List[CoherenceMatches]) -> CoherenceMatches: - """ - This function merge results previously given by the function coherence_ratio. - The return type is the same as coherence_ratio. - """ - per_language_ratios: Dict[str, List[float]] = {} - for result in results: - for sub_result in result: - language, ratio = sub_result - if language not in per_language_ratios: - per_language_ratios[language] = [ratio] - continue - per_language_ratios[language].append(ratio) - - merge = [ - ( - language, - round( - sum(per_language_ratios[language]) / len(per_language_ratios[language]), - 4, - ), - ) - for language in per_language_ratios - ] - - return sorted(merge, key=lambda x: x[1], reverse=True) - - -def filter_alt_coherence_matches(results: CoherenceMatches) -> CoherenceMatches: - """ - We shall NOT return "English—" in CoherenceMatches because it is an alternative - of "English". This function only keeps the best match and remove the em-dash in it. - """ - index_results: Dict[str, List[float]] = dict() - - for result in results: - language, ratio = result - no_em_name: str = language.replace("—", "") - - if no_em_name not in index_results: - index_results[no_em_name] = [] - - index_results[no_em_name].append(ratio) - - if any(len(index_results[e]) > 1 for e in index_results): - filtered_results: CoherenceMatches = [] - - for language in index_results: - filtered_results.append((language, max(index_results[language]))) - - return filtered_results - - return results - - -@lru_cache(maxsize=2048) -def coherence_ratio( - decoded_sequence: str, threshold: float = 0.1, lg_inclusion: Optional[str] = None -) -> CoherenceMatches: - """ - Detect ANY language that can be identified in given sequence. The sequence will be analysed by layers. - A layer = Character extraction by alphabets/ranges. - """ - - results: List[Tuple[str, float]] = [] - ignore_non_latin: bool = False - - sufficient_match_count: int = 0 - - lg_inclusion_list = lg_inclusion.split(",") if lg_inclusion is not None else [] - if "Latin Based" in lg_inclusion_list: - ignore_non_latin = True - lg_inclusion_list.remove("Latin Based") - - for layer in alpha_unicode_split(decoded_sequence): - sequence_frequencies: TypeCounter[str] = Counter(layer) - most_common = sequence_frequencies.most_common() - - character_count: int = sum(o for c, o in most_common) - - if character_count <= TOO_SMALL_SEQUENCE: - continue - - popular_character_ordered: List[str] = [c for c, o in most_common] - - for language in lg_inclusion_list or alphabet_languages( - popular_character_ordered, ignore_non_latin - ): - ratio: float = characters_popularity_compare( - language, popular_character_ordered - ) - - if ratio < threshold: - continue - elif ratio >= 0.8: - sufficient_match_count += 1 - - results.append((language, round(ratio, 4))) - - if sufficient_match_count >= 3: - break - - return sorted( - filter_alt_coherence_matches(results), key=lambda x: x[1], reverse=True - ) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py deleted file mode 100644 index d95fedf..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .__main__ import cli_detect, query_yes_no - -__all__ = ( - "cli_detect", - "query_yes_no", -) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py deleted file mode 100644 index f4bcbaa..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__main__.py +++ /dev/null @@ -1,296 +0,0 @@ -import argparse -import sys -from json import dumps -from os.path import abspath, basename, dirname, join, realpath -from platform import python_version -from typing import List, Optional -from unicodedata import unidata_version - -import charset_normalizer.md as md_module -from charset_normalizer import from_fp -from charset_normalizer.models import CliDetectionResult -from charset_normalizer.version import __version__ - - -def query_yes_no(question: str, default: str = "yes") -> bool: - """Ask a yes/no question via input() and return their answer. - - "question" is a string that is presented to the user. - "default" is the presumed answer if the user just hits . - It must be "yes" (the default), "no" or None (meaning - an answer is required of the user). - - The "answer" return value is True for "yes" or False for "no". - - Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input - """ - valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} - if default is None: - prompt = " [y/n] " - elif default == "yes": - prompt = " [Y/n] " - elif default == "no": - prompt = " [y/N] " - else: - raise ValueError("invalid default answer: '%s'" % default) - - while True: - sys.stdout.write(question + prompt) - choice = input().lower() - if default is not None and choice == "": - return valid[default] - elif choice in valid: - return valid[choice] - else: - sys.stdout.write("Please respond with 'yes' or 'no' " "(or 'y' or 'n').\n") - - -def cli_detect(argv: Optional[List[str]] = None) -> int: - """ - CLI assistant using ARGV and ArgumentParser - :param argv: - :return: 0 if everything is fine, anything else equal trouble - """ - parser = argparse.ArgumentParser( - description="The Real First Universal Charset Detector. " - "Discover originating encoding used on text file. " - "Normalize text to unicode." - ) - - parser.add_argument( - "files", type=argparse.FileType("rb"), nargs="+", help="File(s) to be analysed" - ) - parser.add_argument( - "-v", - "--verbose", - action="store_true", - default=False, - dest="verbose", - help="Display complementary information about file if any. " - "Stdout will contain logs about the detection process.", - ) - parser.add_argument( - "-a", - "--with-alternative", - action="store_true", - default=False, - dest="alternatives", - help="Output complementary possibilities if any. Top-level JSON WILL be a list.", - ) - parser.add_argument( - "-n", - "--normalize", - action="store_true", - default=False, - dest="normalize", - help="Permit to normalize input file. If not set, program does not write anything.", - ) - parser.add_argument( - "-m", - "--minimal", - action="store_true", - default=False, - dest="minimal", - help="Only output the charset detected to STDOUT. Disabling JSON output.", - ) - parser.add_argument( - "-r", - "--replace", - action="store_true", - default=False, - dest="replace", - help="Replace file when trying to normalize it instead of creating a new one.", - ) - parser.add_argument( - "-f", - "--force", - action="store_true", - default=False, - dest="force", - help="Replace file without asking if you are sure, use this flag with caution.", - ) - parser.add_argument( - "-t", - "--threshold", - action="store", - default=0.2, - type=float, - dest="threshold", - help="Define a custom maximum amount of chaos allowed in decoded content. 0. <= chaos <= 1.", - ) - parser.add_argument( - "--version", - action="version", - version="Charset-Normalizer {} - Python {} - Unicode {} - SpeedUp {}".format( - __version__, - python_version(), - unidata_version, - "OFF" if md_module.__file__.lower().endswith(".py") else "ON", - ), - help="Show version information and exit.", - ) - - args = parser.parse_args(argv) - - if args.replace is True and args.normalize is False: - print("Use --replace in addition of --normalize only.", file=sys.stderr) - return 1 - - if args.force is True and args.replace is False: - print("Use --force in addition of --replace only.", file=sys.stderr) - return 1 - - if args.threshold < 0.0 or args.threshold > 1.0: - print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) - return 1 - - x_ = [] - - for my_file in args.files: - matches = from_fp(my_file, threshold=args.threshold, explain=args.verbose) - - best_guess = matches.best() - - if best_guess is None: - print( - 'Unable to identify originating encoding for "{}". {}'.format( - my_file.name, - "Maybe try increasing maximum amount of chaos." - if args.threshold < 1.0 - else "", - ), - file=sys.stderr, - ) - x_.append( - CliDetectionResult( - abspath(my_file.name), - None, - [], - [], - "Unknown", - [], - False, - 1.0, - 0.0, - None, - True, - ) - ) - else: - x_.append( - CliDetectionResult( - abspath(my_file.name), - best_guess.encoding, - best_guess.encoding_aliases, - [ - cp - for cp in best_guess.could_be_from_charset - if cp != best_guess.encoding - ], - best_guess.language, - best_guess.alphabets, - best_guess.bom, - best_guess.percent_chaos, - best_guess.percent_coherence, - None, - True, - ) - ) - - if len(matches) > 1 and args.alternatives: - for el in matches: - if el != best_guess: - x_.append( - CliDetectionResult( - abspath(my_file.name), - el.encoding, - el.encoding_aliases, - [ - cp - for cp in el.could_be_from_charset - if cp != el.encoding - ], - el.language, - el.alphabets, - el.bom, - el.percent_chaos, - el.percent_coherence, - None, - False, - ) - ) - - if args.normalize is True: - if best_guess.encoding.startswith("utf") is True: - print( - '"{}" file does not need to be normalized, as it already came from unicode.'.format( - my_file.name - ), - file=sys.stderr, - ) - if my_file.closed is False: - my_file.close() - continue - - dir_path = dirname(realpath(my_file.name)) - file_name = basename(realpath(my_file.name)) - - o_: List[str] = file_name.split(".") - - if args.replace is False: - o_.insert(-1, best_guess.encoding) - if my_file.closed is False: - my_file.close() - elif ( - args.force is False - and query_yes_no( - 'Are you sure to normalize "{}" by replacing it ?'.format( - my_file.name - ), - "no", - ) - is False - ): - if my_file.closed is False: - my_file.close() - continue - - try: - x_[0].unicode_path = join(dir_path, ".".join(o_)) - - with open(x_[0].unicode_path, "w", encoding="utf-8") as fp: - fp.write(str(best_guess)) - except IOError as e: - print(str(e), file=sys.stderr) - if my_file.closed is False: - my_file.close() - return 2 - - if my_file.closed is False: - my_file.close() - - if args.minimal is False: - print( - dumps( - [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, - ensure_ascii=True, - indent=4, - ) - ) - else: - for my_file in args.files: - print( - ", ".join( - [ - el.encoding or "undefined" - for el in x_ - if el.path == abspath(my_file.name) - ] - ) - ) - - return 0 - - -if __name__ == "__main__": - cli_detect() diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index b391c4cff8ceebf3d2ce328e8f9f5c2abac2c72f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 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 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/constant.py b/.venv/lib/python3.12/site-packages/charset_normalizer/constant.py deleted file mode 100644 index 8634904..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/constant.py +++ /dev/null @@ -1,1995 +0,0 @@ -# -*- coding: utf-8 -*- -from codecs import BOM_UTF8, BOM_UTF16_BE, BOM_UTF16_LE, BOM_UTF32_BE, BOM_UTF32_LE -from encodings.aliases import aliases -from re import IGNORECASE, compile as re_compile -from typing import Dict, List, Set, Union - -# Contain for each eligible encoding a list of/item bytes SIG/BOM -ENCODING_MARKS: Dict[str, Union[bytes, List[bytes]]] = { - "utf_8": BOM_UTF8, - "utf_7": [ - b"\x2b\x2f\x76\x38", - b"\x2b\x2f\x76\x39", - b"\x2b\x2f\x76\x2b", - b"\x2b\x2f\x76\x2f", - b"\x2b\x2f\x76\x38\x2d", - ], - "gb18030": b"\x84\x31\x95\x33", - "utf_32": [BOM_UTF32_BE, BOM_UTF32_LE], - "utf_16": [BOM_UTF16_BE, BOM_UTF16_LE], -} - -TOO_SMALL_SEQUENCE: int = 32 -TOO_BIG_SEQUENCE: int = int(10e6) - -UTF8_MAXIMAL_ALLOCATION: int = 1_112_064 - -# Up-to-date Unicode ucd/15.0.0 -UNICODE_RANGES_COMBINED: Dict[str, range] = { - "Control character": range(32), - "Basic Latin": range(32, 128), - "Latin-1 Supplement": range(128, 256), - "Latin Extended-A": range(256, 384), - "Latin Extended-B": range(384, 592), - "IPA Extensions": range(592, 688), - "Spacing Modifier Letters": range(688, 768), - "Combining Diacritical Marks": range(768, 880), - "Greek and Coptic": range(880, 1024), - "Cyrillic": range(1024, 1280), - "Cyrillic Supplement": range(1280, 1328), - "Armenian": range(1328, 1424), - "Hebrew": range(1424, 1536), - "Arabic": range(1536, 1792), - "Syriac": range(1792, 1872), - "Arabic Supplement": range(1872, 1920), - "Thaana": range(1920, 1984), - "NKo": range(1984, 2048), - "Samaritan": range(2048, 2112), - "Mandaic": range(2112, 2144), - "Syriac Supplement": range(2144, 2160), - "Arabic Extended-B": range(2160, 2208), - "Arabic Extended-A": range(2208, 2304), - "Devanagari": range(2304, 2432), - "Bengali": range(2432, 2560), - "Gurmukhi": range(2560, 2688), - "Gujarati": range(2688, 2816), - "Oriya": range(2816, 2944), - "Tamil": range(2944, 3072), - "Telugu": range(3072, 3200), - "Kannada": range(3200, 3328), - "Malayalam": range(3328, 3456), - "Sinhala": range(3456, 3584), - "Thai": range(3584, 3712), - "Lao": range(3712, 3840), - "Tibetan": range(3840, 4096), - "Myanmar": range(4096, 4256), - "Georgian": range(4256, 4352), - "Hangul Jamo": range(4352, 4608), - "Ethiopic": range(4608, 4992), - "Ethiopic Supplement": range(4992, 5024), - "Cherokee": range(5024, 5120), - "Unified Canadian Aboriginal Syllabics": range(5120, 5760), - "Ogham": range(5760, 5792), - "Runic": range(5792, 5888), - "Tagalog": range(5888, 5920), - "Hanunoo": range(5920, 5952), - "Buhid": range(5952, 5984), - "Tagbanwa": range(5984, 6016), - "Khmer": range(6016, 6144), - "Mongolian": range(6144, 6320), - "Unified Canadian Aboriginal Syllabics Extended": range(6320, 6400), - "Limbu": range(6400, 6480), - "Tai Le": range(6480, 6528), - "New Tai Lue": range(6528, 6624), - "Khmer Symbols": range(6624, 6656), - "Buginese": range(6656, 6688), - "Tai Tham": range(6688, 6832), - "Combining Diacritical Marks Extended": range(6832, 6912), - "Balinese": range(6912, 7040), - "Sundanese": range(7040, 7104), - "Batak": range(7104, 7168), - "Lepcha": range(7168, 7248), - "Ol Chiki": range(7248, 7296), - "Cyrillic Extended-C": range(7296, 7312), - "Georgian Extended": range(7312, 7360), - "Sundanese Supplement": range(7360, 7376), - "Vedic Extensions": range(7376, 7424), - "Phonetic Extensions": range(7424, 7552), - "Phonetic Extensions Supplement": range(7552, 7616), - "Combining Diacritical Marks Supplement": range(7616, 7680), - "Latin Extended Additional": range(7680, 7936), - "Greek Extended": range(7936, 8192), - "General Punctuation": range(8192, 8304), - "Superscripts and Subscripts": range(8304, 8352), - "Currency Symbols": range(8352, 8400), - "Combining Diacritical Marks for Symbols": range(8400, 8448), - "Letterlike Symbols": range(8448, 8528), - "Number Forms": range(8528, 8592), - "Arrows": range(8592, 8704), - "Mathematical Operators": range(8704, 8960), - "Miscellaneous Technical": range(8960, 9216), - "Control Pictures": range(9216, 9280), - "Optical Character Recognition": range(9280, 9312), - "Enclosed Alphanumerics": range(9312, 9472), - "Box Drawing": range(9472, 9600), - "Block Elements": range(9600, 9632), - "Geometric Shapes": range(9632, 9728), - "Miscellaneous Symbols": range(9728, 9984), - "Dingbats": range(9984, 10176), - "Miscellaneous Mathematical Symbols-A": range(10176, 10224), - "Supplemental Arrows-A": range(10224, 10240), - "Braille Patterns": range(10240, 10496), - "Supplemental Arrows-B": range(10496, 10624), - "Miscellaneous Mathematical Symbols-B": range(10624, 10752), - "Supplemental Mathematical Operators": range(10752, 11008), - "Miscellaneous Symbols and Arrows": range(11008, 11264), - "Glagolitic": range(11264, 11360), - "Latin Extended-C": range(11360, 11392), - "Coptic": range(11392, 11520), - "Georgian Supplement": range(11520, 11568), - "Tifinagh": range(11568, 11648), - "Ethiopic Extended": range(11648, 11744), - "Cyrillic Extended-A": range(11744, 11776), - "Supplemental Punctuation": range(11776, 11904), - "CJK Radicals Supplement": range(11904, 12032), - "Kangxi Radicals": range(12032, 12256), - "Ideographic Description Characters": range(12272, 12288), - "CJK Symbols and Punctuation": range(12288, 12352), - "Hiragana": range(12352, 12448), - "Katakana": range(12448, 12544), - "Bopomofo": range(12544, 12592), - "Hangul Compatibility Jamo": range(12592, 12688), - "Kanbun": range(12688, 12704), - "Bopomofo Extended": range(12704, 12736), - "CJK Strokes": range(12736, 12784), - "Katakana Phonetic Extensions": range(12784, 12800), - "Enclosed CJK Letters and Months": range(12800, 13056), - "CJK Compatibility": range(13056, 13312), - "CJK Unified Ideographs Extension A": range(13312, 19904), - "Yijing Hexagram Symbols": range(19904, 19968), - "CJK Unified Ideographs": range(19968, 40960), - "Yi Syllables": range(40960, 42128), - "Yi Radicals": range(42128, 42192), - "Lisu": range(42192, 42240), - "Vai": range(42240, 42560), - "Cyrillic Extended-B": range(42560, 42656), - "Bamum": range(42656, 42752), - "Modifier Tone Letters": range(42752, 42784), - "Latin Extended-D": range(42784, 43008), - "Syloti Nagri": range(43008, 43056), - "Common Indic Number Forms": range(43056, 43072), - "Phags-pa": range(43072, 43136), - "Saurashtra": range(43136, 43232), - "Devanagari Extended": range(43232, 43264), - "Kayah Li": range(43264, 43312), - "Rejang": range(43312, 43360), - "Hangul Jamo Extended-A": range(43360, 43392), - "Javanese": range(43392, 43488), - "Myanmar Extended-B": range(43488, 43520), - "Cham": range(43520, 43616), - "Myanmar Extended-A": range(43616, 43648), - "Tai Viet": range(43648, 43744), - "Meetei Mayek Extensions": range(43744, 43776), - "Ethiopic Extended-A": range(43776, 43824), - "Latin Extended-E": range(43824, 43888), - "Cherokee Supplement": range(43888, 43968), - "Meetei Mayek": range(43968, 44032), - "Hangul Syllables": range(44032, 55216), - "Hangul Jamo Extended-B": range(55216, 55296), - "High Surrogates": range(55296, 56192), - "High Private Use Surrogates": range(56192, 56320), - "Low Surrogates": range(56320, 57344), - "Private Use Area": range(57344, 63744), - "CJK Compatibility Ideographs": range(63744, 64256), - "Alphabetic Presentation Forms": range(64256, 64336), - "Arabic Presentation Forms-A": range(64336, 65024), - "Variation Selectors": range(65024, 65040), - "Vertical Forms": range(65040, 65056), - "Combining Half Marks": range(65056, 65072), - "CJK Compatibility Forms": range(65072, 65104), - "Small Form Variants": range(65104, 65136), - "Arabic Presentation Forms-B": range(65136, 65280), - "Halfwidth and Fullwidth Forms": range(65280, 65520), - "Specials": range(65520, 65536), - "Linear B Syllabary": range(65536, 65664), - "Linear B Ideograms": range(65664, 65792), - "Aegean Numbers": range(65792, 65856), - "Ancient Greek Numbers": range(65856, 65936), - "Ancient Symbols": range(65936, 66000), - "Phaistos Disc": range(66000, 66048), - "Lycian": range(66176, 66208), - "Carian": range(66208, 66272), - "Coptic Epact Numbers": range(66272, 66304), - "Old Italic": range(66304, 66352), - "Gothic": range(66352, 66384), - "Old Permic": range(66384, 66432), - "Ugaritic": range(66432, 66464), - "Old Persian": range(66464, 66528), - "Deseret": range(66560, 66640), - "Shavian": range(66640, 66688), - "Osmanya": range(66688, 66736), - "Osage": range(66736, 66816), - "Elbasan": range(66816, 66864), - "Caucasian Albanian": range(66864, 66928), - "Vithkuqi": range(66928, 67008), - "Linear A": range(67072, 67456), - "Latin Extended-F": range(67456, 67520), - "Cypriot Syllabary": range(67584, 67648), - "Imperial Aramaic": range(67648, 67680), - "Palmyrene": range(67680, 67712), - "Nabataean": range(67712, 67760), - "Hatran": range(67808, 67840), - "Phoenician": range(67840, 67872), - "Lydian": range(67872, 67904), - "Meroitic Hieroglyphs": range(67968, 68000), - "Meroitic Cursive": range(68000, 68096), - "Kharoshthi": range(68096, 68192), - "Old South Arabian": range(68192, 68224), - "Old North Arabian": range(68224, 68256), - "Manichaean": range(68288, 68352), - "Avestan": range(68352, 68416), - "Inscriptional Parthian": range(68416, 68448), - "Inscriptional Pahlavi": range(68448, 68480), - "Psalter Pahlavi": range(68480, 68528), - "Old Turkic": range(68608, 68688), - "Old Hungarian": range(68736, 68864), - "Hanifi Rohingya": range(68864, 68928), - "Rumi Numeral Symbols": range(69216, 69248), - "Yezidi": range(69248, 69312), - "Arabic Extended-C": range(69312, 69376), - "Old Sogdian": range(69376, 69424), - "Sogdian": range(69424, 69488), - "Old Uyghur": range(69488, 69552), - "Chorasmian": range(69552, 69600), - "Elymaic": range(69600, 69632), - "Brahmi": range(69632, 69760), - "Kaithi": range(69760, 69840), - "Sora Sompeng": range(69840, 69888), - "Chakma": range(69888, 69968), - "Mahajani": range(69968, 70016), - "Sharada": range(70016, 70112), - "Sinhala Archaic Numbers": range(70112, 70144), - "Khojki": range(70144, 70224), - "Multani": range(70272, 70320), - "Khudawadi": range(70320, 70400), - "Grantha": range(70400, 70528), - "Newa": range(70656, 70784), - "Tirhuta": range(70784, 70880), - "Siddham": range(71040, 71168), - "Modi": range(71168, 71264), - "Mongolian Supplement": range(71264, 71296), - "Takri": range(71296, 71376), - "Ahom": range(71424, 71504), - "Dogra": range(71680, 71760), - "Warang Citi": range(71840, 71936), - "Dives Akuru": range(71936, 72032), - "Nandinagari": range(72096, 72192), - "Zanabazar Square": range(72192, 72272), - "Soyombo": range(72272, 72368), - "Unified Canadian Aboriginal Syllabics Extended-A": range(72368, 72384), - "Pau Cin Hau": range(72384, 72448), - "Devanagari Extended-A": range(72448, 72544), - "Bhaiksuki": range(72704, 72816), - "Marchen": range(72816, 72896), - "Masaram Gondi": range(72960, 73056), - "Gunjala Gondi": range(73056, 73136), - "Makasar": range(73440, 73472), - "Kawi": range(73472, 73568), - "Lisu Supplement": range(73648, 73664), - "Tamil Supplement": range(73664, 73728), - "Cuneiform": range(73728, 74752), - "Cuneiform Numbers and Punctuation": range(74752, 74880), - "Early Dynastic Cuneiform": range(74880, 75088), - "Cypro-Minoan": range(77712, 77824), - "Egyptian Hieroglyphs": range(77824, 78896), - "Egyptian Hieroglyph Format Controls": range(78896, 78944), - "Anatolian Hieroglyphs": range(82944, 83584), - "Bamum Supplement": range(92160, 92736), - "Mro": range(92736, 92784), - "Tangsa": range(92784, 92880), - "Bassa Vah": range(92880, 92928), - "Pahawh Hmong": range(92928, 93072), - "Medefaidrin": range(93760, 93856), - "Miao": range(93952, 94112), - "Ideographic Symbols and Punctuation": range(94176, 94208), - "Tangut": range(94208, 100352), - "Tangut Components": range(100352, 101120), - "Khitan Small Script": range(101120, 101632), - "Tangut Supplement": range(101632, 101760), - "Kana Extended-B": range(110576, 110592), - "Kana Supplement": range(110592, 110848), - "Kana Extended-A": range(110848, 110896), - "Small Kana Extension": range(110896, 110960), - "Nushu": range(110960, 111360), - "Duployan": range(113664, 113824), - "Shorthand Format Controls": range(113824, 113840), - "Znamenny Musical Notation": range(118528, 118736), - "Byzantine Musical Symbols": range(118784, 119040), - "Musical Symbols": range(119040, 119296), - "Ancient Greek Musical Notation": range(119296, 119376), - "Kaktovik Numerals": range(119488, 119520), - "Mayan Numerals": range(119520, 119552), - "Tai Xuan Jing Symbols": range(119552, 119648), - "Counting Rod Numerals": range(119648, 119680), - "Mathematical Alphanumeric Symbols": range(119808, 120832), - "Sutton SignWriting": range(120832, 121520), - "Latin Extended-G": range(122624, 122880), - "Glagolitic Supplement": range(122880, 122928), - "Cyrillic Extended-D": range(122928, 123024), - "Nyiakeng Puachue Hmong": range(123136, 123216), - "Toto": range(123536, 123584), - "Wancho": range(123584, 123648), - "Nag Mundari": range(124112, 124160), - "Ethiopic Extended-B": range(124896, 124928), - "Mende Kikakui": range(124928, 125152), - "Adlam": range(125184, 125280), - "Indic Siyaq Numbers": range(126064, 126144), - "Ottoman Siyaq Numbers": range(126208, 126288), - "Arabic Mathematical Alphabetic Symbols": range(126464, 126720), - "Mahjong Tiles": range(126976, 127024), - "Domino Tiles": range(127024, 127136), - "Playing Cards": range(127136, 127232), - "Enclosed Alphanumeric Supplement": range(127232, 127488), - "Enclosed Ideographic Supplement": range(127488, 127744), - "Miscellaneous Symbols and Pictographs": range(127744, 128512), - "Emoticons range(Emoji)": range(128512, 128592), - "Ornamental Dingbats": range(128592, 128640), - "Transport and Map Symbols": range(128640, 128768), - "Alchemical Symbols": range(128768, 128896), - "Geometric Shapes Extended": range(128896, 129024), - "Supplemental Arrows-C": range(129024, 129280), - "Supplemental Symbols and Pictographs": range(129280, 129536), - "Chess Symbols": range(129536, 129648), - "Symbols and Pictographs Extended-A": range(129648, 129792), - "Symbols for Legacy Computing": range(129792, 130048), - "CJK Unified Ideographs Extension B": range(131072, 173792), - "CJK Unified Ideographs Extension C": range(173824, 177984), - "CJK Unified Ideographs Extension D": range(177984, 178208), - "CJK Unified Ideographs Extension E": range(178208, 183984), - "CJK Unified Ideographs Extension F": range(183984, 191472), - "CJK Compatibility Ideographs Supplement": range(194560, 195104), - "CJK Unified Ideographs Extension G": range(196608, 201552), - "CJK Unified Ideographs Extension H": range(201552, 205744), - "Tags": range(917504, 917632), - "Variation Selectors Supplement": range(917760, 918000), - "Supplementary Private Use Area-A": range(983040, 1048576), - "Supplementary Private Use Area-B": range(1048576, 1114112), -} - - -UNICODE_SECONDARY_RANGE_KEYWORD: List[str] = [ - "Supplement", - "Extended", - "Extensions", - "Modifier", - "Marks", - "Punctuation", - "Symbols", - "Forms", - "Operators", - "Miscellaneous", - "Drawing", - "Block", - "Shapes", - "Supplemental", - "Tags", -] - -RE_POSSIBLE_ENCODING_INDICATION = re_compile( - r"(?:(?:encoding)|(?:charset)|(?:coding))(?:[\:= ]{1,10})(?:[\"\']?)([a-zA-Z0-9\-_]+)(?:[\"\']?)", - IGNORECASE, -) - -IANA_NO_ALIASES = [ - "cp720", - "cp737", - "cp856", - "cp874", - "cp875", - "cp1006", - "koi8_r", - "koi8_t", - "koi8_u", -] - -IANA_SUPPORTED: List[str] = sorted( - filter( - lambda x: x.endswith("_codec") is False - and x not in {"rot_13", "tactis", "mbcs"}, - list(set(aliases.values())) + IANA_NO_ALIASES, - ) -) - -IANA_SUPPORTED_COUNT: int = len(IANA_SUPPORTED) - -# pre-computed code page that are similar using the function cp_similarity. -IANA_SUPPORTED_SIMILAR: Dict[str, List[str]] = { - "cp037": ["cp1026", "cp1140", "cp273", "cp500"], - "cp1026": ["cp037", "cp1140", "cp273", "cp500"], - "cp1125": ["cp866"], - "cp1140": ["cp037", "cp1026", "cp273", "cp500"], - "cp1250": ["iso8859_2"], - "cp1251": ["kz1048", "ptcp154"], - "cp1252": ["iso8859_15", "iso8859_9", "latin_1"], - "cp1253": ["iso8859_7"], - "cp1254": ["iso8859_15", "iso8859_9", "latin_1"], - "cp1257": ["iso8859_13"], - "cp273": ["cp037", "cp1026", "cp1140", "cp500"], - "cp437": ["cp850", "cp858", "cp860", "cp861", "cp862", "cp863", "cp865"], - "cp500": ["cp037", "cp1026", "cp1140", "cp273"], - "cp850": ["cp437", "cp857", "cp858", "cp865"], - "cp857": ["cp850", "cp858", "cp865"], - "cp858": ["cp437", "cp850", "cp857", "cp865"], - "cp860": ["cp437", "cp861", "cp862", "cp863", "cp865"], - "cp861": ["cp437", "cp860", "cp862", "cp863", "cp865"], - "cp862": ["cp437", "cp860", "cp861", "cp863", "cp865"], - "cp863": ["cp437", "cp860", "cp861", "cp862", "cp865"], - "cp865": ["cp437", "cp850", "cp857", "cp858", "cp860", "cp861", "cp862", "cp863"], - "cp866": ["cp1125"], - "iso8859_10": ["iso8859_14", "iso8859_15", "iso8859_4", "iso8859_9", "latin_1"], - "iso8859_11": ["tis_620"], - "iso8859_13": ["cp1257"], - "iso8859_14": [ - "iso8859_10", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_15": [ - "cp1252", - "cp1254", - "iso8859_10", - "iso8859_14", - "iso8859_16", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_16": [ - "iso8859_14", - "iso8859_15", - "iso8859_2", - "iso8859_3", - "iso8859_9", - "latin_1", - ], - "iso8859_2": ["cp1250", "iso8859_16", "iso8859_4"], - "iso8859_3": ["iso8859_14", "iso8859_15", "iso8859_16", "iso8859_9", "latin_1"], - "iso8859_4": ["iso8859_10", "iso8859_2", "iso8859_9", "latin_1"], - "iso8859_7": ["cp1253"], - "iso8859_9": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "latin_1", - ], - "kz1048": ["cp1251", "ptcp154"], - "latin_1": [ - "cp1252", - "cp1254", - "cp1258", - "iso8859_10", - "iso8859_14", - "iso8859_15", - "iso8859_16", - "iso8859_3", - "iso8859_4", - "iso8859_9", - ], - "mac_iceland": ["mac_roman", "mac_turkish"], - "mac_roman": ["mac_iceland", "mac_turkish"], - "mac_turkish": ["mac_iceland", "mac_roman"], - "ptcp154": ["cp1251", "kz1048"], - "tis_620": ["iso8859_11"], -} - - -CHARDET_CORRESPONDENCE: Dict[str, str] = { - "iso2022_kr": "ISO-2022-KR", - "iso2022_jp": "ISO-2022-JP", - "euc_kr": "EUC-KR", - "tis_620": "TIS-620", - "utf_32": "UTF-32", - "euc_jp": "EUC-JP", - "koi8_r": "KOI8-R", - "iso8859_1": "ISO-8859-1", - "iso8859_2": "ISO-8859-2", - "iso8859_5": "ISO-8859-5", - "iso8859_6": "ISO-8859-6", - "iso8859_7": "ISO-8859-7", - "iso8859_8": "ISO-8859-8", - "utf_16": "UTF-16", - "cp855": "IBM855", - "mac_cyrillic": "MacCyrillic", - "gb2312": "GB2312", - "gb18030": "GB18030", - "cp932": "CP932", - "cp866": "IBM866", - "utf_8": "utf-8", - "utf_8_sig": "UTF-8-SIG", - "shift_jis": "SHIFT_JIS", - "big5": "Big5", - "cp1250": "windows-1250", - "cp1251": "windows-1251", - "cp1252": "Windows-1252", - "cp1253": "windows-1253", - "cp1255": "windows-1255", - "cp1256": "windows-1256", - "cp1254": "Windows-1254", - "cp949": "CP949", -} - - -COMMON_SAFE_ASCII_CHARACTERS: Set[str] = { - "<", - ">", - "=", - ":", - "/", - "&", - ";", - "{", - "}", - "[", - "]", - ",", - "|", - '"', - "-", -} - - -KO_NAMES: Set[str] = {"johab", "cp949", "euc_kr"} -ZH_NAMES: Set[str] = {"big5", "cp950", "big5hkscs", "hz"} - -# Logging LEVEL below DEBUG -TRACE: int = 5 - - -# Language label that contain the em dash "—" -# character are to be considered alternative seq to origin -FREQUENCIES: Dict[str, List[str]] = { - "English": [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "u", - "m", - "f", - "p", - "g", - "w", - "y", - "b", - "v", - "k", - "x", - "j", - "z", - "q", - ], - "English—": [ - "e", - "a", - "t", - "i", - "o", - "n", - "s", - "r", - "h", - "l", - "d", - "c", - "m", - "u", - "f", - "p", - "g", - "w", - "b", - "y", - "v", - "k", - "j", - "x", - "z", - "q", - ], - "German": [ - "e", - "n", - "i", - "r", - "s", - "t", - "a", - "d", - "h", - "u", - "l", - "g", - "o", - "c", - "m", - "b", - "f", - "k", - "w", - "z", - "p", - "v", - "ü", - "ä", - "ö", - "j", - ], - "French": [ - "e", - "a", - "s", - "n", - "i", - "t", - "r", - "l", - "u", - "o", - "d", - "c", - "p", - "m", - "é", - "v", - "g", - "f", - "b", - "h", - "q", - "à", - "x", - "è", - "y", - "j", - ], - "Dutch": [ - "e", - "n", - "a", - "i", - "r", - "t", - "o", - "d", - "s", - "l", - "g", - "h", - "v", - "m", - "u", - "k", - "c", - "p", - "b", - "w", - "j", - "z", - "f", - "y", - "x", - "ë", - ], - "Italian": [ - "e", - "i", - "a", - "o", - "n", - "l", - "t", - "r", - "s", - "c", - "d", - "u", - "p", - "m", - "g", - "v", - "f", - "b", - "z", - "h", - "q", - "è", - "à", - "k", - "y", - "ò", - ], - "Polish": [ - "a", - "i", - "o", - "e", - "n", - "r", - "z", - "w", - "s", - "c", - "t", - "k", - "y", - "d", - "p", - "m", - "u", - "l", - "j", - "ł", - "g", - "b", - "h", - "ą", - "ę", - "ó", - ], - "Spanish": [ - "e", - "a", - "o", - "n", - "s", - "r", - "i", - "l", - "d", - "t", - "c", - "u", - "m", - "p", - "b", - "g", - "v", - "f", - "y", - "ó", - "h", - "q", - "í", - "j", - "z", - "á", - ], - "Russian": [ - "о", - "а", - "е", - "и", - "н", - "с", - "т", - "р", - "в", - "л", - "к", - "м", - "д", - "п", - "у", - "г", - "я", - "ы", - "з", - "б", - "й", - "ь", - "ч", - "х", - "ж", - "ц", - ], - # Jap-Kanji - "Japanese": [ - "人", - "一", - "大", - "亅", - "丁", - "丨", - "竹", - "笑", - "口", - "日", - "今", - "二", - "彳", - "行", - "十", - "土", - "丶", - "寸", - "寺", - "時", - "乙", - "丿", - "乂", - "气", - "気", - "冂", - "巾", - "亠", - "市", - "目", - "儿", - "見", - "八", - "小", - "凵", - "県", - "月", - "彐", - "門", - "間", - "木", - "東", - "山", - "出", - "本", - "中", - "刀", - "分", - "耳", - "又", - "取", - "最", - "言", - "田", - "心", - "思", - "刂", - "前", - "京", - "尹", - "事", - "生", - "厶", - "云", - "会", - "未", - "来", - "白", - "冫", - "楽", - "灬", - "馬", - "尸", - "尺", - "駅", - "明", - "耂", - "者", - "了", - "阝", - "都", - "高", - "卜", - "占", - "厂", - "广", - "店", - "子", - "申", - "奄", - "亻", - "俺", - "上", - "方", - "冖", - "学", - "衣", - "艮", - "食", - "自", - ], - # Jap-Katakana - "Japanese—": [ - "ー", - "ン", - "ス", - "・", - "ル", - "ト", - "リ", - "イ", - "ア", - "ラ", - "ッ", - "ク", - "ド", - "シ", - "レ", - "ジ", - "タ", - "フ", - "ロ", - "カ", - "テ", - "マ", - "ィ", - "グ", - "バ", - "ム", - "プ", - "オ", - "コ", - "デ", - "ニ", - "ウ", - "メ", - "サ", - "ビ", - "ナ", - "ブ", - "ャ", - "エ", - "ュ", - "チ", - "キ", - "ズ", - "ダ", - "パ", - "ミ", - "ェ", - "ョ", - "ハ", - "セ", - "ベ", - "ガ", - "モ", - "ツ", - "ネ", - "ボ", - "ソ", - "ノ", - "ァ", - "ヴ", - "ワ", - "ポ", - "ペ", - "ピ", - "ケ", - "ゴ", - "ギ", - "ザ", - "ホ", - "ゲ", - "ォ", - "ヤ", - "ヒ", - "ユ", - "ヨ", - "ヘ", - "ゼ", - "ヌ", - "ゥ", - "ゾ", - "ヶ", - "ヂ", - "ヲ", - "ヅ", - "ヵ", - "ヱ", - "ヰ", - "ヮ", - "ヽ", - "゠", - "ヾ", - "ヷ", - "ヿ", - "ヸ", - "ヹ", - "ヺ", - ], - # Jap-Hiragana - "Japanese——": [ - "の", - "に", - "る", - "た", - "と", - "は", - "し", - "い", - "を", - "で", - "て", - "が", - "な", - "れ", - "か", - "ら", - "さ", - "っ", - "り", - "す", - "あ", - "も", - "こ", - "ま", - "う", - "く", - "よ", - "き", - "ん", - "め", - "お", - "け", - "そ", - "つ", - "だ", - "や", - "え", - "ど", - "わ", - "ち", - "み", - "せ", - "じ", - "ば", - "へ", - "び", - "ず", - "ろ", - "ほ", - "げ", - "む", - "べ", - "ひ", - "ょ", - "ゆ", - "ぶ", - "ご", - "ゃ", - "ね", - "ふ", - "ぐ", - "ぎ", - "ぼ", - "ゅ", - "づ", - "ざ", - "ぞ", - "ぬ", - "ぜ", - "ぱ", - "ぽ", - "ぷ", - "ぴ", - "ぃ", - "ぁ", - "ぇ", - "ぺ", - "ゞ", - "ぢ", - "ぉ", - "ぅ", - "ゐ", - "ゝ", - "ゑ", - "゛", - "゜", - "ゎ", - "ゔ", - "゚", - "ゟ", - "゙", - "ゕ", - "ゖ", - ], - "Portuguese": [ - "a", - "e", - "o", - "s", - "i", - "r", - "d", - "n", - "t", - "m", - "u", - "c", - "l", - "p", - "g", - "v", - "b", - "f", - "h", - "ã", - "q", - "é", - "ç", - "á", - "z", - "í", - ], - "Swedish": [ - "e", - "a", - "n", - "r", - "t", - "s", - "i", - "l", - "d", - "o", - "m", - "k", - "g", - "v", - "h", - "f", - "u", - "p", - "ä", - "c", - "b", - "ö", - "å", - "y", - "j", - "x", - ], - "Chinese": [ - "的", - "一", - "是", - "不", - "了", - "在", - "人", - "有", - "我", - "他", - "这", - "个", - "们", - "中", - "来", - "上", - "大", - "为", - "和", - "国", - "地", - "到", - "以", - "说", - "时", - "要", - "就", - "出", - "会", - "可", - "也", - "你", - "对", - "生", - "能", - "而", - "子", - "那", - "得", - "于", - "着", - "下", - "自", - "之", - "年", - "过", - "发", - "后", - "作", - "里", - "用", - "道", - "行", - "所", - "然", - "家", - "种", - "事", - "成", - "方", - "多", - "经", - "么", - "去", - "法", - "学", - "如", - "都", - "同", - "现", - "当", - "没", - "动", - "面", - "起", - "看", - "定", - "天", - "分", - "还", - "进", - "好", - "小", - "部", - "其", - "些", - "主", - "样", - "理", - "心", - "她", - "本", - "前", - "开", - "但", - "因", - "只", - "从", - "想", - "实", - ], - "Ukrainian": [ - "о", - "а", - "н", - "і", - "и", - "р", - "в", - "т", - "е", - "с", - "к", - "л", - "у", - "д", - "м", - "п", - "з", - "я", - "ь", - "б", - "г", - "й", - "ч", - "х", - "ц", - "ї", - ], - "Norwegian": [ - "e", - "r", - "n", - "t", - "a", - "s", - "i", - "o", - "l", - "d", - "g", - "k", - "m", - "v", - "f", - "p", - "u", - "b", - "h", - "å", - "y", - "j", - "ø", - "c", - "æ", - "w", - ], - "Finnish": [ - "a", - "i", - "n", - "t", - "e", - "s", - "l", - "o", - "u", - "k", - "ä", - "m", - "r", - "v", - "j", - "h", - "p", - "y", - "d", - "ö", - "g", - "c", - "b", - "f", - "w", - "z", - ], - "Vietnamese": [ - "n", - "h", - "t", - "i", - "c", - "g", - "a", - "o", - "u", - "m", - "l", - "r", - "à", - "đ", - "s", - "e", - "v", - "p", - "b", - "y", - "ư", - "d", - "á", - "k", - "ộ", - "ế", - ], - "Czech": [ - "o", - "e", - "a", - "n", - "t", - "s", - "i", - "l", - "v", - "r", - "k", - "d", - "u", - "m", - "p", - "í", - "c", - "h", - "z", - "á", - "y", - "j", - "b", - "ě", - "é", - "ř", - ], - "Hungarian": [ - "e", - "a", - "t", - "l", - "s", - "n", - "k", - "r", - "i", - "o", - "z", - "á", - "é", - "g", - "m", - "b", - "y", - "v", - "d", - "h", - "u", - "p", - "j", - "ö", - "f", - "c", - ], - "Korean": [ - "이", - "다", - "에", - "의", - "는", - "로", - "하", - "을", - "가", - "고", - "지", - "서", - "한", - "은", - "기", - "으", - "년", - "대", - "사", - "시", - "를", - "리", - "도", - "인", - "스", - "일", - ], - "Indonesian": [ - "a", - "n", - "e", - "i", - "r", - "t", - "u", - "s", - "d", - "k", - "m", - "l", - "g", - "p", - "b", - "o", - "h", - "y", - "j", - "c", - "w", - "f", - "v", - "z", - "x", - "q", - ], - "Turkish": [ - "a", - "e", - "i", - "n", - "r", - "l", - "ı", - "k", - "d", - "t", - "s", - "m", - "y", - "u", - "o", - "b", - "ü", - "ş", - "v", - "g", - "z", - "h", - "c", - "p", - "ç", - "ğ", - ], - "Romanian": [ - "e", - "i", - "a", - "r", - "n", - "t", - "u", - "l", - "o", - "c", - "s", - "d", - "p", - "m", - "ă", - "f", - "v", - "î", - "g", - "b", - "ș", - "ț", - "z", - "h", - "â", - "j", - ], - "Farsi": [ - "ا", - "ی", - "ر", - "د", - "ن", - "ه", - "و", - "م", - "ت", - "ب", - "س", - "ل", - "ک", - "ش", - "ز", - "ف", - "گ", - "ع", - "خ", - "ق", - "ج", - "آ", - "پ", - "ح", - "ط", - "ص", - ], - "Arabic": [ - "ا", - "ل", - "ي", - "م", - "و", - "ن", - "ر", - "ت", - "ب", - "ة", - "ع", - "د", - "س", - "ف", - "ه", - "ك", - "ق", - "أ", - "ح", - "ج", - "ش", - "ط", - "ص", - "ى", - "خ", - "إ", - ], - "Danish": [ - "e", - "r", - "n", - "t", - "a", - "i", - "s", - "d", - "l", - "o", - "g", - "m", - "k", - "f", - "v", - "u", - "b", - "h", - "p", - "å", - "y", - "ø", - "æ", - "c", - "j", - "w", - ], - "Serbian": [ - "а", - "и", - "о", - "е", - "н", - "р", - "с", - "у", - "т", - "к", - "ј", - "в", - "д", - "м", - "п", - "л", - "г", - "з", - "б", - "a", - "i", - "e", - "o", - "n", - "ц", - "ш", - ], - "Lithuanian": [ - "i", - "a", - "s", - "o", - "r", - "e", - "t", - "n", - "u", - "k", - "m", - "l", - "p", - "v", - "d", - "j", - "g", - "ė", - "b", - "y", - "ų", - "š", - "ž", - "c", - "ą", - "į", - ], - "Slovene": [ - "e", - "a", - "i", - "o", - "n", - "r", - "s", - "l", - "t", - "j", - "v", - "k", - "d", - "p", - "m", - "u", - "z", - "b", - "g", - "h", - "č", - "c", - "š", - "ž", - "f", - "y", - ], - "Slovak": [ - "o", - "a", - "e", - "n", - "i", - "r", - "v", - "t", - "s", - "l", - "k", - "d", - "m", - "p", - "u", - "c", - "h", - "j", - "b", - "z", - "á", - "y", - "ý", - "í", - "č", - "é", - ], - "Hebrew": [ - "י", - "ו", - "ה", - "ל", - "ר", - "ב", - "ת", - "מ", - "א", - "ש", - "נ", - "ע", - "ם", - "ד", - "ק", - "ח", - "פ", - "ס", - "כ", - "ג", - "ט", - "צ", - "ן", - "ז", - "ך", - ], - "Bulgarian": [ - "а", - "и", - "о", - "е", - "н", - "т", - "р", - "с", - "в", - "л", - "к", - "д", - "п", - "м", - "з", - "г", - "я", - "ъ", - "у", - "б", - "ч", - "ц", - "й", - "ж", - "щ", - "х", - ], - "Croatian": [ - "a", - "i", - "o", - "e", - "n", - "r", - "j", - "s", - "t", - "u", - "k", - "l", - "v", - "d", - "m", - "p", - "g", - "z", - "b", - "c", - "č", - "h", - "š", - "ž", - "ć", - "f", - ], - "Hindi": [ - "क", - "र", - "स", - "न", - "त", - "म", - "ह", - "प", - "य", - "ल", - "व", - "ज", - "द", - "ग", - "ब", - "श", - "ट", - "अ", - "ए", - "थ", - "भ", - "ड", - "च", - "ध", - "ष", - "इ", - ], - "Estonian": [ - "a", - "i", - "e", - "s", - "t", - "l", - "u", - "n", - "o", - "k", - "r", - "d", - "m", - "v", - "g", - "p", - "j", - "h", - "ä", - "b", - "õ", - "ü", - "f", - "c", - "ö", - "y", - ], - "Thai": [ - "า", - "น", - "ร", - "อ", - "ก", - "เ", - "ง", - "ม", - "ย", - "ล", - "ว", - "ด", - "ท", - "ส", - "ต", - "ะ", - "ป", - "บ", - "ค", - "ห", - "แ", - "จ", - "พ", - "ช", - "ข", - "ใ", - ], - "Greek": [ - "α", - "τ", - "ο", - "ι", - "ε", - "ν", - "ρ", - "σ", - "κ", - "η", - "π", - "ς", - "υ", - "μ", - "λ", - "ί", - "ό", - "ά", - "γ", - "έ", - "δ", - "ή", - "ω", - "χ", - "θ", - "ύ", - ], - "Tamil": [ - "க", - "த", - "ப", - "ட", - "ர", - "ம", - "ல", - "ன", - "வ", - "ற", - "ய", - "ள", - "ச", - "ந", - "இ", - "ண", - "அ", - "ஆ", - "ழ", - "ங", - "எ", - "உ", - "ஒ", - "ஸ", - ], - "Kazakh": [ - "а", - "ы", - "е", - "н", - "т", - "р", - "л", - "і", - "д", - "с", - "м", - "қ", - "к", - "о", - "б", - "и", - "у", - "ғ", - "ж", - "ң", - "з", - "ш", - "й", - "п", - "г", - "ө", - ], -} - -LANGUAGE_SUPPORTED_COUNT: int = len(FREQUENCIES) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/legacy.py b/.venv/lib/python3.12/site-packages/charset_normalizer/legacy.py deleted file mode 100644 index 43aad21..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/legacy.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Any, Dict, Optional, Union -from warnings import warn - -from .api import from_bytes -from .constant import CHARDET_CORRESPONDENCE - - -def detect( - byte_str: bytes, should_rename_legacy: bool = False, **kwargs: Any -) -> Dict[str, Optional[Union[str, float]]]: - """ - chardet legacy method - Detect the encoding of the given byte string. It should be mostly backward-compatible. - Encoding name will match Chardet own writing whenever possible. (Not on encoding name unsupported by it) - This function is deprecated and should be used to migrate your project easily, consult the documentation for - further information. Not planned for removal. - - :param byte_str: The byte sequence to examine. - :param should_rename_legacy: Should we rename legacy encodings - to their more modern equivalents? - """ - if len(kwargs): - warn( - f"charset-normalizer disregard arguments '{','.join(list(kwargs.keys()))}' in legacy function detect()" - ) - - if not isinstance(byte_str, (bytearray, bytes)): - raise TypeError( # pragma: nocover - "Expected object of type bytes or bytearray, got: " - "{0}".format(type(byte_str)) - ) - - if isinstance(byte_str, bytearray): - byte_str = bytes(byte_str) - - r = from_bytes(byte_str).best() - - encoding = r.encoding if r is not None else None - language = r.language if r is not None and r.language != "Unknown" else "" - confidence = 1.0 - r.chaos if r is not None else None - - # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process - # but chardet does return 'utf-8-sig' and it is a valid codec name. - if r is not None and encoding == "utf_8" and r.bom: - encoding += "_sig" - - if should_rename_legacy is False and encoding in CHARDET_CORRESPONDENCE: - encoding = CHARDET_CORRESPONDENCE[encoding] - - return { - "encoding": encoding, - "language": language, - "confidence": confidence, - } diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/charset_normalizer/md.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index 3d0b46ae77baa966a03418d61fbd1da9d141366e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16064 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 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/md.py b/.venv/lib/python3.12/site-packages/charset_normalizer/md.py deleted file mode 100644 index 77897aa..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/md.py +++ /dev/null @@ -1,615 +0,0 @@ -from functools import lru_cache -from logging import getLogger -from typing import List, Optional - -from .constant import ( - COMMON_SAFE_ASCII_CHARACTERS, - TRACE, - UNICODE_SECONDARY_RANGE_KEYWORD, -) -from .utils import ( - is_accentuated, - is_arabic, - is_arabic_isolated_form, - is_case_variable, - is_cjk, - is_emoticon, - is_hangul, - is_hiragana, - is_katakana, - is_latin, - is_punctuation, - is_separator, - is_symbol, - is_thai, - is_unprintable, - remove_accent, - unicode_range, -) - - -class MessDetectorPlugin: - """ - Base abstract class used for mess detection plugins. - All detectors MUST extend and implement given methods. - """ - - def eligible(self, character: str) -> bool: - """ - Determine if given character should be fed in. - """ - raise NotImplementedError # pragma: nocover - - def feed(self, character: str) -> None: - """ - The main routine to be executed upon character. - Insert the logic in witch the text would be considered chaotic. - """ - raise NotImplementedError # pragma: nocover - - def reset(self) -> None: # pragma: no cover - """ - Permit to reset the plugin to the initial state. - """ - raise NotImplementedError - - @property - def ratio(self) -> float: - """ - Compute the chaos ratio based on what your feed() has seen. - Must NOT be lower than 0.; No restriction gt 0. - """ - raise NotImplementedError # pragma: nocover - - -class TooManySymbolOrPunctuationPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._punctuation_count: int = 0 - self._symbol_count: int = 0 - self._character_count: int = 0 - - self._last_printable_char: Optional[str] = None - self._frenzy_symbol_in_word: bool = False - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character != self._last_printable_char - and character not in COMMON_SAFE_ASCII_CHARACTERS - ): - if is_punctuation(character): - self._punctuation_count += 1 - elif ( - character.isdigit() is False - and is_symbol(character) - and is_emoticon(character) is False - ): - self._symbol_count += 2 - - self._last_printable_char = character - - def reset(self) -> None: # pragma: no cover - self._punctuation_count = 0 - self._character_count = 0 - self._symbol_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - ratio_of_punctuation: float = ( - self._punctuation_count + self._symbol_count - ) / self._character_count - - return ratio_of_punctuation if ratio_of_punctuation >= 0.3 else 0.0 - - -class TooManyAccentuatedPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count: int = 0 - self._accentuated_count: int = 0 - - def eligible(self, character: str) -> bool: - return character.isalpha() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_accentuated(character): - self._accentuated_count += 1 - - def reset(self) -> None: # pragma: no cover - self._character_count = 0 - self._accentuated_count = 0 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - ratio_of_accentuation: float = self._accentuated_count / self._character_count - return ratio_of_accentuation if ratio_of_accentuation >= 0.35 else 0.0 - - -class UnprintablePlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._unprintable_count: int = 0 - self._character_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if is_unprintable(character): - self._unprintable_count += 1 - self._character_count += 1 - - def reset(self) -> None: # pragma: no cover - self._unprintable_count = 0 - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._unprintable_count * 8) / self._character_count - - -class SuspiciousDuplicateAccentPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._successive_count: int = 0 - self._character_count: int = 0 - - self._last_latin_character: Optional[str] = None - - def eligible(self, character: str) -> bool: - return character.isalpha() and is_latin(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - if ( - self._last_latin_character is not None - and is_accentuated(character) - and is_accentuated(self._last_latin_character) - ): - if character.isupper() and self._last_latin_character.isupper(): - self._successive_count += 1 - # Worse if its the same char duplicated with different accent. - if remove_accent(character) == remove_accent(self._last_latin_character): - self._successive_count += 1 - self._last_latin_character = character - - def reset(self) -> None: # pragma: no cover - self._successive_count = 0 - self._character_count = 0 - self._last_latin_character = None - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return (self._successive_count * 2) / self._character_count - - -class SuspiciousRange(MessDetectorPlugin): - def __init__(self) -> None: - self._suspicious_successive_range_count: int = 0 - self._character_count: int = 0 - self._last_printable_seen: Optional[str] = None - - def eligible(self, character: str) -> bool: - return character.isprintable() - - def feed(self, character: str) -> None: - self._character_count += 1 - - if ( - character.isspace() - or is_punctuation(character) - or character in COMMON_SAFE_ASCII_CHARACTERS - ): - self._last_printable_seen = None - return - - if self._last_printable_seen is None: - self._last_printable_seen = character - return - - unicode_range_a: Optional[str] = unicode_range(self._last_printable_seen) - unicode_range_b: Optional[str] = unicode_range(character) - - if is_suspiciously_successive_range(unicode_range_a, unicode_range_b): - self._suspicious_successive_range_count += 1 - - self._last_printable_seen = character - - def reset(self) -> None: # pragma: no cover - self._character_count = 0 - self._suspicious_successive_range_count = 0 - self._last_printable_seen = None - - @property - def ratio(self) -> float: - if self._character_count <= 24: - return 0.0 - - ratio_of_suspicious_range_usage: float = ( - self._suspicious_successive_range_count * 2 - ) / self._character_count - - return ratio_of_suspicious_range_usage - - -class SuperWeirdWordPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._word_count: int = 0 - self._bad_word_count: int = 0 - self._foreign_long_count: int = 0 - - self._is_current_word_bad: bool = False - self._foreign_long_watch: bool = False - - self._character_count: int = 0 - self._bad_character_count: int = 0 - - self._buffer: str = "" - self._buffer_accent_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if character.isalpha(): - self._buffer += character - if is_accentuated(character): - self._buffer_accent_count += 1 - if ( - self._foreign_long_watch is False - and (is_latin(character) is False or is_accentuated(character)) - and is_cjk(character) is False - and is_hangul(character) is False - and is_katakana(character) is False - and is_hiragana(character) is False - and is_thai(character) is False - ): - self._foreign_long_watch = True - return - if not self._buffer: - return - if ( - character.isspace() or is_punctuation(character) or is_separator(character) - ) and self._buffer: - self._word_count += 1 - buffer_length: int = len(self._buffer) - - self._character_count += buffer_length - - if buffer_length >= 4: - if self._buffer_accent_count / buffer_length > 0.34: - self._is_current_word_bad = True - # Word/Buffer ending with an upper case accentuated letter are so rare, - # that we will consider them all as suspicious. Same weight as foreign_long suspicious. - if ( - is_accentuated(self._buffer[-1]) - and self._buffer[-1].isupper() - and all(_.isupper() for _ in self._buffer) is False - ): - self._foreign_long_count += 1 - self._is_current_word_bad = True - if buffer_length >= 24 and self._foreign_long_watch: - camel_case_dst = [ - i - for c, i in zip(self._buffer, range(0, buffer_length)) - if c.isupper() - ] - probable_camel_cased: bool = False - - if camel_case_dst and (len(camel_case_dst) / buffer_length <= 0.3): - probable_camel_cased = True - - if not probable_camel_cased: - self._foreign_long_count += 1 - self._is_current_word_bad = True - - if self._is_current_word_bad: - self._bad_word_count += 1 - self._bad_character_count += len(self._buffer) - self._is_current_word_bad = False - - self._foreign_long_watch = False - self._buffer = "" - self._buffer_accent_count = 0 - elif ( - character not in {"<", ">", "-", "=", "~", "|", "_"} - and character.isdigit() is False - and is_symbol(character) - ): - self._is_current_word_bad = True - self._buffer += character - - def reset(self) -> None: # pragma: no cover - self._buffer = "" - self._is_current_word_bad = False - self._foreign_long_watch = False - self._bad_word_count = 0 - self._word_count = 0 - self._character_count = 0 - self._bad_character_count = 0 - self._foreign_long_count = 0 - - @property - def ratio(self) -> float: - if self._word_count <= 10 and self._foreign_long_count == 0: - return 0.0 - - return self._bad_character_count / self._character_count - - -class CjkInvalidStopPlugin(MessDetectorPlugin): - """ - GB(Chinese) based encoding often render the stop incorrectly when the content does not fit and - can be easily detected. Searching for the overuse of '丅' and '丄'. - """ - - def __init__(self) -> None: - self._wrong_stop_count: int = 0 - self._cjk_character_count: int = 0 - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - if character in {"丅", "丄"}: - self._wrong_stop_count += 1 - return - if is_cjk(character): - self._cjk_character_count += 1 - - def reset(self) -> None: # pragma: no cover - self._wrong_stop_count = 0 - self._cjk_character_count = 0 - - @property - def ratio(self) -> float: - if self._cjk_character_count < 16: - return 0.0 - return self._wrong_stop_count / self._cjk_character_count - - -class ArchaicUpperLowerPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._buf: bool = False - - self._character_count_since_last_sep: int = 0 - - self._successive_upper_lower_count: int = 0 - self._successive_upper_lower_count_final: int = 0 - - self._character_count: int = 0 - - self._last_alpha_seen: Optional[str] = None - self._current_ascii_only: bool = True - - def eligible(self, character: str) -> bool: - return True - - def feed(self, character: str) -> None: - is_concerned = character.isalpha() and is_case_variable(character) - chunk_sep = is_concerned is False - - if chunk_sep and self._character_count_since_last_sep > 0: - if ( - self._character_count_since_last_sep <= 64 - and character.isdigit() is False - and self._current_ascii_only is False - ): - self._successive_upper_lower_count_final += ( - self._successive_upper_lower_count - ) - - self._successive_upper_lower_count = 0 - self._character_count_since_last_sep = 0 - self._last_alpha_seen = None - self._buf = False - self._character_count += 1 - self._current_ascii_only = True - - return - - if self._current_ascii_only is True and character.isascii() is False: - self._current_ascii_only = False - - if self._last_alpha_seen is not None: - if (character.isupper() and self._last_alpha_seen.islower()) or ( - character.islower() and self._last_alpha_seen.isupper() - ): - if self._buf is True: - self._successive_upper_lower_count += 2 - self._buf = False - else: - self._buf = True - else: - self._buf = False - - self._character_count += 1 - self._character_count_since_last_sep += 1 - self._last_alpha_seen = character - - def reset(self) -> None: # pragma: no cover - self._character_count = 0 - self._character_count_since_last_sep = 0 - self._successive_upper_lower_count = 0 - self._successive_upper_lower_count_final = 0 - self._last_alpha_seen = None - self._buf = False - self._current_ascii_only = True - - @property - def ratio(self) -> float: - if self._character_count == 0: - return 0.0 - - return self._successive_upper_lower_count_final / self._character_count - - -class ArabicIsolatedFormPlugin(MessDetectorPlugin): - def __init__(self) -> None: - self._character_count: int = 0 - self._isolated_form_count: int = 0 - - def reset(self) -> None: # pragma: no cover - self._character_count = 0 - self._isolated_form_count = 0 - - def eligible(self, character: str) -> bool: - return is_arabic(character) - - def feed(self, character: str) -> None: - self._character_count += 1 - - if is_arabic_isolated_form(character): - self._isolated_form_count += 1 - - @property - def ratio(self) -> float: - if self._character_count < 8: - return 0.0 - - isolated_form_usage: float = self._isolated_form_count / self._character_count - - return isolated_form_usage - - -@lru_cache(maxsize=1024) -def is_suspiciously_successive_range( - unicode_range_a: Optional[str], unicode_range_b: Optional[str] -) -> bool: - """ - Determine if two Unicode range seen next to each other can be considered as suspicious. - """ - if unicode_range_a is None or unicode_range_b is None: - return True - - if unicode_range_a == unicode_range_b: - return False - - if "Latin" in unicode_range_a and "Latin" in unicode_range_b: - return False - - if "Emoticons" in unicode_range_a or "Emoticons" in unicode_range_b: - return False - - # Latin characters can be accompanied with a combining diacritical mark - # eg. Vietnamese. - if ("Latin" in unicode_range_a or "Latin" in unicode_range_b) and ( - "Combining" in unicode_range_a or "Combining" in unicode_range_b - ): - return False - - keywords_range_a, keywords_range_b = unicode_range_a.split( - " " - ), unicode_range_b.split(" ") - - for el in keywords_range_a: - if el in UNICODE_SECONDARY_RANGE_KEYWORD: - continue - if el in keywords_range_b: - return False - - # Japanese Exception - range_a_jp_chars, range_b_jp_chars = ( - unicode_range_a - in ( - "Hiragana", - "Katakana", - ), - unicode_range_b in ("Hiragana", "Katakana"), - ) - if (range_a_jp_chars or range_b_jp_chars) and ( - "CJK" in unicode_range_a or "CJK" in unicode_range_b - ): - return False - if range_a_jp_chars and range_b_jp_chars: - return False - - if "Hangul" in unicode_range_a or "Hangul" in unicode_range_b: - if "CJK" in unicode_range_a or "CJK" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - # Chinese/Japanese use dedicated range for punctuation and/or separators. - if ("CJK" in unicode_range_a or "CJK" in unicode_range_b) or ( - unicode_range_a in ["Katakana", "Hiragana"] - and unicode_range_b in ["Katakana", "Hiragana"] - ): - if "Punctuation" in unicode_range_a or "Punctuation" in unicode_range_b: - return False - if "Forms" in unicode_range_a or "Forms" in unicode_range_b: - return False - if unicode_range_a == "Basic Latin" or unicode_range_b == "Basic Latin": - return False - - return True - - -@lru_cache(maxsize=2048) -def mess_ratio( - decoded_sequence: str, maximum_threshold: float = 0.2, debug: bool = False -) -> float: - """ - Compute a mess ratio given a decoded bytes sequence. The maximum threshold does stop the computation earlier. - """ - - detectors: List[MessDetectorPlugin] = [ - md_class() for md_class in MessDetectorPlugin.__subclasses__() - ] - - length: int = len(decoded_sequence) + 1 - - mean_mess_ratio: float = 0.0 - - if length < 512: - intermediary_mean_mess_ratio_calc: int = 32 - elif length <= 1024: - intermediary_mean_mess_ratio_calc = 64 - else: - intermediary_mean_mess_ratio_calc = 128 - - for character, index in zip(decoded_sequence + "\n", range(length)): - for detector in detectors: - if detector.eligible(character): - detector.feed(character) - - if ( - index > 0 and index % intermediary_mean_mess_ratio_calc == 0 - ) or index == length - 1: - mean_mess_ratio = sum(dt.ratio for dt in detectors) - - if mean_mess_ratio >= maximum_threshold: - break - - if debug: - logger = getLogger("charset_normalizer") - - logger.log( - TRACE, - "Mess-detector extended-analysis start. " - f"intermediary_mean_mess_ratio_calc={intermediary_mean_mess_ratio_calc} mean_mess_ratio={mean_mess_ratio} " - f"maximum_threshold={maximum_threshold}", - ) - - if len(decoded_sequence) > 16: - logger.log(TRACE, f"Starting with: {decoded_sequence[:16]}") - logger.log(TRACE, f"Ending with: {decoded_sequence[-16::]}") - - for dt in detectors: # pragma: nocover - logger.log(TRACE, f"{dt.__class__}: {dt.ratio}") - - return round(mean_mess_ratio, 3) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so deleted file mode 100755 index e3044d9abd70dd87a4a1262fa53e3fb0b434d993..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 272640 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

Ude%8yZ^!Z|lc9jdo}U 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

GL>E3U8;wWJxulxD51)p=PryE4$P% zs%0}?ob3#fGjRy%k-xeJvTSh;3lozVly=?)q_TonL@g* ze2n|#N#`C9YD2hsvuy^cQJD1JXeTD%k|kwid`C9q{Z%UrPJG_Xl<`Q{lBSvQH+vHy zdzv(c34=Y~8e@g%oWohrO8CC7xI|@RpFX9um+Hz-(a*ut)w!e;PL^)%%P~%33u9S~ z{z6sacdSS6KFW3nWFWWcU}HJ7Ub-!_{xGAb|(*%V&V7?W*&Jr!iBkayi{#PnSt<)&VvkE&*PnX6}*e$J~O%<^aHupDPmdL74; zcKQjQxbT5%>4gvaFXMOST2Gb>xJ|!oR}rUG{w5_k^}duyXxGe~BQRfi$FbbW%`{Zg z?TxKT+Lr0zLPMgtO(Y~wUgovSsQS+pnPJ(jZ_#Z*&z#j%uMszDs{7SFg388N4=xGw zWK8)(U61T(aqzI)32Gzf{C;Vd@;ue27nm&mwhvidp{u)7jO7MiE5efq(}8K*7r9 z1lz2qMGRsQY$9TDC_*|tbB7nmUCvyD1RD#BU=?g^QwBQ=t%F$Dd8CjuLa?y1v9Yi) ze(&d-ot@lCaAEJw%+5D6JM-qv-@CU_&Wf}G>svWdIeKWLT4#9Oaf|{vANiq_He6;a zO}oxW7U70~3VP8cL}e&%nF3B(x22q7+r_>G@h=r6EiOWXv!g`x;UiAImB);TG9l<1r6lqdKS5xlYZ?nsLdI z%3|)fr7I>sE0XpUK0>8Qg<)QHWx6tli~Ib_+=!whhKlbBS7_R{$S=zxt>kB=3D4`4 zVPq>%ocq3_?h8w?4j*!2AkuPUGTK1J;V4BH)+#v;j}(%IH2#rCco`X}NZIaVPY!&j zg(~?@(nQHdv9Z2}^+)s~&P7mUV>FssflahzT!NB^R*NxJ_CB$fLlL&UlBu!) z!6}33BV`M{vf{7;i>lnA7_4_;Z4GpF#b}2voWZw?$GxY8tnyn^i>;u(j+|c=t@R#| zMlq7=6Pclkl3wb0fM+Z1rKSMf)yD+ASQJ@YtjbcW5Uoo*TZXBbW+>VnSFi81fThn6 zrjl<6x&On+m;O{@(7l1pC+s&y4|NKg% zm$G!-++_?c;QDoNuuQyvHXxAJPgv9mN6@}36Ts(t!b#l3Z=SLzq+77VDVO9cmJ5le zy~bIDN!rBAcO2v*{W4{K{+jhe`a*zN+;EVT|5(?ak10cZ+^4*q*vcnTZ}lS`Xa-Bd~G#J3d^gN z%bPQXy@X40VEuxtiU{N54qAAptjCw|}Y`;OoC>O>U1YEDT$!y&v! z{Kjo_ZCp1x^yX*1vE1Z?Jz&%P=kFM9{)W+QBg`)HKhyb|;X}g#vqo=a3`891_Y6<_ z=?~p5`u23pS{=2jH~fHK(qb`dv{U=sLORUIaW$tTKjDz~uQR~uq~kAV;a;7Jkw@as znn)2dj&IymZxA_f50P0M%3rA-mfd37@5Zi$K7$?E^kV)DI}67s{KQ3T@!yQ-G{^r1 DNR#cw diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/models.py b/.venv/lib/python3.12/site-packages/charset_normalizer/models.py deleted file mode 100644 index a760b9c..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/models.py +++ /dev/null @@ -1,340 +0,0 @@ -from encodings.aliases import aliases -from hashlib import sha256 -from json import dumps -from typing import Any, Dict, Iterator, List, Optional, Tuple, Union - -from .constant import TOO_BIG_SEQUENCE -from .utils import iana_name, is_multi_byte_encoding, unicode_range - - -class CharsetMatch: - def __init__( - self, - payload: bytes, - guessed_encoding: str, - mean_mess_ratio: float, - has_sig_or_bom: bool, - languages: "CoherenceMatches", - decoded_payload: Optional[str] = None, - ): - self._payload: bytes = payload - - self._encoding: str = guessed_encoding - self._mean_mess_ratio: float = mean_mess_ratio - self._languages: CoherenceMatches = languages - self._has_sig_or_bom: bool = has_sig_or_bom - self._unicode_ranges: Optional[List[str]] = None - - self._leaves: List[CharsetMatch] = [] - self._mean_coherence_ratio: float = 0.0 - - self._output_payload: Optional[bytes] = None - self._output_encoding: Optional[str] = None - - self._string: Optional[str] = decoded_payload - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CharsetMatch): - raise TypeError( - "__eq__ cannot be invoked on {} and {}.".format( - str(other.__class__), str(self.__class__) - ) - ) - return self.encoding == other.encoding and self.fingerprint == other.fingerprint - - def __lt__(self, other: object) -> bool: - """ - Implemented to make sorted available upon CharsetMatches items. - """ - if not isinstance(other, CharsetMatch): - raise ValueError - - chaos_difference: float = abs(self.chaos - other.chaos) - coherence_difference: float = abs(self.coherence - other.coherence) - - # Below 1% difference --> Use Coherence - if chaos_difference < 0.01 and coherence_difference > 0.02: - return self.coherence > other.coherence - elif chaos_difference < 0.01 and coherence_difference <= 0.02: - # When having a difficult decision, use the result that decoded as many multi-byte as possible. - # preserve RAM usage! - if len(self._payload) >= TOO_BIG_SEQUENCE: - return self.chaos < other.chaos - return self.multi_byte_usage > other.multi_byte_usage - - return self.chaos < other.chaos - - @property - def multi_byte_usage(self) -> float: - return 1.0 - (len(str(self)) / len(self.raw)) - - def __str__(self) -> str: - # Lazy Str Loading - if self._string is None: - self._string = str(self._payload, self._encoding, "strict") - return self._string - - def __repr__(self) -> str: - return "".format(self.encoding, self.fingerprint) - - def add_submatch(self, other: "CharsetMatch") -> None: - if not isinstance(other, CharsetMatch) or other == self: - raise ValueError( - "Unable to add instance <{}> as a submatch of a CharsetMatch".format( - other.__class__ - ) - ) - - other._string = None # Unload RAM usage; dirty trick. - self._leaves.append(other) - - @property - def encoding(self) -> str: - return self._encoding - - @property - def encoding_aliases(self) -> List[str]: - """ - Encoding name are known by many name, using this could help when searching for IBM855 when it's listed as CP855. - """ - also_known_as: List[str] = [] - for u, p in aliases.items(): - if self.encoding == u: - also_known_as.append(p) - elif self.encoding == p: - also_known_as.append(u) - return also_known_as - - @property - def bom(self) -> bool: - return self._has_sig_or_bom - - @property - def byte_order_mark(self) -> bool: - return self._has_sig_or_bom - - @property - def languages(self) -> List[str]: - """ - Return the complete list of possible languages found in decoded sequence. - Usually not really useful. Returned list may be empty even if 'language' property return something != 'Unknown'. - """ - return [e[0] for e in self._languages] - - @property - def language(self) -> str: - """ - Most probable language found in decoded sequence. If none were detected or inferred, the property will return - "Unknown". - """ - if not self._languages: - # Trying to infer the language based on the given encoding - # Its either English or we should not pronounce ourselves in certain cases. - if "ascii" in self.could_be_from_charset: - return "English" - - # doing it there to avoid circular import - from charset_normalizer.cd import encoding_languages, mb_encoding_languages - - languages = ( - mb_encoding_languages(self.encoding) - if is_multi_byte_encoding(self.encoding) - else encoding_languages(self.encoding) - ) - - if len(languages) == 0 or "Latin Based" in languages: - return "Unknown" - - return languages[0] - - return self._languages[0][0] - - @property - def chaos(self) -> float: - return self._mean_mess_ratio - - @property - def coherence(self) -> float: - if not self._languages: - return 0.0 - return self._languages[0][1] - - @property - def percent_chaos(self) -> float: - return round(self.chaos * 100, ndigits=3) - - @property - def percent_coherence(self) -> float: - return round(self.coherence * 100, ndigits=3) - - @property - def raw(self) -> bytes: - """ - Original untouched bytes. - """ - return self._payload - - @property - def submatch(self) -> List["CharsetMatch"]: - return self._leaves - - @property - def has_submatch(self) -> bool: - return len(self._leaves) > 0 - - @property - def alphabets(self) -> List[str]: - if self._unicode_ranges is not None: - return self._unicode_ranges - # list detected ranges - detected_ranges: List[Optional[str]] = [ - unicode_range(char) for char in str(self) - ] - # filter and sort - self._unicode_ranges = sorted(list({r for r in detected_ranges if r})) - return self._unicode_ranges - - @property - def could_be_from_charset(self) -> List[str]: - """ - The complete list of encoding that output the exact SAME str result and therefore could be the originating - encoding. - This list does include the encoding available in property 'encoding'. - """ - return [self._encoding] + [m.encoding for m in self._leaves] - - def output(self, encoding: str = "utf_8") -> bytes: - """ - Method to get re-encoded bytes payload using given target encoding. Default to UTF-8. - Any errors will be simply ignored by the encoder NOT replaced. - """ - if self._output_encoding is None or self._output_encoding != encoding: - self._output_encoding = encoding - self._output_payload = str(self).encode(encoding, "replace") - - return self._output_payload # type: ignore - - @property - def fingerprint(self) -> str: - """ - Retrieve the unique SHA256 computed using the transformed (re-encoded) payload. Not the original one. - """ - return sha256(self.output()).hexdigest() - - -class CharsetMatches: - """ - Container with every CharsetMatch items ordered by default from most probable to the less one. - Act like a list(iterable) but does not implements all related methods. - """ - - def __init__(self, results: Optional[List[CharsetMatch]] = None): - self._results: List[CharsetMatch] = sorted(results) if results else [] - - def __iter__(self) -> Iterator[CharsetMatch]: - yield from self._results - - def __getitem__(self, item: Union[int, str]) -> CharsetMatch: - """ - Retrieve a single item either by its position or encoding name (alias may be used here). - Raise KeyError upon invalid index or encoding not present in results. - """ - if isinstance(item, int): - return self._results[item] - if isinstance(item, str): - item = iana_name(item, False) - for result in self._results: - if item in result.could_be_from_charset: - return result - raise KeyError - - def __len__(self) -> int: - return len(self._results) - - def __bool__(self) -> bool: - return len(self._results) > 0 - - def append(self, item: CharsetMatch) -> None: - """ - Insert a single match. Will be inserted accordingly to preserve sort. - Can be inserted as a submatch. - """ - if not isinstance(item, CharsetMatch): - raise ValueError( - "Cannot append instance '{}' to CharsetMatches".format( - str(item.__class__) - ) - ) - # We should disable the submatch factoring when the input file is too heavy (conserve RAM usage) - if len(item.raw) <= TOO_BIG_SEQUENCE: - for match in self._results: - if match.fingerprint == item.fingerprint and match.chaos == item.chaos: - match.add_submatch(item) - return - self._results.append(item) - self._results = sorted(self._results) - - def best(self) -> Optional["CharsetMatch"]: - """ - Simply return the first match. Strict equivalent to matches[0]. - """ - if not self._results: - return None - return self._results[0] - - def first(self) -> Optional["CharsetMatch"]: - """ - Redundant method, call the method best(). Kept for BC reasons. - """ - return self.best() - - -CoherenceMatch = Tuple[str, float] -CoherenceMatches = List[CoherenceMatch] - - -class CliDetectionResult: - def __init__( - self, - path: str, - encoding: Optional[str], - encoding_aliases: List[str], - alternative_encodings: List[str], - language: str, - alphabets: List[str], - has_sig_or_bom: bool, - chaos: float, - coherence: float, - unicode_path: Optional[str], - is_preferred: bool, - ): - self.path: str = path - self.unicode_path: Optional[str] = unicode_path - self.encoding: Optional[str] = encoding - self.encoding_aliases: List[str] = encoding_aliases - self.alternative_encodings: List[str] = alternative_encodings - self.language: str = language - self.alphabets: List[str] = alphabets - self.has_sig_or_bom: bool = has_sig_or_bom - self.chaos: float = chaos - self.coherence: float = coherence - self.is_preferred: bool = is_preferred - - @property - def __dict__(self) -> Dict[str, Any]: # type: ignore - return { - "path": self.path, - "encoding": self.encoding, - "encoding_aliases": self.encoding_aliases, - "alternative_encodings": self.alternative_encodings, - "language": self.language, - "alphabets": self.alphabets, - "has_sig_or_bom": self.has_sig_or_bom, - "chaos": self.chaos, - "coherence": self.coherence, - "unicode_path": self.unicode_path, - "is_preferred": self.is_preferred, - } - - def to_json(self) -> str: - return dumps(self.__dict__, ensure_ascii=True, indent=4) diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/py.typed b/.venv/lib/python3.12/site-packages/charset_normalizer/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py b/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py deleted file mode 100644 index e5cbbf4..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/utils.py +++ /dev/null @@ -1,421 +0,0 @@ -import importlib -import logging -import unicodedata -from codecs import IncrementalDecoder -from encodings.aliases import aliases -from functools import lru_cache -from re import findall -from typing import Generator, List, Optional, Set, Tuple, Union - -from _multibytecodec import MultibyteIncrementalDecoder - -from .constant import ( - ENCODING_MARKS, - IANA_SUPPORTED_SIMILAR, - RE_POSSIBLE_ENCODING_INDICATION, - UNICODE_RANGES_COMBINED, - UNICODE_SECONDARY_RANGE_KEYWORD, - UTF8_MAXIMAL_ALLOCATION, -) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_accentuated(character: str) -> bool: - try: - description: str = unicodedata.name(character) - except ValueError: - return False - return ( - "WITH GRAVE" in description - or "WITH ACUTE" in description - or "WITH CEDILLA" in description - or "WITH DIAERESIS" in description - or "WITH CIRCUMFLEX" in description - or "WITH TILDE" in description - or "WITH MACRON" in description - or "WITH RING ABOVE" in description - ) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def remove_accent(character: str) -> str: - decomposed: str = unicodedata.decomposition(character) - if not decomposed: - return character - - codes: List[str] = decomposed.split(" ") - - return chr(int(codes[0], 16)) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def unicode_range(character: str) -> Optional[str]: - """ - Retrieve the Unicode range official name from a single character. - """ - character_ord: int = ord(character) - - for range_name, ord_range in UNICODE_RANGES_COMBINED.items(): - if character_ord in ord_range: - return range_name - - return None - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_latin(character: str) -> bool: - try: - description: str = unicodedata.name(character) - except ValueError: - return False - return "LATIN" in description - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_punctuation(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "P" in character_category: - return True - - character_range: Optional[str] = unicode_range(character) - - if character_range is None: - return False - - return "Punctuation" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_symbol(character: str) -> bool: - character_category: str = unicodedata.category(character) - - if "S" in character_category or "N" in character_category: - return True - - character_range: Optional[str] = unicode_range(character) - - if character_range is None: - return False - - return "Forms" in character_range and character_category != "Lo" - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_emoticon(character: str) -> bool: - character_range: Optional[str] = unicode_range(character) - - if character_range is None: - return False - - return "Emoticons" in character_range or "Pictographs" in character_range - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_separator(character: str) -> bool: - if character.isspace() or character in {"|", "+", "<", ">"}: - return True - - character_category: str = unicodedata.category(character) - - return "Z" in character_category or character_category in {"Po", "Pd", "Pc"} - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_case_variable(character: str) -> bool: - return character.islower() != character.isupper() - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_cjk(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "CJK" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hiragana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "HIRAGANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_katakana(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "KATAKANA" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_hangul(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "HANGUL" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_thai(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "THAI" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "ARABIC" in character_name - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_arabic_isolated_form(character: str) -> bool: - try: - character_name = unicodedata.name(character) - except ValueError: - return False - - return "ARABIC" in character_name and "ISOLATED FORM" in character_name - - -@lru_cache(maxsize=len(UNICODE_RANGES_COMBINED)) -def is_unicode_range_secondary(range_name: str) -> bool: - return any(keyword in range_name for keyword in UNICODE_SECONDARY_RANGE_KEYWORD) - - -@lru_cache(maxsize=UTF8_MAXIMAL_ALLOCATION) -def is_unprintable(character: str) -> bool: - return ( - character.isspace() is False # includes \n \t \r \v - and character.isprintable() is False - and character != "\x1A" # Why? Its the ASCII substitute character. - and character != "\ufeff" # bug discovered in Python, - # Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space. - ) - - -def any_specified_encoding(sequence: bytes, search_zone: int = 8192) -> Optional[str]: - """ - Extract using ASCII-only decoder any specified encoding in the first n-bytes. - """ - if not isinstance(sequence, bytes): - raise TypeError - - seq_len: int = len(sequence) - - results: List[str] = findall( - RE_POSSIBLE_ENCODING_INDICATION, - sequence[: min(seq_len, search_zone)].decode("ascii", errors="ignore"), - ) - - if len(results) == 0: - return None - - for specified_encoding in results: - specified_encoding = specified_encoding.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if encoding_alias == specified_encoding: - return encoding_iana - if encoding_iana == specified_encoding: - return encoding_iana - - return None - - -@lru_cache(maxsize=128) -def is_multi_byte_encoding(name: str) -> bool: - """ - Verify is a specific encoding is a multi byte one based on it IANA name - """ - return name in { - "utf_8", - "utf_8_sig", - "utf_16", - "utf_16_be", - "utf_16_le", - "utf_32", - "utf_32_le", - "utf_32_be", - "utf_7", - } or issubclass( - importlib.import_module("encodings.{}".format(name)).IncrementalDecoder, - MultibyteIncrementalDecoder, - ) - - -def identify_sig_or_bom(sequence: bytes) -> Tuple[Optional[str], bytes]: - """ - Identify and extract SIG/BOM in given sequence. - """ - - for iana_encoding in ENCODING_MARKS: - marks: Union[bytes, List[bytes]] = ENCODING_MARKS[iana_encoding] - - if isinstance(marks, bytes): - marks = [marks] - - for mark in marks: - if sequence.startswith(mark): - return iana_encoding, mark - - return None, b"" - - -def should_strip_sig_or_bom(iana_encoding: str) -> bool: - return iana_encoding not in {"utf_16", "utf_32"} - - -def iana_name(cp_name: str, strict: bool = True) -> str: - cp_name = cp_name.lower().replace("-", "_") - - encoding_alias: str - encoding_iana: str - - for encoding_alias, encoding_iana in aliases.items(): - if cp_name in [encoding_alias, encoding_iana]: - return encoding_iana - - if strict: - raise ValueError("Unable to retrieve IANA for '{}'".format(cp_name)) - - return cp_name - - -def range_scan(decoded_sequence: str) -> List[str]: - ranges: Set[str] = set() - - for character in decoded_sequence: - character_range: Optional[str] = unicode_range(character) - - if character_range is None: - continue - - ranges.add(character_range) - - return list(ranges) - - -def cp_similarity(iana_name_a: str, iana_name_b: str) -> float: - if is_multi_byte_encoding(iana_name_a) or is_multi_byte_encoding(iana_name_b): - return 0.0 - - decoder_a = importlib.import_module( - "encodings.{}".format(iana_name_a) - ).IncrementalDecoder - decoder_b = importlib.import_module( - "encodings.{}".format(iana_name_b) - ).IncrementalDecoder - - id_a: IncrementalDecoder = decoder_a(errors="ignore") - id_b: IncrementalDecoder = decoder_b(errors="ignore") - - character_match_count: int = 0 - - for i in range(255): - to_be_decoded: bytes = bytes([i]) - if id_a.decode(to_be_decoded) == id_b.decode(to_be_decoded): - character_match_count += 1 - - return character_match_count / 254 - - -def is_cp_similar(iana_name_a: str, iana_name_b: str) -> bool: - """ - Determine if two code page are at least 80% similar. IANA_SUPPORTED_SIMILAR dict was generated using - the function cp_similarity. - """ - return ( - iana_name_a in IANA_SUPPORTED_SIMILAR - and iana_name_b in IANA_SUPPORTED_SIMILAR[iana_name_a] - ) - - -def set_logging_handler( - name: str = "charset_normalizer", - level: int = logging.INFO, - format_string: str = "%(asctime)s | %(levelname)s | %(message)s", -) -> None: - logger = logging.getLogger(name) - logger.setLevel(level) - - handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter(format_string)) - logger.addHandler(handler) - - -def cut_sequence_chunks( - sequences: bytes, - encoding_iana: str, - offsets: range, - chunk_size: int, - bom_or_sig_available: bool, - strip_sig_or_bom: bool, - sig_payload: bytes, - is_multi_byte_decoder: bool, - decoded_payload: Optional[str] = None, -) -> Generator[str, None, None]: - if decoded_payload and is_multi_byte_decoder is False: - for i in offsets: - chunk = decoded_payload[i : i + chunk_size] - if not chunk: - break - yield chunk - else: - for i in offsets: - chunk_end = i + chunk_size - if chunk_end > len(sequences) + 8: - continue - - cut_sequence = sequences[i : i + chunk_size] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode( - encoding_iana, - errors="ignore" if is_multi_byte_decoder else "strict", - ) - - # multi-byte bad cutting detector and adjustment - # not the cleanest way to perform that fix but clever enough for now. - if is_multi_byte_decoder and i > 0: - chunk_partial_size_chk: int = min(chunk_size, 16) - - if ( - decoded_payload - and chunk[:chunk_partial_size_chk] not in decoded_payload - ): - for j in range(i, i - 4, -1): - cut_sequence = sequences[j:chunk_end] - - if bom_or_sig_available and strip_sig_or_bom is False: - cut_sequence = sig_payload + cut_sequence - - chunk = cut_sequence.decode(encoding_iana, errors="ignore") - - if chunk[:chunk_partial_size_chk] in decoded_payload: - break - - yield chunk diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer/version.py b/.venv/lib/python3.12/site-packages/charset_normalizer/version.py deleted file mode 100644 index 5a4da4f..0000000 --- a/.venv/lib/python3.12/site-packages/charset_normalizer/version.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -Expose version -""" - -__version__ = "3.3.2" -VERSION = __version__.split(".") diff --git a/.venv/lib/python3.12/site-packages/dns/__init__.py b/.venv/lib/python3.12/site-packages/dns/__init__.py deleted file mode 100644 index a4249b9..0000000 --- a/.venv/lib/python3.12/site-packages/dns/__init__.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""dnspython DNS toolkit""" - -__all__ = [ - "asyncbackend", - "asyncquery", - "asyncresolver", - "dnssec", - "dnssecalgs", - "dnssectypes", - "e164", - "edns", - "entropy", - "exception", - "flags", - "immutable", - "inet", - "ipv4", - "ipv6", - "message", - "name", - "namedict", - "node", - "opcode", - "query", - "quic", - "rcode", - "rdata", - "rdataclass", - "rdataset", - "rdatatype", - "renderer", - "resolver", - "reversename", - "rrset", - "serial", - "set", - "tokenizer", - "transaction", - "tsig", - "tsigkeyring", - "ttl", - "rdtypes", - "update", - "version", - "versioned", - "wire", - "xfr", - "zone", - "zonetypes", - "zonefile", -] - -from dns.version import version as __version__ # noqa diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index dd1616cf9138d5aa85c49258f3c8bab1bb5db3ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 zcmXw1y^a$x5MF0@v-#mlM03RzG(jTE9q14eqN2Skpi8rI>`7w9Ywy`kxJ?6ZK~INx z6y9K3DmvgKimMp!221|t^Vsw6?x{PB>52!|JUG1?oNq(PGqu3)`?_a z*n-J{O!c5n7a2?1Y?dD$O5>Y0Y^*tZd;R__SgW@x%u0N~eQU&$i!B(5lkRl01J~k7 z#~pa9_uz0y9y|z~^pfkf$7#<)+knRm&M#jOk`4&!&5pJP%%B(RBL1W=$-<095p|k*t=s$L0d?X+-`=A_Pdb@I%9jpNyN9^rJk*<`6 zpK-`D=AMwap5<6nm9(O&BH>ySi9?tly>`!VT2;q$ReeqFqKEsda${>KT>~_jy%L); zcCEZpUX)~Q_N7+Kvi~B_=jRutR{@?j^e<=}zN8psRVkxFRn43Be)PJwa;M>qdqv(E fZU2biSIQXs{WxK#f3j!O!KdP{WC<(XOS=09Rn-Hg diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_asyncbackend.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_asyncbackend.cpython-312.pyc deleted file mode 100644 index e1203dc4239fd6e379b0543ecc2b5354da523242..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4784 zcmbVP-H#j96~EV>vB$B!-efnMkKIJh6449-n}imMKr~9A5CMK52({ayVKQ?!>tsA* z?j3Jpmxm}_RjJylePa7BtfKxUeW6Gw(TF!*;4O()3goHh+&g2>%o<2?S9^Tt+^=(g z=kxxzUf)Nco%!JQ`mZa5e1jiJ)8o$g26VPbfxJyzvPE2ayP!ta6t4ih;)F-#JPmj{ zqqmFk%!1^WfLl&+i;`;qw=!9;N!~u-RVO@sSTpO6+Dg$_0y2IczPAY@Yt$uc1-I-L zw&+^XEpCxD%>^mebXNyja^dW?@{pQFRP78r?|k5g?0VR7lAUm0?4!iv139_?1MxV%To8ZMhCNk#Ox}}5_3R6lE%WFM)QA-<`+hjBj3@0q??omncHNj z&fOt9nmjZz*mcJWhkX`Ru(xS@1L%g_b{OI!a9I*)b?NUB&E0SU5(tDnX3e4`bZnW?!Zw$+DpVGwud@9WN z#pA%6SF`STfncMW+O8$Pj)6!VeT(DWW*@NxBg(!)=&5#-PIL+JWuX&<7kYh<_0Wvm zw>S^@9xJO}W?`Q(jv5#ph=oY;ny~X2twn0~>$wwO7$;)omMiqBMB^u6yP1LrfHW^i z!g~f6{Q}7;AbX;%^tqqGL>_DwR8%O3-5v`DVH|6f^CxIHjE8wNkoRm>h{T3~D?`2% z0_`_({EK<#%4uJk_-KC&`_$o0*h8SbCysrq8hdn6&w6wbCdp&ZLMJ))N?I`BW4o<> zV6sd81gkzTY#0Bc>ky%afWR{2}O_P6LF^0u5H+1RCSpMyS^QE1D{LfVl1C#$dZ6kA`C_cbK)uXFJW%VPZ5Zu zJ&{a1MdhcO=#H2Y1NWNdY0jL@bsqHgz?2NU0@J6Gbj)(j?4n}V4^_^QQ(wdRxlDsl zb0nB0#9<&?5P1DSvQ;Br@94P$>}TQkQHY>o=JrJEoq~ z5kHF)&LPQZ<_+W_LAA*sqr$*yN*g#lTN|n_pH%xY=?@ELF{)#w^&z)?(GPg&Bu^LE zXd2-k%cb<#d^Z9SMjCD=^%2#*z_C5cc3mj^g)Ak}wc_ft>IA;eoX`rL{^&?XmR4%$ zO%P|Qsg9}TVY`46a)`-ANY2B*z|u)oO|j|XNCvD)d_)-YrduJ?Ki7atiOzZ^xn(eO z1AwhW<#Xx{k|`NrWA%{G^E@MWTED&n(ZY})6%O*8>d;~@f!T2V; z=}_q?nT*m>y(tyIdy;sPTn)WU*4ps! zLG*q*8?_ZT&RZxd8W_ZOczJ$0=yK+cP9{?P)U4m5SnK$=y(Lx$=YE9bcS!z#~SGM&oE%`xQ2b8-SaGG13N#5XwJ5!EZ+ z0&=fNDSbFMN9*?qkcZFhr_1*Vklj-TUH#Wby99oAmvrdAyi4Hc;e3Iv-X}mF?pvm- K4+s#cwEqLT@vD~r diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_asyncio_backend.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_asyncio_backend.cpython-312.pyc deleted file mode 100644 index 88cd29e1d0124d426f4295a74a0351129906c6d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14121 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 D@E&?i diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_ddr.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_ddr.cpython-312.pyc deleted file mode 100644 index 72f10456b5f3ad3a541580c0133ec06464fc867c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7833 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 VYe)Ni-`4CwWaJe2Io|!Z{{TV8Bgg;% diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_immutable_ctx.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_immutable_ctx.cpython-312.pyc deleted file mode 100644 index 2eed9c0c971f468e558f8c2b8738599f646d94a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3229 zcmb_eOKclO7@pZ(zv{K)JX-hRHio82Oj4UZplVf`0);9Rav%tVi&)&w_u|^V%ycIG=)4oBH z@R*yFe&=@+o84i2Bs-SP$JJbh^BY7J8~%ve;=DEt1r^Pb)u$hoDEenpNC>bX=gn@~k!kKfrYQ(jI&huSHoR^Bl0yE+n z!(iDnB}0#cu-UWu5j}5!V!;OMnc`X9FaxiR7xfnyD=^aohjj)#3fB$1Kv5EsDRDhF zWJ>wWh;DjDGr5v(1{B3I0x+WD@pydl!C-2*FrueeQ8!p&G^-A$)Pkm`UdeJbl^oUc zqp4i>OsY6;3>WeTlKb|jc-GMO7BlMk%vqhMv^-BW_*gZ@lErZrhTA8$Jak5~L9{Kf z$M*ucKS^!{w|v|;-M1Kw&7P9?Nw@F!9w4fo%RvxPTX+oZC5JM5pc~6 zAb4er64NoL+zAAgz)%FX2jxJ0--+9TH*FA_C17(may+1kDw?XE_< z=b9IzJ(JHZNo^~^&}6?AZ0q6-wZ2P*qtg`Iz*?q%ZZb(g&ixeXoi>#Fw!jxYV8zrAd?1pxcf`kY_ z8ye(>D>rZ;7H??4;d7RN17T`I0}j8lWaXDMfItGV$f3djtgOqnx!pGoz?|_ zivk#N77DoVV%27lyG$X&1v|r`GSdNwPYcfhOzp?Y4ao-c$KVczp=g65>Wl@{#zlOu zOvE1{7*s#|s$;;#Hv@raz7yFzI*qSQ>3{uxx;QSQt$a9B0A6aodvcU_n>n```$C{23sBOp)7Lw^#aahhjB>hW6J) z65hFrqq|-biO%q=14~Ukx0~az+k6CeTOM1pMm`ehwXOOTT$2b`rO1R5IKm20_gwkd z0ob->v-Gf3KxIJ6gSdsq*MU{QwH!MHd#EIT9|(e~ule%emD4k)tG@V}mu&5tjR#fFmE7FpG+sgKx*OH$lVWVj!*fe>{_K436 zs)f9vj~SyG##txGW^Q`kUU@yk>?aJuglz>>!pa}pT=-0R;6G}=A&xQh1ZEC@8px_h zDXoPGZT*RaZ-+Z7=a<7B)o{nco}=ICOW~I%gZI3xwDT?j@>}mF8m?`R(TH{LpB25T AcmMzZ diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_trio_backend.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_trio_backend.cpython-312.pyc deleted file mode 100644 index 2afe8656f0b0499d688fdeee93e97b13826a8f27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13172 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 KA&^8F_x}Je$Swu| diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/asyncbackend.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/asyncbackend.cpython-312.pyc deleted file mode 100644 index f574560b6c1fc272f64ee0eb64f8f8de0420f27f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3412 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 Az5oCK diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/asyncquery.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/asyncquery.cpython-312.pyc deleted file mode 100644 index 5d98326f370ae11d658658d7f38346d0be74a49d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31670 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/asyncresolver.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/asyncresolver.cpython-312.pyc deleted file mode 100644 index b7689d5f80400ad93c77bab0c01e10b6a6f8a5ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21326 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/dnssectypes.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/dnssectypes.cpython-312.pyc deleted file mode 100644 index 4337d418f3ae70f16ecf1117ef0882a175200b6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1970 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 fg-J)wS)}XjERj diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/entropy.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/entropy.cpython-312.pyc deleted file mode 100644 index d44be4c5649bb46e8f14dffe01edde0860e74d28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5965 zcmcH-TWl29_0DS_-p8!*I>ry|H8_|hiWl1$kPReYlZpVQ2}v7VC9Cz$fY;c&W@c=> zix-oW51T?`CzZSwl`v{0Y*C5)HT`M>`KVN>^0HK34VD_I`QdMgk%&+vdd|$wc-L8? z{`5+7&$;Km&OP_M?)<~$DkD(Z-o10?GY=vE!G=*Rd1URMfXtEii9iHui1g`@>Z7za z-A8MX>0>m=_OSrzA#T{xXBoEkSt&x3pAmt%LIm~>t*_Z*gjsciEhCTAJQyB8PGg&Y8w$(oG%$ggAcLEX);dhU zUU%~R!c2$JGeLda1~X9j8_EP_jE4(nVUEe8vc(uWSZ`qKuz*`I0lmbDz!k2b$*Z8( zZ|Rj#e2=|Qt!Zq&_ZD1H*$CWCFcKBSan&M3&Wf_4+N0vQ66}vFqH0$LBrzmJqGwh1 zcr1KQWrwh~4us@^p-4a6=|HGeWwEK+unrE1QPqxfLy{DVt1PasazR;%NU8-jKonFu zCaadSqB0T@R2$NQfPrd*h76sN@NQ+~)#rPDJTMR&76a0V2#Pco2@eFqF+mKRh{)kU z%a|A)3jh~^k+?Dti?+A4wgu#fBECKn3ZDzXQ366#4v4xAwv2$)1mUC+B^XS23pBIE zp!#s*PoTO?vNg3+>@`07S^$6}TU`Udz3SaE*E`#r@isxrzUr-=J3f2-^ImLZH{%Ew zj7W(K!tLQ(!|D3=jCZ%z=irwewOOa<)3>MJo>3k;>#{ZVQ$5#?t=84wJUV~$#<8iM z>nFacX+T=GZVR^7fD58p^SrATNmNFqXp!-tYHP;xcR=RI1l2=6{E(0&sO=!m-cW#Q zn$LRIxgCT^)?&k(panVs8j?gsM-$ypO0PdokJG5*6HEd?m2-6z7Gz1B_}RRgzJSS(QBD1fgr+2+-b(0S{j5}nxDP6o3aeDVj7<8VNN6@v6;z4x zq67n_Y`oZA;0)6UlbBK_pmld1D*r|-8XpEzJKsR7n5x*>GO+6@f=0yk9h zUOEVcJveSdDRK`O!-x@$D3H`P(n@6N0!@+AumV!_NU=mgkdV;wH9-JwN-J+vnv}rS zKqd)Kp_q`e4<^bxp#mwn%0E3lefkNZ>^?(#FFbTMJY#_H45?bza~WOc^-VjNMXuEv z&U-*05rUSi3BUuY_c8%Ujd1)Ds;QX1d(en$+M;@8_`q{};h=qXQZO3X*-!-2(uR$|5 z8nYl0=Fuc|nnYP6je(TrZw3w#=`pHl!Wj_6vB0PliVCq|m6rOoK=q#AEp3Kw2?Z&k zl%*P|RI4%2eq=l>YFUuV#zr9Ouo$USMph)1RU*TpY7wItEhHySMjmyGV04Q>*W zD|i0zZI$mkLczS+`rj)zvPUXfzL!)9*l(XqxCAhCR<1?toVQu-(P!&Wwn-=X> zoONku-9u-+X5rHpm-(%b*F|qdzhwXF`itwq_9I!3cg{X*U-9_T9^W4;7nIxaTk%X| z>qAf5YJKw}d)K~XU)ixQy<=ZyM`xzK3-SsF&MBKMOYOedHQ)8n)0}PG3S@WIyLs-| z?6C#*w)>Vl?F~#FK|j!YwKo+?I~%U|J}U>Na+`>!a?1IvVjX`jqieg~UDw^t+;8W) z4-|)Ayczt~-iAsGzh)2vLcSOpCs6o+0ZsUcF$tJ+iumc?$B=LkKP7bnVTscGDMT;u zqk3*(&&#)UPolEWhnnOMV62QU8{LB@r<1>#T&PI%O(4bOt>hmj)BK)g+a5ioDrD&Y zc_cU!9O)o%B^^NiN*t)Yt>l5)+kS-;d{F5Jx-@+$!#6Cvn&z7qo9?zOwPbd7ruoig zTc>s;l{*uW;GxA1g=8gc8W2op;nS|{Ye3-2=m{zT4hk*@+K`Yl0)4Ir7<4G6R0PH_ z_D?A2XSIGFF4A-yFg0J2@a9i)(XBAfVvfT-Hxw9Wg>mlEjsbdYNp1pmqsatoD3GQ; z@J9hTO~BrHWwG~a7CvO0k|hbN5{vozBWI;IVW`&eV)Df#LnI8Wkr$BqOWOeRvufEd z(4vn>QcRK#0ZG3d)H=UKkK%`s+6@&Xw2_1eNdTk~Dl;4!m(giKV27e>r8}>F{i1R~ z6r+in5~`W+`v|7WqflwrIU`O_fF<$`Q*743&+L5Qs9W7smpYx<)V%C$M$)TS_h&1r z<_^vtgb==|Zn_(C=b7%SeFem->F#e}$bs1dIHYo_JIi}#lzHLCnc3uG#p3y;s&rLr znr~gUwQ4#d?SSnedv65L>v!YZH5wWggF&?{7#xlXqeF;0gTeEop&`9T!kg4GcnNJw z%XTC`V(3>@CIT;El$|a`O`b=gxkU60wZ{_*O~R@MKKUY4SI85mo2yTKuprI{*9f3F zpM`5oy`Lih7L+vvISNIC~M#g`6;$^zK|Q$CC6n&91iMts7WOZbhF^mQN`B!p_(I#+41o! z>v*YGmOu%#@vb^W6w??6&0kFBQf5*MKG29>Z$dA;NKXP#HMa*0o)z@~rMZ$`=qZq| zM#Jg?d@|ngWwL7ZE?a%8eBBDaJ4lKzaxE*AI`H1?p?^TNIY9c{`I%!Kh2{ z$!$>mSQ4X<>Atfxkm2_(+xCJqR`{)H2*e$WO&LD0Yzt^I@s~^ZaM9if@TWw3Mo9RR zPr^l2ixMA!|5p-fhi)O7j^nQcl@A6((P&Hw;m=eV?dg=JX7B2Y;H^q6SYcwUn+T)zgRcF1;GjFYUo73LrjMsnpM6Qgbd^2D^0CH}Y+BMUj1AJF1 utbv8_Q*3>ot0J~?NcnMr&7Yh=9;%df8` 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 Dz%dnJ diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/exception.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/exception.cpython-312.pyc deleted file mode 100644 index c9fbefd9342974a3dfbe8758806c5d2a12001538..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7222 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/flags.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/flags.cpython-312.pyc deleted file mode 100644 index 68e199082713b4f81a6219670c89d4e9d58abb22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmb_e?Mqu%7(eH}Brz|VsI{$I*PEhQGg>oi*&Hh!R@<&+bxa3^+m4v#9&KWBliquk zDG?$Rs}K z&dKkAfS16yFtt7V8)z@laE7a{9F!>`n?xfj(WoY5s7hfLGeTBWMM?y6ooLcSqREV= zJh79y{2#_s2`EN0}HW#kr9$@{TnPGhlKDKi-xT4efS zER(ty%P(2?bNa~8@P(L}ve@~2A~_F4m@!Q^W3!x%{E{KVf?V{2Vh)0MNM3Yw6|cNa ztX?ttobSDvL_~a)M5u^pd#*xBkT3(!&}39He7KGuMF4~?Onhq`LyNC}9JP~<7y`v~ zgWx-K_JCnqve=K7?PdBxHlEBROw)E5%vvz?Bvhj@5G$U!0}TY6Te>0?NZKiUBaM*l zy1pV7#0m1l2|`w+f>59ZDP2FaEy;~++{tn;KF_n2YAk&>a|v(N+yU|ZL0DV^ur$2>MVUUnT=Dlk_n+GJpDHK*@GDgra;h%k@7fq$A1&S7^PS$2 zPV-VE>)7EZ2wo$#Dx3jjlUR*dfmn@x-mYuF16`gQ&*_WIu#|*i!Z~N>O_om>35#h; zCS_VmZdO683ZhmP6PX2ODk?&H{X4MUNkbE|*^U_? zo0c&%Gvr{#7H}|Q54B=;JAHBy0=F>OMBH1t`pd;VpRyw+H13S_H#~(dzyW?>!mxMb?!q*C}{iS?L zpEK~*wcR&#>l(g}ct7%o&laH*Z4pjj?cniF#It2!yBSEifxl~XqUb__Pp^SJXmq3K zK>@E4G4O?Ndt6_8F4g{qTsFRfC_*#;1_2KRrGE_&dh#Xl{Ym-`y`40;c5&m<`lUkx z>fu=dY-^*iUN|J6R=qL}m8Po%rmZBOo+b~_ss?4Ml*ZAeZ1Du;=`==^YS2y3U<9V} z_yJDUUN;SwM$i>5kDx1zu1L8DU6HLGbVblLSQgPWxFw=%uzG^HypL|L-YyCix#NE* RRX=fcKDx7dXI0>Z`UjihilYDk diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/grange.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/grange.cpython-312.pyc deleted file mode 100644 index 1bc04e2a1391a4dbb21e49f306a48f8e52db18b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1772 zcmbVM%}*Og6rcU@+V~4_O%tLNHcBFJ0^SCCAW@n^j0c@|OFOeQBtcORyw%Rmn|<&1e(%kj z`8^T|0wQDcyIC~=z$>bph`%#=GeIWLfeIFZ%BXCCDYAuf#`QfanFToK z@))jRS3nS#kghQFZ%2IO%aQlZJ=P<{R2h}2vMO8URIbXaeAQM#Ik z5O>@GSRsCAWrd=i5mngj#NA|UFb#gl15g!gp@Z0G`&8b;?!hNT0ycl*Ls(HZY}L2* z3Gwm&k0t9_28iXDEv$5%p+VH$&Jq%A-WDnm!h_$MJx4}e#9JVEr+M+h|8DEJnM z;ILkf;SW}W)nmjWKrJlxE3nCY(2BD;g7W=|jm?k>zoXgg>hwAHGV~5p#%66U%PoLJ zxN?y4dJu~^4@KH@V%9)K(a4H&r%5%POf#8lC%v1=h!&PHEJB@l8KSHQMIxGxVGg2X z(9>>Es2d}mr-e}_GjbZBBp2QyzmH2pcL3(S#4;iaVkx6kP(^BI%E*G^b*pkgH@r~)5fCjCDq2>9YNgNPAr?!PhkEyO&n)3{ zIaRmZF1V&Z6O*Ivh8EU!EU%>zGLXxWY+S!)saj6M2|wZ~S)T{x@-!tnLeu4x67tqD zbQ#5>2aZ;q$ngUyZxo?~Oo)+D)|9-Y7%G&$)+|LzmZ4sj3fi({uH(F+k0)=9NtTA; zs3|L}at>M&VJGFh`zUFyyIomi6qo4kA$ohz*P0|1+yLzuh;(nHTcOCtbSo4lLomFN zYK0>k>DK`e2s^y<%~SvO_|MUUXz!C^z4$UZv=<#}4nNqBK5Paa9!7fq0%6}QQ)7s` z!!;(heLKCs1P|gDwz+4swR9^Muccb?zIwV9i8@ys-yVGQv12`(s7=53=gH&x(a!xYOkO zYIj=xa1C$TXOO?xk4`oNlZWA6=dUUsHFnDhX#pC8`?^eq0KQ2^ZFWKuKx)xz5>Cv eATfhZstw3zJH2yt$NDw3GrD*A)4%D8hxad?Yq^gA diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/immutable.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/immutable.cpython-312.pyc deleted file mode 100644 index da37deeb66a3bbcfc4037d2a18e2932cdaf5a538..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3782 zcmcInO>7j&6|U;;`5!a>#Ri0F1FDlTw zc#JJskq>r4ge}n8Q6jB#%3989_nc#LOD^`>lC74dY?O_Z+zi8Ems8&BnQjkWHm9Vd zdG)$}UcKM1YW`YR7bVcn{ctt&_Yfifzz?qlt4w(sm_?!!of>3_LN6FXUK|qh(vU=P zTr>jt;82hf78J-$qDwPG4=|cL;ECGnbMHGl8L~9-JXVF+Bt{4w{moIz0mPKq;DxIZ|&{ zv)^{2I&@jHpz@LvDwv997E4YnuNIYzs@WzlX;RHG$fG;Te*v;cCIx=prr!J{wFQsQ z)$Xbe=Dd)3i=bbv?%;_@I!P3NmyTcl`Vtk$r04~pcHJb&g=@rlC&j)#Hc3~jnnF_S zlb#e*3X}cnD3jHK><3KVbB>(hW?r^Om~0i9md#|DUNf7DM&)X|osqk$e(aL7mi*ow zKE~cVjav(H*Pg&G`C-;DGBYwn0}e{b^1B72BwNe|F(5OW zkxS;7ta8w13<$^u>d7mZG8?xUFQ|qui?a+YDQL`+ZPPVlPm`3tY9JYMqFD7W2b$M0Hqm4)j-2xgS){7VbMi#Y90)aIC1SHgf{Xh-!@_B}cNDk>MB{j+xG} zkSz=^y6M$Gdc@4LG%qrnn-8+uNLn*>mhR75S~~TB6&|FG>~OkRvPaCq`PA8SX)9~9 zGeuP!Rqrz^trx7cAEXo{Y*$dS(9oWZf}c>R;UxxTG<`+B^6X<{?p zwcTh#lE(emV;!40)TLYASb3M_n8ew=M@FU zns7`UWhDy_m1W)x9miUqsIMCAFK>rYs};yJ*-kV)c{KNEE77^0=v=lo5~tR}r}#mb z)BLK1j{y8!<=24uCgrE!)#((qeG|Y$zgl5iwdDY&_cyO@SF30Cl6zp|lmK|)fgm7> zH#-4VP0`1V60u$IM`^jb{W1x-SO*CnIMZ)%`5;M(100>k5iXMNO<%=RGVts9Q-d<{7*;tVUS^=3m}Qz7+~h%#+uBle0Jc#1c;~ z&RtxK9o?>Pn7y{sdUQ$uaP-%sTkWa!_S8mu_eSg47lhUwn-^ihvpY*Cp59$Jut0Z3 z82cjLSj|6s`NtoroAK_?A*d5`6Kml^UymfJ=0@@~FpH!Hn@O%VU11iR=kr0r+u### zFM}0audGnlXbWRjH8rFtFbkH(t10Jxb-@Cw0mwAjNi@#)EM8i;w2?Tr7C!cWP)~x2 z;I4lBTX=4bb_fA5SrdrL=b2jln&x{K`xg2(67pJDcJ&)bCNPEtHO~~qi7HCo)W;0u zSNSBrQm=D*16Fok)^ER)~96rE0N4I1)^L zYY@l`*^QDz?c2S#o<|x2hd*y_-3>s$8zhYfcSFblIJGYgFQ58-`giHA<5$;@UtMdz zy54fFjO$-?(!l8#!4PnbG$5CQG|*8w4^MHZ=>$2m$9Un*MgIXf@%2>MyT<+NIc6hB zxNWi~ZE6$D_OEMy*MSJBDRF-v7VF?^$!W(u>HgkE+lLZen1LO=WIvGXli|PTd>NN* z42#>my;E@Y=P+IOHE$lk3BdMeP%KlzlxQRHx(%Kmm~7DCp(0#{{&5RH6h_=_lT;VI za|d1fPSgO{NyrUKYAm8}wn!81f@dC*aq%vBNR!gU`6wWbeBCTOVBD6g??~A$6)P9H ze+gEGDmVFSVa7B)55Mv`Abo_o_g`+zP|N**^zq(nXzA5s?=R;Ux! z;0a)7Go=;55mMcb$PByoE_xNYe4iEAc#&V3IO45MT{cWjHLNQse-a&M;pO!&^5r!7 ztgYvx^S^CdKJnqv^|qd`%BbwmTUzP=+ydSMlKFS=aD+kc0{tHF|5e>9xMNS<=I&+d z*-w_V#e3_`-CyyGFncE%xsbCvsz4V?}&OIg*jJ zY>u$x1n`2l_yyoyL`X`!+a1>sZ5%r4&6LY<@*Jj_}X+5?~pFWzsG2iivTX0WB zPJEGQn!mBsQ$g14SbX}{moJ-0{UO|RVW+7L;`?dGW?RojQ_pAd9^7)_m3wV`9RGe7 z`BT@C>tXTFAqo`!n4LBezfL1O1XGG%1d3*lbHphZlATcX zk?Oj(;^Z_=;`_&S=Bx1K3ff=cw_yy+!9oVKqqH0(wDoU9{wMi10I?8zDV(GovzMN{ mHTTw5?D%@@c$vUJS%j%woZ20rO|))a+$GR0i7&BpkNOYrDjsJ5 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/inet.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/inet.cpython-312.pyc deleted file mode 100644 index 9a2fac4cd87478595abae24297be8f117730b939..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6695 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 CKW*~> diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/ipv6.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/ipv6.cpython-312.pyc deleted file mode 100644 index ddb00182122916689bca642fc0fb7acc58eaf4bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6858 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 TixgV>P}{l9Ti>G0u=;-itGxv4 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/message.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/message.cpython-312.pyc deleted file mode 100644 index 044c59ff941597730ff5da9f27065aefd26baa34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83663 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 E1)c7MB>(^b diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/namedict.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/namedict.cpython-312.pyc deleted file mode 100644 index 5265982ea0084439887c4a252fefb84aae945a98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4356 zcmb_f-ESMm5#PNJ>LijP_2ER4zfPtcg|bb;PEZH6>&7je!a&RxR$CZEiqnaAl1@62 z%-u=01j-N!1Em``CXp1B(E=8NKGg6Z&_4Aa=!@hJm^kPmK%VkuOHNVbsWW@z=_D}- z0`ve5_hxtIW_Mbx#%M&2=s1=U*m8doH#+kI`pQ729P znp!Fuh3RbGlk_OpeA&st;1BR|o#ZGOz-ctcPZN#%g64z@nF15;NI4Ndp9^Us&<-sG zRMI+tc4`vPF0B)2SnC2B(ZWEZ6>^K_y2nW};!0UCF9X&s|KoDNO8MqY(bnZTeaV(p z3+yl0Dcnd3m&?V;Sv~LAsje;=o~s2})ieY5Cp$%XQituxrZK0>s%+`g3#Mwxzglu; zV0!l$PRq`WVKez%rkqznmuVN}Y2ASZ@LgRm>9!-!t4@AKo+?_PbJ|$c3us!3i6?ot zb6&lrXnM(+abx~aF&ur~b~_Zsc8Zpsr-1|RfHAn6>{NmLnbwKpPu}IrW`18!#$VPv1{jJUZne*{AIH6Y-E zaCgQ2zg@fiBz&@w7=4jAznM7y&*9C)g-YgmMBa+Vt9_r`SQ8$0Kj?lEeQWEe zyx#Yh8{O`x6xD0kTROvA+A|yXCc?Z^q;eN%qY0^Awh#rW0+?%q47)Mg#DQIJBHqdK z3jl?ah^v@zfDc`UZ4_)rE#!5VheUJ*oNxs^DYxsls=1(Fv8pv7 zS?Cj2TtrD5&k7L2i#2eC-PRR8_kCE-G902SHn^7ftNrlSgIf<*9;`fzo!RO=vT^js zPkS$HL@qRr3|EBvk-L%V_@*=nnf^jLz9}7F=buX>2ZPD?ngGf1UBNUP^fTCjPw+H@ znA^fy@If2DGF<3~Imy!!q<{qa614_@LB5(Sm#|5Ajy|=w^KM@AP?m>=h48fR)HeFA{vb1RK!D_}e)RWKaIKMfldqjT7q#2qZaI zfUSDD0EmN-YNPsQfa4_#(*QJkE-%LVC9d^QW-HcL&D1}vefTssyde$$|E=`A$nY?= z&qSzY;9>v^;{hmSoa;dYd#mr(FV-$T4ZxreZ?F!>>==*101^rcn0A4=2-wUH&N>R~ z4q|i1jA|=@N&#b2w`+@3`b=F z;d6@1Lwhg|*G-vx&o?4$8+!<9SP_4?Tv?prprYqhH0}eVptY%=p*kPK3t~(*pj5~5 z+HXsmwLHMR{ObdCw_&r@X0ZSTpW}5j2Q)y-0JWpsEb38_DzoxcE6%I1x;WMRcuJPD zMMnpfsw0mDmY`XAb3xN>uil3~q6_ul)3V#aqBdZx$3G3)~(_9jEM zUCbL$l56r!!^Cf2~8S?IF!ztFNow^1m)#QYvogrS-j0zGoM8RT9rB73L}@!h zdM>VBeJyl|r?z`V@#J=>^uXw51GLV-%rS#Q%gbsZZBJ|W( TB=$OVmC{7z_BRConTG!XPLZ)} diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/nameserver.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/nameserver.cpython-312.pyc deleted file mode 100644 index 3ac1d766c271848c595278ce187df5b976a9c394..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14133 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 BC@ug1 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/node.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/node.cpython-312.pyc deleted file mode 100644 index 02cd8f9565df513daaea9c1a6b83c2309ee47643..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16639 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 N(idAF5P)s;{{<^0Q0o8y diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/opcode.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/opcode.cpython-312.pyc deleted file mode 100644 index c79686169ae180c3240743c9a39f868f35c476ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3130 zcmb_e&2Jk;6rc63H~x%MH%&i7H*E@aNY=E79smWRHbGHU7xEz$3u(RaP8z57u9;nj z#OlFFIgnG$Ddq+e2ul7FxFpJm)u-Ar=ECkcAePyxFGg0^WK~H#&3S__h$TO zJRTwN-JaQ6{2ioMsI~Ei7KMVGoC)oSJK5~Nn9N0%sun(*S zQhn9fz09ogu$;FHYO8zbmdSTI2YjXVDF~Z{lB@vrfI4JFBaoF0F&i|btZW3cJv3y< z>txFh$c91h>Chu~Tp#3G1FmTjz&GYvJ-HSrbx$%w7WI=j6HI zius~#qr39vetO9&(KIVlhgmCy{8AbZlzvdK^J#U3nk(sIVIf^!bCxXgef8Guv|VuM zSXs|M(H~PgZJ2i2;uxsqH8-S{^wmPCQmQ7rWYx9-%VcAgHS#iXrUgQGzL*Tp7df>_ z9CYB{EJ+EOl;Fna%_pYyjp>6BgGKKI3?tkNTE$z}=xUypoq}a*`J!&y)vN7HPH{j& z*w_a(@=xN*cIXO23TIMLIE`lNC8}v|MAJ%^Q7NJv)3mQEdeL7Aelb5e^Q9|K`}6GF z#N7O>D`y_gO;3S+{_}eibCYhA=Tf48qQS5X>?{sZ9PsD(AsE(*4;MhNCtz47FJtlA zy4Mk}dyKNSW$ycP(>;GDpGPm4fM5if;NxQ6murz7bp+ z8C7&8m$My~%Z)0|66FC$q% zPcEnOEK&hpayPultdfQ)r37{r4%b0%b`5ks+`$xp8kdIV-axBe5343Q6SMkQFk!F&_ll7U6`;9w0hn(p`22tV2{D-#t zhoG}<`0D8Y-*oL3@9w!CKnZhW?;=1*e#N_qRS+2t^SIz4$NTNs_{MnS#%@&EmK08{ zlac!n!n)h6-C7Rvj(^KY@gv_8;55I!O4D{HECti+AjNx6KJhOwUg1;dbW)2R@Co2p zu&m<29VGH*@*beekP~|ch7Lg7aKY9pWkYu;dk>ZtaQU$92I_P-dgW*N*Z5EIo#@!M zG{&(>oo8=>qZ`6olbV&1D}v<4x!QiFU5T&sOqk{DGk-~35pHXK z8@eJ~q+aUCRbDYy!jthV*?BZ#n(QJDmvG?6>wb9Ogg*A3_i^?SxF8SgKVWEzf*`ye zCc@cQB=I-t-N?poSK(H!bm;eBrr7=TR1fb!r(R4BrrAX7Ea9z5n-r)2Lp#1c?=xFK(#)L ifofw81J&jw5>7meZNzH9zd#t=7_7;s3g61S9RC7nn!t$w diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/query.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/query.cpython-312.pyc deleted file mode 100644 index 21c66e206e13f3693a9be3067f9f1676f21769e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59067 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/rcode.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/rcode.cpython-312.pyc deleted file mode 100644 index aed870c14f100b44b535e84791b901758f4dd307..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4427 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 p8K@7`+LvgH diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/rdata.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/rdata.cpython-312.pyc deleted file mode 100644 index 4d8b274f7f545d288866491b979842c7834f74e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37461 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 BF?9d{ diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/rdataset.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/rdataset.cpython-312.pyc deleted file mode 100644 index 30679f65b0a09e510cc66338f479bf57e8ab2ebc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22964 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/rdatatype.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/rdatatype.cpython-312.pyc deleted file mode 100644 index 7ee1c10ef9ec80ebc607a566545b9e0c23084905..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10114 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 J<0xfQ|39{(ftCOO diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/resolver.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/resolver.cpython-312.pyc deleted file mode 100644 index 54a32ddeaa12dfd1495ce12f361ef11b74587064..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88375 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 Ul@<`c+Ix#`i|aiiw=44h0sh%Nh5!Hn diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/reversename.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/reversename.cpython-312.pyc deleted file mode 100644 index 084a125ee4b0227ee098769f2875bdb44b4c30be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4724 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 JQ_o} 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 UNxOduVIF+`!=WeiPBrO20j(;4c>n+a diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/serial.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/serial.cpython-312.pyc deleted file mode 100644 index d0cb00c9be500f48a1961df262ec5ad0ccc45fd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5182 zcmeHLT})F~96#r_x0HTRks?&l)q#fc)xdNc&gh)tg3cz&)C3>8(tDLcY4O}!vevSg zjR%@-5uYfT3z#gC#s~08_cR|hdl;Y(O~Zr9_Trn;hb2Dk|39}a6spr~Sr+vq{hiOA z^FROZbAPp3O$18Eshj7wR1oqzcJf7^Yh>%7F-auSO9b*Mk?0tagqwmk+e@Q5mp-<; zM`i(E(CLW`^~ub6h^hU7Az2Oho#$D2$a(7MVP|{ihaLX>-dKydPWB6EOcI&&QaD49 zHc3?0NrJ39ua|UVVy|A(kC9$c5&;d80kBLe12jrTz;dY^&?K1v&5{|=B3S^fk`=H* zssOY_ZLZ3UL8CDvj`)HRIiv3jsES{gBLXXI<1gC+FiGN6qH!T+BqU}aBu>0As+G6^ zJ(}-DiA(T2Mt-COhD>94GnX!-SLERN)e!~vb~>HUj=THAL$aF<%PI?x1pNJOe^`>; zCjyG!-8LeJM%=+bpL;l}_J>37wY7J+m4GU@4*UFrz6-MAmO_eK;p}c3j%JKrZy*#< zz22BjyI)&=pbU;vY5^{jboJKc$r=6B*{j}h{dcCcy>?vxeR&qm?KV0?NEB( z!4E)l3@WmQmmW~L0dAf75v_(jONTq!dZ zp0L_IG#r$NZ?5b>@;!uA0!P_qge?H;+`LVqa36Hcy$C%=NS4oH=Elmu?T- z8dz-n_-^|>`)`i>j)l`_?r&c_e0HhvY`Ur@ZLPf0o9IpMnr@zIUb60*ZJ29HHO)tE zU%GW^vFVe$C)1WqSGp73Nt!tER3{vzrWGPIo1Pemz4lR6?MBr`|HK{oR%k$!21kv~ z?@OwPRO`WtoH?aR+fuODmayA|{6T|)0As6~h$KdrY%MeTnaFj^g0W>I75Y3CZw(AM zv8KG5QvWXuENARdJ=+c!dA?a)C^jqXw&EY02pe4R-|HzDcHZs3i(8a(`xlB;6}und z+J=&67PPXByM5p;EL&ej1r_aw7f6EeKfDyu7ArWo{NS2;R2Om-{j=ag3;)!`DXdzP z#C%OvOx<{i>u%XywFcmy9=n;s< zi(!K`BeVgm6FNT4G^ww7n(=|pU~%>FO=~323VtH@`sQueAdKQ~mcH$cuFW z9Dow(D{=t5e-3r017Sk|c{8lE6$H_d^gkg`%~oZxS}{`b;EF*MT`LtF zEHG9_#nu(W+E&9#omI52?68OpS#0qe@-sXxAp=S%h5hi61!_7#H2_x%fsPDFwL diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/set.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/set.cpython-312.pyc deleted file mode 100644 index eddd8df54388cb6775e8362bb920d9d9bf8f67c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12157 zcmd5?du$xXdEdPkmv=|r@hOVpV5CJnMzAvKT!MUa1NGH43{ zE&BUrcK4nlc1%r2h;#ym0ATTsS;qdS`8RfpHYK1eZwI&gjQG$1JLIU(-$&57UNKPR3Q-fR$r>z+Ab!tadE|Ic~u+v7#yR+CSd zB{BA7l$TSwr7KB2LwjTUx7kB1I>InJLfZtTG=ugGM#&>1L+O+@GSbiH=zIyrU{IgU z;aj6MhGO2FPt_BaL0ILyimFnNsU^pFkB?2D&0_S!^Mm8*No~-W(kvr=RZol$Ceo@l z_^NIu1_!QcsjGuYeRPns^y>p({o0_3=|`uO#1-YTW)6bggW%!7)ND?Q#|g7|JQKc% z9;YSaMdmSp8^W){LUZf;uh08#1-C-cwNTquOZ&Fqt?69!eI5|%nwF&{X{GVcYrm*M z-{)Q-+`fZ(Lc%3H8ddl)Mvb5XyCLRTWl=vXV2NX2oG$!N(A<*Zu-9Rh^PdR_!bKtG z8OlVk#R1wPHr3Z-#$vvl&(NlfoR1{cCg~Mz)?5{hYOHP&wz3fdB$Ck3JV|)#C=ELV za6{ON$Ui#vm%a}#eKfo}{84;0zJBz@^}dtqU8k~G6GS4AVLKH}M&_&kQ&61nfR3RPqBHc49QNaYarLlU&=VcRmul$)e7GT)pbp`NKC zRzt+TLQlH@ZU|syIJ#&pXO=SA(7~(nc=$x{{pMSlwu*{S@pr7ckgjnILVL*MEn`^a0SGz18ho(Z=QAQgG#1HY)$; zgUG&ZL9A|nP~Wyj`$qMlNhEu>CMqmOWp5EvT>=nez|@K?ogfmGlsQoq9ZxBOIM{aY zF58xMSZf1@H)RbB#XOt>-l_Cd`G%`hm;l94Ga=fs-FOkNH;Fck&DAxxgA2jiwF|Y2 z*2<+%C7ZK_>PjRml-9@In4_5A#R}I&SXmM3C}s}lH>Vh*x@9N^>_6s!TzzyLu*rV2^*3G)EY+cuSUC%;bzHa{X zRwOzvaRDyerpvz^<`%k-Ianx%KSmAs>WVG~+U2^K3gZc_QBtmpb7Cc{H77cD>bl3G zjj`g8Y|ayR9nzdg9;;^_lUxrhwP#9b8P!n`KBQ-2 z@bXi$6bLxV#RCqI7(s^yFAfK-$i0aqoEXF}ZKV_GWG_Z5mTXRsvLHockp#JR9-{roL<>4kLiYqCCp)-VabMOO&ci$9|(%5JEhGa;xH{lI_#C>8jpMk8TN8}+q7%lK^ye2EJH^G zgdSBJ(`i8jB8(#T77H9bi^UvD$c&06wn+AO#(==`c?jT?JB+Z-`gf7y#T!6oU8&h6Ck0s1Y z00b{-v6YUEP~TR4^JaZlw!UlO%xCq@%L7XT^Jlhd>uz6JxU$lyxx%`3=@4GY3~r%1qmPQCP#1+& zF>`QFsfWWnf>}mAKuHup(wdI~cWowID%SsmPn=Z z`lBQbO;O=GY!wAPT%~x}#%E4W!?ui)r31NKlQ0zX=Yl%4iCYMR=w$3CKonq6(W>oj z{A?W_IeGDr^p?eiVWy*Tt}B@+aig$j9AnLI06-UlLbP?I=SK}2ksb;q+E1+4Kfe}! zek&ANe0Jf5t#JKTwZa37roRsGIK2IJKxo>} zP1$;5_qO1v?g5J<4J*&yJ#pv6Cy~QjjV;R;mM*MV*~acK{CK32J3eb^zZ<#}f`-?0E&Aw`5nc)- z-i{uEiq~}Q!t$Tvb^k{!7bYq()pZRlZBi);bw?oL6ec3rKuIYS3}z6XFBb|+K_&t| zA)P`_$aM-Zv2XKCR9^9I&P$azZ=ArZbKycIHl9P5L5DOu!ZGL&n5)7u(?}{)#dhI< zIXj8C)6f&~GHJ+kl922&U=iz@ZvsGE0(SzRWz6f9T_PIo%uR^mZ?l?%sE@LQ}EN&iS@8}-k{?o z$bvFrob8tx&x{y4iuvVdNMx#sGpzan4WR%L5n5P?G*Hy}-sHR&k)|DGHf>0q1ra7i zl}`EJBIFNuCuE_tmeSLWaGoMve-HaLC{`(=r9p`T9@rEj^cOVFCTMLbK|8Zj=RN<1 zbojSS(A7r})Rn}ec{%Kt^#L3TNn$Cjz|+ul9z!(YjxUNhae_0`{P9zhWwJlzG|vU| zRwh$RLY!~^WH-XMXC<-?MQ5eoh9v*$5rpq^DOo`Im^>%rflWhKl4%4Pj2Jkh9nUj{ zD6Zk$j01qX8cU?x3vlb>E<;xhI3BR;3iuB9?5-PFC?DKCxfwcDnRShoJVB;~m{{ov zg>sG_MN9RlH!H#PZ%6}s=}|XUd6b3EXPD^lOS#}P7`*l>I{BXTw(-)mZr^sY(0PYWaC6|7$wAGHu4YWyP&9U@S6^mbjxmn8?K&nvzj zd7eBuSR;#}W}ZFoEWEQay&maVlPVk`eq($nhNy_oH6|@Kdst(G@F`$VUpCVBA=opx zCJpXFp*~F8TZSo?yeKfCfh4mSu9DE`g;=r1CYIl60)P-p;Z13OR@%SPvmwb4>G{yp z#0$dATxl6zbgjmv*|}C@t$!wZ6<{W3C5ZEe)ce#iGw4dRoQn)ECelY=&>BveQE7KcN&r>H3Fgrz?t%w_}Yl@7J+)wgk z2nC=lPU2q`@W2Q;?&*>g9(&*}HmS_Av>Ot7n9MK1DZtMM^+O=L1cH>G$BkZeb2r3a z@+-7WWS*eGpGSqJ)?M>tg%l0s4x?Q|I$+F@N?fDWM9WMu)HzR1?Li3o1C&{ z-K#ft!Ys|vvQB;qVVQ@2$9g8^lyN5|h3UqPA3i>nTFZ6J#oCsaiuY~Ez?E(U?JH1A z+ejK@SjuG@US4}U@8zlf5Ca`EWPAB<-Tls;@4$MmNqyz`xvu0eChl#x!wyDZ_}HUx zN@;nVsQzDsQzHP3cEi5q6H6!H+8TP+3cOn(qg|Bx8A|!@36Os%41@8M%6)sTO{$nD z=S^xUo*OL9{38&-v%KeJ4FL<7lux`NJc!gU9$P-Xbo{+{)}#(jt*43_ed}RV#*OSZ z9s)VXK(UsWu;X#TgGj^TSxPUiM>^M}&T?jw3NjU`#iYIROvcOkD}sE|xKb(@bPp1x ziwlG{T--w~^ww7FB?6a}`##>0=b%La2P?lBMs)k0!TkF~1^Ty{$G#l-B{gC}mRZXA zO?-u<8H81?n&@t&)3_Q7=)9jN8gZjwSol=ON>35g08`pE3n>gru;xU)D9R1RDb>Ye?RtjW9u!it~U&=g@^vNeC)^K$ESblUvGJFz2W3q_++^}5Pg|E{1BBr z<>6pn9_X5gu23BM;j#dkKb9j5pu427>wr332T+Ev(sO$?d@RCazL<}v_uj@rj0H?1 zoDAMI^ya);(kjYeJ1@O|opQC^i(V%^Z!Y~I zrE0tRS5E93k$d8u=d#h=lDBhMelH?E`rfS)VL(<`C`#HvZ7L9s-@#DE9>n#=e3^4E z=TmC3LU;A#!{F3ye}6K6(eI??6r8t5A={L*qr?ammSjD5Y1L)}iAlkYS$R%f!Y7C9 zRopZ=tp+0cX-0H>Nmlt?}&hbwoJ^znMjeGf9 z^?3mwvL$mD3gN@2i8OLR4uzcdQKHaECX5dvI1`rN&^q_B4t@A1ai2sEmeYr)Gd%Q> z8GVbw?j2*#(C33GWVrFAMKB(pOsmsLs)yt8x2Kh){RAO~nogh_-({G|v}NLR4Dyo( zxi%gow^EH=U$s$2&3K= diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/tokenizer.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/tokenizer.cpython-312.pyc deleted file mode 100644 index b5f268c3fa1f50687b0fc136c6cff04f00133227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26544 zcmeHwdvH|Ondj}NT2f2hl3EWWfv#Sl1|jh<7z4o(NEl%wVk2Tlq-I~PwiIiwK@0nx#yhwopZkLJKyVE{g0B8A`aKKSMCfqyv}jIq95wz zP%?`dWUg`oH^2!N!5Xp*SlHb_4;&xCO^4%Rq@>87LK;10JDZz$>^0%7ns!a-nFTLMR^aS-BTD!TlB|l!%2R z8�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 TrT-MOzgb}E_zg!XV`={f8OX;V diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/transaction.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/transaction.cpython-312.pyc deleted file mode 100644 index c1042b4d45a4488ea2f79e731fee5a65eea5ea96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29402 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/tsig.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/tsig.cpython-312.pyc deleted file mode 100644 index 61eb53568a5b949f615f48424e7241346badd2c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16756 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 CLzu$= diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/ttl.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/ttl.cpython-312.pyc deleted file mode 100644 index 4db99ca6a55dcf38adcbc3db1717ce20cf3c4fc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2443 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/versioned.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/versioned.cpython-312.pyc deleted file mode 100644 index 2f73cd0f94acc8230b9ad3f9b4fbf4e2b38add3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14685 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ᄝcS9~gDsz@u#AF1^-zsOo#pX!z^jZ?ZcC8@HlMAS%G q^<>3@icPT9VPqAK-xk9}RL-ZI0p|1%%B}`Rm*rL!6CNOJ7b8w zrb3911Qn5JrM1}~!WW59RaRPQ`Rww$KM7umbU@ll{aOAD{#aD?W6!xW;KNP);i<|fF9(Q_jljQI&ZDU1k7 zaYRhoMr@q%%|6n^c%_jhp8S%?!VMye4|rp>5l5B_*mZrLNc2Y~E zQ?Uu%c0MyXp~MY-1KP|%CCK4X6oT82@bU(kQv{h;#8DBpfg{Kw;Jj?RA&%H(yCT9^ zfNxDOZjv|3QkCKWZi#*639RTsI}Avrg(-@C8foMnhg=hUu7CVf#kV7iB><$I06wHO|!&K z@?_j1*7OfdPMA~OA_DbvZJTxDc7lY8TU~ec?TOBWglZ`X*!XOR+IUjmRlAf}#sgW_2kVO{5Z9G@5f8 zQVm-K7i^}!1K=822?VFbFB~f!*twqj+h?^xb}sveK^RKkIDIQF&+Va^q1my2xH?yY zTc*WZEfttZbURhF3{AzW!UiHrfT^Y*5Kv7+p##-)C19$y9Juj0vd9gGc-jI~Cc#CH zdH`2(k1py3s5_{VjKQ^1V>yp`t12acylnvBT5kXCshO!_&)wcTz0236UY{~@`~p>>VdUJ;nFSq6UTI2?7OR$Xr#S6kD@?P~}y zgq6uSTFRORR2I+b$#0-?IbhpJu7WL2aSi8jIgn+XU$s;XWy>0@*=pgdedC?1*atS> zWbx+tbA&>~B_W9hq08*9#T6C@b;pS`O@2UWny#9;E+k`9)Qz(Qrdo>8Mz^Ok z+GIwvoWRudn4(25WHm*tYPw44fLW>vfN6U6vzyr^DL9>7ak^)F3gNkM$@lgX=iASH zZH2zMzNfy>vM;pc3axnC3LEA&6#MTUx^rlu=d0eu-baDuU5A&vgVXjE$#vUx(^V9g zrOmfo&x4za$M2rGb7rCct3!*2O2Gl}hn5w0+jNUzM^5nVG3{b>ojGApVCIwpq(C%) zMjJ=!ng-&o29ecIdCm$nGG~b%mE4fG8kEkF5I?L77ZPKOf-WRdn%+E0(@8^wv|b-nxO zlalus2o>D$^O?_!qb2vYC21QIU0uXLcVW_c9}pHX8aNx=yn*a27qnyr=GYF4K>X-} zj@cw-c7iLK(W3=#QH>Y5K+WB%)E;10(V~qgg2$KdrtYLlo#B!@vLr>;-vS++-Qr_F zKUYXq8e#Zu4>ZKxh$Y}fL_Jn9N&HP1uo073@F&HYBUqqbbBU+!QZ&^+Uq*- zx*0HqfjSprlpU~Gr5gaK&)s(W>de*R`T73)gZBm>c9*vHm)!3>j4itdmZX87>Aojl zU{q!IrG1F4y`WMbfwG}n5uhKuS?2u%>ki#Nup6eRn*pFr`IzT3 z*ID?e)ERl$R&pO$k`6F^==M>_4l%7R6JtJPZbSk3Z*IggbA#Yn<9rSB?rLbY1h9I2 zBYcQ747<|^f-V5M6pu~BG0a0`!D@uQ3qVDob(v9x8KU8jbwV*n`;dJbfK^BP_5At! zpWgfQFDL$b_RnX(K2hpFRoZsCkGZX!Lz)z0=sLwbOIx((NEmAGQe&p>1U>Flrb5Gqnm)$!T0ws5N zTKvZ8UGaAm-kW=Get*foYx+3knrWwDT(&D06Nj9UbMX^zI5&9Mad?#3T^#S0uc15v1%Q?-+hA)Ui$bpgJ8Y$a}%R9U0 z=E0&3(ynw*dJ^2ZuHNa`4E4Ip2!ORlfrr})zshNd0;dRvnfGTcd#~~id@VF8eOZxP zD^QvIkuPxY1Z&LSu&lmx0A&4>%5*pvKJ{wUxpfWY3YijqORjKV3RgIzAhsLD#~M{* z zPR^a2c9eOCqkE-eQz1W>pO=?Az=ggz`OM`lOT-hnouA1UwYyVyrk=QWJa6AnNX{jn zw)ZW!_bqw)$^!B11Op6qPanT^^7U*MyC2joaGL8!xT8JX-^3%%qe1f6Bf;Qt&~da| zc-$=jUYCZeRw%dnz;GA(B&5O+nRpJieb z<`)}#8&S+G)Q13TR-+Ewi5M$@FeB4`1mFRL{sMq5fMHS0oOBQYs(~Iyfbm9ej;Epe zpVHuO2EEn9ld%;1yFe)hP&$O*6o5sev|_I{ikX7}d=jcHprsq+C2zNN6+bBx=oZdZ zuq%5w+m7PC3W2U{Z?d%)<7EQfyi&pLrH{95UpQAL&^>CaU{{tn+wNipXY4N9Iom;F z_CW(bhV*s#K#|c0kY|{i_m_cbm|K7av43e7ZGqy{f_r((K$!sk_1Ow`WtYwNR&i^Y zKsVo0!LHm&?4GB#&ShI?nQygq&kJP&-GcNAyNW>UTNu6ERl_IRLi0Oc!HOZq@RA3N z#b73%P&54qWrbCSZr8Gt@K=T*Sf&Wni_Wrs^(AejTFI&e3{juMHT(#?0KS(F8CEh# se*;7G5ms`P1&-q?cEb6dk(Pgw-euDJ9SJ-m?tj~dIWF{yAY{w@2X}A?=Kufz diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/xfr.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/xfr.cpython-312.pyc deleted file mode 100644 index 74c8164d234e6f396bd3f184e874c14f65e771f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14488 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 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/zone.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/zone.cpython-312.pyc deleted file mode 100644 index 3891ae027edc367c30e0bfbf7004ef35866b6c0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67615 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 0: - infos.append(_SVCBInfo(bootstrap_address, port, host, nameservers)) - return infos - - -def _get_nameservers_sync(answer, lifetime): - """Return a list of TLS-validated resolver nameservers extracted from an SVCB - answer.""" - nameservers = [] - infos = _extract_nameservers_from_svcb(answer) - for info in infos: - try: - if info.ddr_tls_check_sync(lifetime): - nameservers.extend(info.nameservers) - except Exception: - pass - return nameservers - - -async def _get_nameservers_async(answer, lifetime): - """Return a list of TLS-validated resolver nameservers extracted from an SVCB - answer.""" - nameservers = [] - infos = _extract_nameservers_from_svcb(answer) - for info in infos: - try: - if await info.ddr_tls_check_async(lifetime): - nameservers.extend(info.nameservers) - except Exception: - pass - return nameservers diff --git a/.venv/lib/python3.12/site-packages/dns/_features.py b/.venv/lib/python3.12/site-packages/dns/_features.py deleted file mode 100644 index 03ccaa7..0000000 --- a/.venv/lib/python3.12/site-packages/dns/_features.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import importlib.metadata -import itertools -import string -from typing import Dict, List, Tuple - - -def _tuple_from_text(version: str) -> Tuple: - text_parts = version.split(".") - int_parts = [] - for text_part in text_parts: - digit_prefix = "".join( - itertools.takewhile(lambda x: x in string.digits, text_part) - ) - try: - int_parts.append(int(digit_prefix)) - except Exception: - break - return tuple(int_parts) - - -def _version_check( - requirement: str, -) -> bool: - """Is the requirement fulfilled? - - The requirement must be of the form - - package>=version - """ - package, minimum = requirement.split(">=") - try: - version = importlib.metadata.version(package) - except Exception: - return False - t_version = _tuple_from_text(version) - t_minimum = _tuple_from_text(minimum) - if t_version < t_minimum: - return False - return True - - -_cache: Dict[str, bool] = {} - - -def have(feature: str) -> bool: - """Is *feature* available? - - This tests if all optional packages needed for the - feature are available and recent enough. - - Returns ``True`` if the feature is available, - and ``False`` if it is not or if metadata is - missing. - """ - value = _cache.get(feature) - if value is not None: - return value - requirements = _requirements.get(feature) - if requirements is None: - # we make a cache entry here for consistency not performance - _cache[feature] = False - return False - ok = True - for requirement in requirements: - if not _version_check(requirement): - ok = False - break - _cache[feature] = ok - return ok - - -def force(feature: str, enabled: bool) -> None: - """Force the status of *feature* to be *enabled*. - - This method is provided as a workaround for any cases - where importlib.metadata is ineffective, or for testing. - """ - _cache[feature] = enabled - - -_requirements: Dict[str, List[str]] = { - ### BEGIN generated requirements - "dnssec": ["cryptography>=41"], - "doh": ["httpcore>=1.0.0", "httpx>=0.26.0", "h2>=4.1.0"], - "doq": ["aioquic>=0.9.25"], - "idna": ["idna>=3.6"], - "trio": ["trio>=0.23"], - "wmi": ["wmi>=1.5.1"], - ### END generated requirements -} diff --git a/.venv/lib/python3.12/site-packages/dns/_immutable_ctx.py b/.venv/lib/python3.12/site-packages/dns/_immutable_ctx.py deleted file mode 100644 index ae7a33b..0000000 --- a/.venv/lib/python3.12/site-packages/dns/_immutable_ctx.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# This implementation of the immutable decorator requires python >= -# 3.7, and is significantly more storage efficient when making classes -# with slots immutable. It's also faster. - -import contextvars -import inspect - -_in__init__ = contextvars.ContextVar("_immutable_in__init__", default=False) - - -class _Immutable: - """Immutable mixin class""" - - # We set slots to the empty list to say "we don't have any attributes". - # We do this so that if we're mixed in with a class with __slots__, we - # don't cause a __dict__ to be added which would waste space. - - __slots__ = () - - def __setattr__(self, name, value): - if _in__init__.get() is not self: - raise TypeError("object doesn't support attribute assignment") - else: - super().__setattr__(name, value) - - def __delattr__(self, name): - if _in__init__.get() is not self: - raise TypeError("object doesn't support attribute assignment") - else: - super().__delattr__(name) - - -def _immutable_init(f): - def nf(*args, **kwargs): - previous = _in__init__.set(args[0]) - try: - # call the actual __init__ - f(*args, **kwargs) - finally: - _in__init__.reset(previous) - - nf.__signature__ = inspect.signature(f) - return nf - - -def immutable(cls): - if _Immutable in cls.__mro__: - # Some ancestor already has the mixin, so just make sure we keep - # following the __init__ protocol. - cls.__init__ = _immutable_init(cls.__init__) - if hasattr(cls, "__setstate__"): - cls.__setstate__ = _immutable_init(cls.__setstate__) - ncls = cls - else: - # Mixin the Immutable class and follow the __init__ protocol. - class ncls(_Immutable, cls): - # We have to do the __slots__ declaration here too! - __slots__ = () - - @_immutable_init - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if hasattr(cls, "__setstate__"): - - @_immutable_init - def __setstate__(self, *args, **kwargs): - super().__setstate__(*args, **kwargs) - - # make ncls have the same name and module as cls - ncls.__name__ = cls.__name__ - ncls.__qualname__ = cls.__qualname__ - ncls.__module__ = cls.__module__ - return ncls diff --git a/.venv/lib/python3.12/site-packages/dns/_trio_backend.py b/.venv/lib/python3.12/site-packages/dns/_trio_backend.py deleted file mode 100644 index 398e327..0000000 --- a/.venv/lib/python3.12/site-packages/dns/_trio_backend.py +++ /dev/null @@ -1,250 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""trio async I/O library query support""" - -import socket - -import trio -import trio.socket # type: ignore - -import dns._asyncbackend -import dns._features -import dns.exception -import dns.inet - -if not dns._features.have("trio"): - raise ImportError("trio not found or too old") - - -def _maybe_timeout(timeout): - if timeout is not None: - return trio.move_on_after(timeout) - else: - return dns._asyncbackend.NullContext() - - -# for brevity -_lltuple = dns.inet.low_level_address_tuple - -# pylint: disable=redefined-outer-name - - -class DatagramSocket(dns._asyncbackend.DatagramSocket): - def __init__(self, socket): - super().__init__(socket.family) - self.socket = socket - - async def sendto(self, what, destination, timeout): - with _maybe_timeout(timeout): - return await self.socket.sendto(what, destination) - raise dns.exception.Timeout( - timeout=timeout - ) # pragma: no cover lgtm[py/unreachable-statement] - - async def recvfrom(self, size, timeout): - with _maybe_timeout(timeout): - return await self.socket.recvfrom(size) - raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] - - async def close(self): - self.socket.close() - - async def getpeername(self): - return self.socket.getpeername() - - async def getsockname(self): - return self.socket.getsockname() - - async def getpeercert(self, timeout): - raise NotImplementedError - - -class StreamSocket(dns._asyncbackend.StreamSocket): - def __init__(self, family, stream, tls=False): - self.family = family - self.stream = stream - self.tls = tls - - async def sendall(self, what, timeout): - with _maybe_timeout(timeout): - return await self.stream.send_all(what) - raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] - - async def recv(self, size, timeout): - with _maybe_timeout(timeout): - return await self.stream.receive_some(size) - raise dns.exception.Timeout(timeout=timeout) # lgtm[py/unreachable-statement] - - async def close(self): - await self.stream.aclose() - - async def getpeername(self): - if self.tls: - return self.stream.transport_stream.socket.getpeername() - else: - return self.stream.socket.getpeername() - - async def getsockname(self): - if self.tls: - return self.stream.transport_stream.socket.getsockname() - else: - return self.stream.socket.getsockname() - - async def getpeercert(self, timeout): - if self.tls: - with _maybe_timeout(timeout): - await self.stream.do_handshake() - return self.stream.getpeercert() - else: - raise NotImplementedError - - -if dns._features.have("doh"): - import httpcore - import httpcore._backends.trio - import httpx - - _CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend - _CoreTrioStream = httpcore._backends.trio.TrioStream - - from dns.query import _compute_times, _expiration_for_this_attempt, _remaining - - class _NetworkBackend(_CoreAsyncNetworkBackend): - def __init__(self, resolver, local_port, bootstrap_address, family): - super().__init__() - self._local_port = local_port - self._resolver = resolver - self._bootstrap_address = bootstrap_address - self._family = family - - async def connect_tcp( - self, host, port, timeout, local_address, socket_options=None - ): # pylint: disable=signature-differs - addresses = [] - _, expiration = _compute_times(timeout) - if dns.inet.is_address(host): - addresses.append(host) - elif self._bootstrap_address is not None: - addresses.append(self._bootstrap_address) - else: - timeout = _remaining(expiration) - family = self._family - if local_address: - family = dns.inet.af_for_address(local_address) - answers = await self._resolver.resolve_name( - host, family=family, lifetime=timeout - ) - addresses = answers.addresses() - for address in addresses: - try: - af = dns.inet.af_for_address(address) - if local_address is not None or self._local_port != 0: - source = (local_address, self._local_port) - else: - source = None - destination = (address, port) - attempt_expiration = _expiration_for_this_attempt(2.0, expiration) - timeout = _remaining(attempt_expiration) - sock = await Backend().make_socket( - af, socket.SOCK_STREAM, 0, source, destination, timeout - ) - return _CoreTrioStream(sock.stream) - except Exception: - continue - raise httpcore.ConnectError - - async def connect_unix_socket( - self, path, timeout, socket_options=None - ): # pylint: disable=signature-differs - raise NotImplementedError - - async def sleep(self, seconds): # pylint: disable=signature-differs - await trio.sleep(seconds) - - class _HTTPTransport(httpx.AsyncHTTPTransport): - def __init__( - self, - *args, - local_port=0, - bootstrap_address=None, - resolver=None, - family=socket.AF_UNSPEC, - **kwargs, - ): - if resolver is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.asyncresolver - - resolver = dns.asyncresolver.Resolver() - super().__init__(*args, **kwargs) - self._pool._network_backend = _NetworkBackend( - resolver, local_port, bootstrap_address, family - ) - -else: - _HTTPTransport = dns._asyncbackend.NullTransport # type: ignore - - -class Backend(dns._asyncbackend.Backend): - def name(self): - return "trio" - - async def make_socket( - self, - af, - socktype, - proto=0, - source=None, - destination=None, - timeout=None, - ssl_context=None, - server_hostname=None, - ): - s = trio.socket.socket(af, socktype, proto) - stream = None - try: - if source: - await s.bind(_lltuple(source, af)) - if socktype == socket.SOCK_STREAM: - connected = False - with _maybe_timeout(timeout): - await s.connect(_lltuple(destination, af)) - connected = True - if not connected: - raise dns.exception.Timeout( - timeout=timeout - ) # lgtm[py/unreachable-statement] - except Exception: # pragma: no cover - s.close() - raise - if socktype == socket.SOCK_DGRAM: - return DatagramSocket(s) - elif socktype == socket.SOCK_STREAM: - stream = trio.SocketStream(s) - tls = False - if ssl_context: - tls = True - try: - stream = trio.SSLStream( - stream, ssl_context, server_hostname=server_hostname - ) - except Exception: # pragma: no cover - await stream.aclose() - raise - return StreamSocket(af, stream, tls) - raise NotImplementedError( - "unsupported socket " + f"type {socktype}" - ) # pragma: no cover - - async def sleep(self, interval): - await trio.sleep(interval) - - def get_transport_class(self): - return _HTTPTransport - - async def wait_for(self, awaitable, timeout): - with _maybe_timeout(timeout): - return await awaitable - raise dns.exception.Timeout( - timeout=timeout - ) # pragma: no cover lgtm[py/unreachable-statement] diff --git a/.venv/lib/python3.12/site-packages/dns/asyncbackend.py b/.venv/lib/python3.12/site-packages/dns/asyncbackend.py deleted file mode 100644 index 0ec58b0..0000000 --- a/.venv/lib/python3.12/site-packages/dns/asyncbackend.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -from typing import Dict - -import dns.exception - -# pylint: disable=unused-import -from dns._asyncbackend import ( # noqa: F401 lgtm[py/unused-import] - Backend, - DatagramSocket, - Socket, - StreamSocket, -) - -# pylint: enable=unused-import - -_default_backend = None - -_backends: Dict[str, Backend] = {} - -# Allow sniffio import to be disabled for testing purposes -_no_sniffio = False - - -class AsyncLibraryNotFoundError(dns.exception.DNSException): - pass - - -def get_backend(name: str) -> Backend: - """Get the specified asynchronous backend. - - *name*, a ``str``, the name of the backend. Currently the "trio" - and "asyncio" backends are available. - - Raises NotImplementedError if an unknown backend name is specified. - """ - # pylint: disable=import-outside-toplevel,redefined-outer-name - backend = _backends.get(name) - if backend: - return backend - if name == "trio": - import dns._trio_backend - - backend = dns._trio_backend.Backend() - elif name == "asyncio": - import dns._asyncio_backend - - backend = dns._asyncio_backend.Backend() - else: - raise NotImplementedError(f"unimplemented async backend {name}") - _backends[name] = backend - return backend - - -def sniff() -> str: - """Attempt to determine the in-use asynchronous I/O library by using - the ``sniffio`` module if it is available. - - Returns the name of the library, or raises AsyncLibraryNotFoundError - if the library cannot be determined. - """ - # pylint: disable=import-outside-toplevel - try: - if _no_sniffio: - raise ImportError - import sniffio - - try: - return sniffio.current_async_library() - except sniffio.AsyncLibraryNotFoundError: - raise AsyncLibraryNotFoundError("sniffio cannot determine async library") - except ImportError: - import asyncio - - try: - asyncio.get_running_loop() - return "asyncio" - except RuntimeError: - raise AsyncLibraryNotFoundError("no async library detected") - - -def get_default_backend() -> Backend: - """Get the default backend, initializing it if necessary.""" - if _default_backend: - return _default_backend - - return set_default_backend(sniff()) - - -def set_default_backend(name: str) -> Backend: - """Set the default backend. - - It's not normally necessary to call this method, as - ``get_default_backend()`` will initialize the backend - appropriately in many cases. If ``sniffio`` is not installed, or - in testing situations, this function allows the backend to be set - explicitly. - """ - global _default_backend - _default_backend = get_backend(name) - return _default_backend diff --git a/.venv/lib/python3.12/site-packages/dns/asyncquery.py b/.venv/lib/python3.12/site-packages/dns/asyncquery.py deleted file mode 100644 index 4d9ab9a..0000000 --- a/.venv/lib/python3.12/site-packages/dns/asyncquery.py +++ /dev/null @@ -1,780 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Talk to a DNS server.""" - -import base64 -import contextlib -import socket -import struct -import time -from typing import Any, Dict, Optional, Tuple, Union - -import dns.asyncbackend -import dns.exception -import dns.inet -import dns.message -import dns.name -import dns.quic -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.transaction -from dns._asyncbackend import NullContext -from dns.query import ( - BadResponse, - NoDOH, - NoDOQ, - UDPMode, - _compute_times, - _make_dot_ssl_context, - _matches_destination, - _remaining, - have_doh, - ssl, -) - -if have_doh: - import httpx - -# for brevity -_lltuple = dns.inet.low_level_address_tuple - - -def _source_tuple(af, address, port): - # Make a high level source tuple, or return None if address and port - # are both None - if address or port: - if address is None: - if af == socket.AF_INET: - address = "0.0.0.0" - elif af == socket.AF_INET6: - address = "::" - else: - raise NotImplementedError(f"unknown address family {af}") - return (address, port) - else: - return None - - -def _timeout(expiration, now=None): - if expiration is not None: - if not now: - now = time.time() - return max(expiration - now, 0) - else: - return None - - -async def send_udp( - sock: dns.asyncbackend.DatagramSocket, - what: Union[dns.message.Message, bytes], - destination: Any, - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified UDP socket. - - *sock*, a ``dns.asyncbackend.DatagramSocket``. - - *what*, a ``bytes`` or ``dns.message.Message``, the message to send. - - *destination*, a destination tuple appropriate for the address family - of the socket, specifying where to send the query. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. The expiration value is meaningless for the asyncio backend, as - asyncio's transport sendto() never blocks. - - Returns an ``(int, float)`` tuple of bytes sent and the sent time. - """ - - if isinstance(what, dns.message.Message): - what = what.to_wire() - sent_time = time.time() - n = await sock.sendto(what, destination, _timeout(expiration, sent_time)) - return (n, sent_time) - - -async def receive_udp( - sock: dns.asyncbackend.DatagramSocket, - destination: Optional[Any] = None, - expiration: Optional[float] = None, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - ignore_errors: bool = False, - query: Optional[dns.message.Message] = None, -) -> Any: - """Read a DNS message from a UDP socket. - - *sock*, a ``dns.asyncbackend.DatagramSocket``. - - See :py:func:`dns.query.receive_udp()` for the documentation of the other - parameters, and exceptions. - - Returns a ``(dns.message.Message, float, tuple)`` tuple of the received message, the - received time, and the address where the message arrived from. - """ - - wire = b"" - while True: - (wire, from_address) = await sock.recvfrom(65535, _timeout(expiration)) - if not _matches_destination( - sock.family, from_address, destination, ignore_unexpected - ): - continue - received_time = time.time() - try: - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - raise_on_truncation=raise_on_truncation, - ) - except dns.message.Truncated as e: - # See the comment in query.py for details. - if ( - ignore_errors - and query is not None - and not query.is_response(e.message()) - ): - continue - else: - raise - except Exception: - if ignore_errors: - continue - else: - raise - if ignore_errors and query is not None and not query.is_response(r): - continue - return (r, received_time, from_address) - - -async def udp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - sock: Optional[dns.asyncbackend.DatagramSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ignore_errors: bool = False, -) -> dns.message.Message: - """Return the response obtained after sending a query via UDP. - - *sock*, a ``dns.asyncbackend.DatagramSocket``, or ``None``, - the socket to use for the query. If ``None``, the default, a - socket is created. Note that if a socket is provided, the - *source*, *source_port*, and *backend* are ignored. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.udp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - af = dns.inet.af_for_address(where) - destination = _lltuple((where, port), af) - if sock: - cm: contextlib.AbstractAsyncContextManager = NullContext(sock) - else: - if not backend: - backend = dns.asyncbackend.get_default_backend() - stuple = _source_tuple(af, source, source_port) - if backend.datagram_connection_required(): - dtuple = (where, port) - else: - dtuple = None - cm = await backend.make_socket(af, socket.SOCK_DGRAM, 0, stuple, dtuple) - async with cm as s: - await send_udp(s, wire, destination, expiration) - (r, received_time, _) = await receive_udp( - s, - destination, - expiration, - ignore_unexpected, - one_rr_per_rrset, - q.keyring, - q.mac, - ignore_trailing, - raise_on_truncation, - ignore_errors, - q, - ) - r.time = received_time - begin_time - # We don't need to check q.is_response() if we are in ignore_errors mode - # as receive_udp() will have checked it. - if not (ignore_errors or q.is_response(r)): - raise BadResponse - return r - - -async def udp_with_fallback( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - udp_sock: Optional[dns.asyncbackend.DatagramSocket] = None, - tcp_sock: Optional[dns.asyncbackend.StreamSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ignore_errors: bool = False, -) -> Tuple[dns.message.Message, bool]: - """Return the response to the query, trying UDP first and falling back - to TCP if UDP results in a truncated response. - - *udp_sock*, a ``dns.asyncbackend.DatagramSocket``, or ``None``, - the socket to use for the UDP query. If ``None``, the default, a - socket is created. Note that if a socket is provided the *source*, - *source_port*, and *backend* are ignored for the UDP query. - - *tcp_sock*, a ``dns.asyncbackend.StreamSocket``, or ``None``, the - socket to use for the TCP query. If ``None``, the default, a - socket is created. Note that if a socket is provided *where*, - *source*, *source_port*, and *backend* are ignored for the TCP query. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.udp_with_fallback()` for the documentation - of the other parameters, exceptions, and return type of this - method. - """ - try: - response = await udp( - q, - where, - timeout, - port, - source, - source_port, - ignore_unexpected, - one_rr_per_rrset, - ignore_trailing, - True, - udp_sock, - backend, - ignore_errors, - ) - return (response, False) - except dns.message.Truncated: - response = await tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - tcp_sock, - backend, - ) - return (response, True) - - -async def send_tcp( - sock: dns.asyncbackend.StreamSocket, - what: Union[dns.message.Message, bytes], - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified TCP socket. - - *sock*, a ``dns.asyncbackend.StreamSocket``. - - See :py:func:`dns.query.send_tcp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - if isinstance(what, dns.message.Message): - tcpmsg = what.to_wire(prepend_length=True) - else: - # copying the wire into tcpmsg is inefficient, but lets us - # avoid writev() or doing a short write that would get pushed - # onto the net - tcpmsg = len(what).to_bytes(2, "big") + what - sent_time = time.time() - await sock.sendall(tcpmsg, _timeout(expiration, sent_time)) - return (len(tcpmsg), sent_time) - - -async def _read_exactly(sock, count, expiration): - """Read the specified number of bytes from stream. Keep trying until we - either get the desired amount, or we hit EOF. - """ - s = b"" - while count > 0: - n = await sock.recv(count, _timeout(expiration)) - if n == b"": - raise EOFError - count = count - len(n) - s = s + n - return s - - -async def receive_tcp( - sock: dns.asyncbackend.StreamSocket, - expiration: Optional[float] = None, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, -) -> Tuple[dns.message.Message, float]: - """Read a DNS message from a TCP socket. - - *sock*, a ``dns.asyncbackend.StreamSocket``. - - See :py:func:`dns.query.receive_tcp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - ldata = await _read_exactly(sock, 2, expiration) - (l,) = struct.unpack("!H", ldata) - wire = await _read_exactly(sock, l, expiration) - received_time = time.time() - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - return (r, received_time) - - -async def tcp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[dns.asyncbackend.StreamSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> dns.message.Message: - """Return the response obtained after sending a query via TCP. - - *sock*, a ``dns.asyncbacket.StreamSocket``, or ``None``, the - socket to use for the query. If ``None``, the default, a socket - is created. Note that if a socket is provided - *where*, *port*, *source*, *source_port*, and *backend* are ignored. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.tcp()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - if sock: - # Verify that the socket is connected, as if it's not connected, - # it's not writable, and the polling in send_tcp() will time out or - # hang forever. - await sock.getpeername() - cm: contextlib.AbstractAsyncContextManager = NullContext(sock) - else: - # These are simple (address, port) pairs, not family-dependent tuples - # you pass to low-level socket code. - af = dns.inet.af_for_address(where) - stuple = _source_tuple(af, source, source_port) - dtuple = (where, port) - if not backend: - backend = dns.asyncbackend.get_default_backend() - cm = await backend.make_socket( - af, socket.SOCK_STREAM, 0, stuple, dtuple, timeout - ) - async with cm as s: - await send_tcp(s, wire, expiration) - (r, received_time) = await receive_tcp( - s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing - ) - r.time = received_time - begin_time - if not q.is_response(r): - raise BadResponse - return r - - -async def tls( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[dns.asyncbackend.StreamSocket] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ssl_context: Optional[ssl.SSLContext] = None, - server_hostname: Optional[str] = None, - verify: Union[bool, str] = True, -) -> dns.message.Message: - """Return the response obtained after sending a query via TLS. - - *sock*, an ``asyncbackend.StreamSocket``, or ``None``, the socket - to use for the query. If ``None``, the default, a socket is - created. Note that if a socket is provided, it must be a - connected SSL stream socket, and *where*, *port*, - *source*, *source_port*, *backend*, *ssl_context*, and *server_hostname* - are ignored. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.tls()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - (begin_time, expiration) = _compute_times(timeout) - if sock: - cm: contextlib.AbstractAsyncContextManager = NullContext(sock) - else: - if ssl_context is None: - ssl_context = _make_dot_ssl_context(server_hostname, verify) - af = dns.inet.af_for_address(where) - stuple = _source_tuple(af, source, source_port) - dtuple = (where, port) - if not backend: - backend = dns.asyncbackend.get_default_backend() - cm = await backend.make_socket( - af, - socket.SOCK_STREAM, - 0, - stuple, - dtuple, - timeout, - ssl_context, - server_hostname, - ) - async with cm as s: - timeout = _timeout(expiration) - response = await tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - s, - backend, - ) - end_time = time.time() - response.time = end_time - begin_time - return response - - -async def https( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 443, - source: Optional[str] = None, - source_port: int = 0, # pylint: disable=W0613 - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - client: Optional["httpx.AsyncClient"] = None, - path: str = "/dns-query", - post: bool = True, - verify: Union[bool, str] = True, - bootstrap_address: Optional[str] = None, - resolver: Optional["dns.asyncresolver.Resolver"] = None, - family: Optional[int] = socket.AF_UNSPEC, -) -> dns.message.Message: - """Return the response obtained after sending a query via DNS-over-HTTPS. - - *client*, a ``httpx.AsyncClient``. If provided, the client to use for - the query. - - Unlike the other dnspython async functions, a backend cannot be provided - in this function because httpx always auto-detects the async backend. - - See :py:func:`dns.query.https()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - if not have_doh: - raise NoDOH # pragma: no cover - if client and not isinstance(client, httpx.AsyncClient): - raise ValueError("session parameter must be an httpx.AsyncClient") - - wire = q.to_wire() - try: - af = dns.inet.af_for_address(where) - except ValueError: - af = None - transport = None - headers = {"accept": "application/dns-message"} - if af is not None and dns.inet.is_address(where): - if af == socket.AF_INET: - url = "https://{}:{}{}".format(where, port, path) - elif af == socket.AF_INET6: - url = "https://[{}]:{}{}".format(where, port, path) - else: - url = where - - backend = dns.asyncbackend.get_default_backend() - - if source is None: - local_address = None - local_port = 0 - else: - local_address = source - local_port = source_port - transport = backend.get_transport_class()( - local_address=local_address, - http1=True, - http2=True, - verify=verify, - local_port=local_port, - bootstrap_address=bootstrap_address, - resolver=resolver, - family=family, - ) - - if client: - cm: contextlib.AbstractAsyncContextManager = NullContext(client) - else: - cm = httpx.AsyncClient( - http1=True, http2=True, verify=verify, transport=transport - ) - - async with cm as the_client: - # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH - # GET and POST examples - if post: - headers.update( - { - "content-type": "application/dns-message", - "content-length": str(len(wire)), - } - ) - response = await backend.wait_for( - the_client.post(url, headers=headers, content=wire), timeout - ) - else: - wire = base64.urlsafe_b64encode(wire).rstrip(b"=") - twire = wire.decode() # httpx does a repr() if we give it bytes - response = await backend.wait_for( - the_client.get(url, headers=headers, params={"dns": twire}), timeout - ) - - # see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH - # status codes - if response.status_code < 200 or response.status_code > 299: - raise ValueError( - "{} responded with status code {}" - "\nResponse body: {!r}".format( - where, response.status_code, response.content - ) - ) - r = dns.message.from_wire( - response.content, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = response.elapsed.total_seconds() - if not q.is_response(r): - raise BadResponse - return r - - -async def inbound_xfr( - where: str, - txn_manager: dns.transaction.TransactionManager, - query: Optional[dns.message.Message] = None, - port: int = 53, - timeout: Optional[float] = None, - lifetime: Optional[float] = None, - source: Optional[str] = None, - source_port: int = 0, - udp_mode: UDPMode = UDPMode.NEVER, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> None: - """Conduct an inbound transfer and apply it via a transaction from the - txn_manager. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.inbound_xfr()` for the documentation of - the other parameters, exceptions, and return type of this method. - """ - if query is None: - (query, serial) = dns.xfr.make_query(txn_manager) - else: - serial = dns.xfr.extract_serial_from_query(query) - rdtype = query.question[0].rdtype - is_ixfr = rdtype == dns.rdatatype.IXFR - origin = txn_manager.from_wire_origin() - wire = query.to_wire() - af = dns.inet.af_for_address(where) - stuple = _source_tuple(af, source, source_port) - dtuple = (where, port) - (_, expiration) = _compute_times(lifetime) - retry = True - while retry: - retry = False - if is_ixfr and udp_mode != UDPMode.NEVER: - sock_type = socket.SOCK_DGRAM - is_udp = True - else: - sock_type = socket.SOCK_STREAM - is_udp = False - if not backend: - backend = dns.asyncbackend.get_default_backend() - s = await backend.make_socket( - af, sock_type, 0, stuple, dtuple, _timeout(expiration) - ) - async with s: - if is_udp: - await s.sendto(wire, dtuple, _timeout(expiration)) - else: - tcpmsg = struct.pack("!H", len(wire)) + wire - await s.sendall(tcpmsg, expiration) - with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: - done = False - tsig_ctx = None - while not done: - (_, mexpiration) = _compute_times(timeout) - if mexpiration is None or ( - expiration is not None and mexpiration > expiration - ): - mexpiration = expiration - if is_udp: - destination = _lltuple((where, port), af) - while True: - timeout = _timeout(mexpiration) - (rwire, from_address) = await s.recvfrom(65535, timeout) - if _matches_destination( - af, from_address, destination, True - ): - break - else: - ldata = await _read_exactly(s, 2, mexpiration) - (l,) = struct.unpack("!H", ldata) - rwire = await _read_exactly(s, l, mexpiration) - is_ixfr = rdtype == dns.rdatatype.IXFR - r = dns.message.from_wire( - rwire, - keyring=query.keyring, - request_mac=query.mac, - xfr=True, - origin=origin, - tsig_ctx=tsig_ctx, - multi=(not is_udp), - one_rr_per_rrset=is_ixfr, - ) - try: - done = inbound.process_message(r) - except dns.xfr.UseTCP: - assert is_udp # should not happen if we used TCP! - if udp_mode == UDPMode.ONLY: - raise - done = True - retry = True - udp_mode = UDPMode.NEVER - continue - tsig_ctx = r.tsig_ctx - if not retry and query.keyring and not r.had_tsig: - raise dns.exception.FormError("missing TSIG") - - -async def quic( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - connection: Optional[dns.quic.AsyncQuicConnection] = None, - verify: Union[bool, str] = True, - backend: Optional[dns.asyncbackend.Backend] = None, - server_hostname: Optional[str] = None, -) -> dns.message.Message: - """Return the response obtained after sending an asynchronous query via - DNS-over-QUIC. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.query.quic()` for the documentation of the other - parameters, exceptions, and return type of this method. - """ - - if not dns.quic.have_quic: - raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover - - q.id = 0 - wire = q.to_wire() - the_connection: dns.quic.AsyncQuicConnection - if connection: - cfactory = dns.quic.null_factory - mfactory = dns.quic.null_factory - the_connection = connection - else: - (cfactory, mfactory) = dns.quic.factories_for_backend(backend) - - async with cfactory() as context: - async with mfactory( - context, verify_mode=verify, server_name=server_hostname - ) as the_manager: - if not connection: - the_connection = the_manager.connect(where, port, source, source_port) - (start, expiration) = _compute_times(timeout) - stream = await the_connection.make_stream(timeout) - async with stream: - await stream.send(wire, True) - wire = await stream.receive(_remaining(expiration)) - finish = time.time() - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = max(finish - start, 0.0) - if not q.is_response(r): - raise BadResponse - return r diff --git a/.venv/lib/python3.12/site-packages/dns/asyncresolver.py b/.venv/lib/python3.12/site-packages/dns/asyncresolver.py deleted file mode 100644 index 8f5e062..0000000 --- a/.venv/lib/python3.12/site-packages/dns/asyncresolver.py +++ /dev/null @@ -1,475 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Asynchronous DNS stub resolver.""" - -import socket -import time -from typing import Any, Dict, List, Optional, Union - -import dns._ddr -import dns.asyncbackend -import dns.asyncquery -import dns.exception -import dns.name -import dns.query -import dns.rdataclass -import dns.rdatatype -import dns.resolver # lgtm[py/import-and-import-from] - -# import some resolver symbols for brevity -from dns.resolver import NXDOMAIN, NoAnswer, NoRootSOA, NotAbsolute - -# for indentation purposes below -_udp = dns.asyncquery.udp -_tcp = dns.asyncquery.tcp - - -class Resolver(dns.resolver.BaseResolver): - """Asynchronous DNS stub resolver.""" - - async def resolve( - self, - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - ) -> dns.resolver.Answer: - """Query nameservers asynchronously to find the answer to the question. - - *backend*, a ``dns.asyncbackend.Backend``, or ``None``. If ``None``, - the default, then dnspython will use the default backend. - - See :py:func:`dns.resolver.Resolver.resolve()` for the - documentation of the other parameters, exceptions, and return - type of this method. - """ - - resolution = dns.resolver._Resolution( - self, qname, rdtype, rdclass, tcp, raise_on_no_answer, search - ) - if not backend: - backend = dns.asyncbackend.get_default_backend() - start = time.time() - while True: - (request, answer) = resolution.next_request() - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - # cache hit! - return answer - assert request is not None # needed for type checking - done = False - while not done: - (nameserver, tcp, backoff) = resolution.next_nameserver() - if backoff: - await backend.sleep(backoff) - timeout = self._compute_timeout(start, lifetime, resolution.errors) - try: - response = await nameserver.async_query( - request, - timeout=timeout, - source=source, - source_port=source_port, - max_size=tcp, - backend=backend, - ) - except Exception as ex: - (_, done) = resolution.query_result(None, ex) - continue - (answer, done) = resolution.query_result(response, None) - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - return answer - - async def resolve_address( - self, ipaddr: str, *args: Any, **kwargs: Any - ) -> dns.resolver.Answer: - """Use an asynchronous resolver to run a reverse query for PTR - records. - - This utilizes the resolve() method to perform a PTR lookup on the - specified IP address. - - *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get - the PTR record for. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs["rdtype"] = dns.rdatatype.PTR - modified_kwargs["rdclass"] = dns.rdataclass.IN - return await self.resolve( - dns.reversename.from_address(ipaddr), *args, **modified_kwargs - ) - - async def resolve_name( - self, - name: Union[dns.name.Name, str], - family: int = socket.AF_UNSPEC, - **kwargs: Any, - ) -> dns.resolver.HostAnswers: - """Use an asynchronous resolver to query for address records. - - This utilizes the resolve() method to perform A and/or AAAA lookups on - the specified name. - - *qname*, a ``dns.name.Name`` or ``str``, the name to resolve. - - *family*, an ``int``, the address family. If socket.AF_UNSPEC - (the default), both A and AAAA records will be retrieved. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs.pop("rdtype", None) - modified_kwargs["rdclass"] = dns.rdataclass.IN - - if family == socket.AF_INET: - v4 = await self.resolve(name, dns.rdatatype.A, **modified_kwargs) - return dns.resolver.HostAnswers.make(v4=v4) - elif family == socket.AF_INET6: - v6 = await self.resolve(name, dns.rdatatype.AAAA, **modified_kwargs) - return dns.resolver.HostAnswers.make(v6=v6) - elif family != socket.AF_UNSPEC: - raise NotImplementedError(f"unknown address family {family}") - - raise_on_no_answer = modified_kwargs.pop("raise_on_no_answer", True) - lifetime = modified_kwargs.pop("lifetime", None) - start = time.time() - v6 = await self.resolve( - name, - dns.rdatatype.AAAA, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - # Note that setting name ensures we query the same name - # for A as we did for AAAA. (This is just in case search lists - # are active by default in the resolver configuration and - # we might be talking to a server that says NXDOMAIN when it - # wants to say NOERROR no data. - name = v6.qname - v4 = await self.resolve( - name, - dns.rdatatype.A, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - answers = dns.resolver.HostAnswers.make( - v6=v6, v4=v4, add_empty=not raise_on_no_answer - ) - if not answers: - raise NoAnswer(response=v6.response) - return answers - - # pylint: disable=redefined-outer-name - - async def canonical_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - The canonical name is the name the resolver uses for queries - after all CNAME and DNAME renamings have been applied. - - *name*, a ``dns.name.Name`` or ``str``, the query name. - - This method can raise any exception that ``resolve()`` can - raise, other than ``dns.resolver.NoAnswer`` and - ``dns.resolver.NXDOMAIN``. - - Returns a ``dns.name.Name``. - """ - try: - answer = await self.resolve(name, raise_on_no_answer=False) - canonical_name = answer.canonical_name - except dns.resolver.NXDOMAIN as e: - canonical_name = e.canonical_name - return canonical_name - - async def try_ddr(self, lifetime: float = 5.0) -> None: - """Try to update the resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - *lifetime*, a float, is the maximum time to spend attempting DDR. The default - is 5 seconds. - - If the SVCB query is successful and results in a non-empty list of nameservers, - then the resolver's nameservers are set to the returned servers in priority - order. - - The current implementation does not use any address hints from the SVCB record, - nor does it resolve addresses for the SCVB target name, rather it assumes that - the bootstrap nameserver will always be one of the addresses and uses it. - A future revision to the code may offer fuller support. The code verifies that - the bootstrap nameserver is in the Subject Alternative Name field of the - TLS certficate. - """ - try: - expiration = time.time() + lifetime - answer = await self.resolve( - dns._ddr._local_resolver_name, "svcb", lifetime=lifetime - ) - timeout = dns.query._remaining(expiration) - nameservers = await dns._ddr._get_nameservers_async(answer, timeout) - if len(nameservers) > 0: - self.nameservers = nameservers - except Exception: - pass - - -default_resolver = None - - -def get_default_resolver() -> Resolver: - """Get the default asynchronous resolver, initializing it if necessary.""" - if default_resolver is None: - reset_default_resolver() - assert default_resolver is not None - return default_resolver - - -def reset_default_resolver() -> None: - """Re-initialize default asynchronous resolver. - - Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX - systems) will be re-read immediately. - """ - - global default_resolver - default_resolver = Resolver() - - -async def resolve( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> dns.resolver.Answer: - """Query nameservers asynchronously to find the answer to the question. - - This is a convenience function that uses the default resolver - object to make the query. - - See :py:func:`dns.asyncresolver.Resolver.resolve` for more - information on the parameters. - """ - - return await get_default_resolver().resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - backend, - ) - - -async def resolve_address( - ipaddr: str, *args: Any, **kwargs: Any -) -> dns.resolver.Answer: - """Use a resolver to run a reverse query for PTR records. - - See :py:func:`dns.asyncresolver.Resolver.resolve_address` for more - information on the parameters. - """ - - return await get_default_resolver().resolve_address(ipaddr, *args, **kwargs) - - -async def resolve_name( - name: Union[dns.name.Name, str], family: int = socket.AF_UNSPEC, **kwargs: Any -) -> dns.resolver.HostAnswers: - """Use a resolver to asynchronously query for address records. - - See :py:func:`dns.asyncresolver.Resolver.resolve_name` for more - information on the parameters. - """ - - return await get_default_resolver().resolve_name(name, family, **kwargs) - - -async def canonical_name(name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - See :py:func:`dns.resolver.Resolver.canonical_name` for more - information on the parameters and possible exceptions. - """ - - return await get_default_resolver().canonical_name(name) - - -async def try_ddr(timeout: float = 5.0) -> None: - """Try to update the default resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - See :py:func:`dns.resolver.Resolver.try_ddr` for more information. - """ - return await get_default_resolver().try_ddr(timeout) - - -async def zone_for_name( - name: Union[dns.name.Name, str], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - tcp: bool = False, - resolver: Optional[Resolver] = None, - backend: Optional[dns.asyncbackend.Backend] = None, -) -> dns.name.Name: - """Find the name of the zone which contains the specified name. - - See :py:func:`dns.resolver.Resolver.zone_for_name` for more - information on the parameters and possible exceptions. - """ - - if isinstance(name, str): - name = dns.name.from_text(name, dns.name.root) - if resolver is None: - resolver = get_default_resolver() - if not name.is_absolute(): - raise NotAbsolute(name) - while True: - try: - answer = await resolver.resolve( - name, dns.rdatatype.SOA, rdclass, tcp, backend=backend - ) - assert answer.rrset is not None - if answer.rrset.name == name: - return name - # otherwise we were CNAMEd or DNAMEd and need to look higher - except (NXDOMAIN, NoAnswer): - pass - try: - name = name.parent() - except dns.name.NoParent: # pragma: no cover - raise NoRootSOA - - -async def make_resolver_at( - where: Union[dns.name.Name, str], - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> Resolver: - """Make a stub resolver using the specified destination as the full resolver. - - *where*, a ``dns.name.Name`` or ``str`` the domain name or IP address of the - full resolver. - - *port*, an ``int``, the port to use. If not specified, the default is 53. - - *family*, an ``int``, the address family to use. This parameter is used if - *where* is not an address. The default is ``socket.AF_UNSPEC`` in which case - the first address returned by ``resolve_name()`` will be used, otherwise the - first address of the specified family will be used. - - *resolver*, a ``dns.asyncresolver.Resolver`` or ``None``, the resolver to use for - resolution of hostnames. If not specified, the default resolver will be used. - - Returns a ``dns.resolver.Resolver`` or raises an exception. - """ - if resolver is None: - resolver = get_default_resolver() - nameservers: List[Union[str, dns.nameserver.Nameserver]] = [] - if isinstance(where, str) and dns.inet.is_address(where): - nameservers.append(dns.nameserver.Do53Nameserver(where, port)) - else: - answers = await resolver.resolve_name(where, family) - for address in answers.addresses(): - nameservers.append(dns.nameserver.Do53Nameserver(address, port)) - res = dns.asyncresolver.Resolver(configure=False) - res.nameservers = nameservers - return res - - -async def resolve_at( - where: Union[dns.name.Name, str], - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - backend: Optional[dns.asyncbackend.Backend] = None, - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> dns.resolver.Answer: - """Query nameservers to find the answer to the question. - - This is a convenience function that calls ``dns.asyncresolver.make_resolver_at()`` - to make a resolver, and then uses it to resolve the query. - - See ``dns.asyncresolver.Resolver.resolve`` for more information on the resolution - parameters, and ``dns.asyncresolver.make_resolver_at`` for information about the - resolver parameters *where*, *port*, *family*, and *resolver*. - - If making more than one query, it is more efficient to call - ``dns.asyncresolver.make_resolver_at()`` and then use that resolver for the queries - instead of calling ``resolve_at()`` multiple times. - """ - res = await make_resolver_at(where, port, family, resolver) - return await res.resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - backend, - ) diff --git a/.venv/lib/python3.12/site-packages/dns/dnssec.py b/.venv/lib/python3.12/site-packages/dns/dnssec.py deleted file mode 100644 index e49c3b7..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssec.py +++ /dev/null @@ -1,1223 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNSSEC-related functions and constants.""" - - -import base64 -import contextlib -import functools -import hashlib -import struct -import time -from datetime import datetime -from typing import Callable, Dict, List, Optional, Set, Tuple, Union, cast - -import dns._features -import dns.exception -import dns.name -import dns.node -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rrset -import dns.transaction -import dns.zone -from dns.dnssectypes import Algorithm, DSDigest, NSEC3Hash -from dns.exception import ( # pylint: disable=W0611 - AlgorithmKeyMismatch, - DeniedByPolicy, - UnsupportedAlgorithm, - ValidationFailure, -) -from dns.rdtypes.ANY.CDNSKEY import CDNSKEY -from dns.rdtypes.ANY.CDS import CDS -from dns.rdtypes.ANY.DNSKEY import DNSKEY -from dns.rdtypes.ANY.DS import DS -from dns.rdtypes.ANY.NSEC import NSEC, Bitmap -from dns.rdtypes.ANY.NSEC3PARAM import NSEC3PARAM -from dns.rdtypes.ANY.RRSIG import RRSIG, sigtime_to_posixtime -from dns.rdtypes.dnskeybase import Flag - -PublicKey = Union[ - "GenericPublicKey", - "rsa.RSAPublicKey", - "ec.EllipticCurvePublicKey", - "ed25519.Ed25519PublicKey", - "ed448.Ed448PublicKey", -] - -PrivateKey = Union[ - "GenericPrivateKey", - "rsa.RSAPrivateKey", - "ec.EllipticCurvePrivateKey", - "ed25519.Ed25519PrivateKey", - "ed448.Ed448PrivateKey", -] - -RRsetSigner = Callable[[dns.transaction.Transaction, dns.rrset.RRset], None] - - -def algorithm_from_text(text: str) -> Algorithm: - """Convert text into a DNSSEC algorithm value. - - *text*, a ``str``, the text to convert to into an algorithm value. - - Returns an ``int``. - """ - - return Algorithm.from_text(text) - - -def algorithm_to_text(value: Union[Algorithm, int]) -> str: - """Convert a DNSSEC algorithm value to text - - *value*, a ``dns.dnssec.Algorithm``. - - Returns a ``str``, the name of a DNSSEC algorithm. - """ - - return Algorithm.to_text(value) - - -def to_timestamp(value: Union[datetime, str, float, int]) -> int: - """Convert various format to a timestamp""" - if isinstance(value, datetime): - return int(value.timestamp()) - elif isinstance(value, str): - return sigtime_to_posixtime(value) - elif isinstance(value, float): - return int(value) - elif isinstance(value, int): - return value - else: - raise TypeError("Unsupported timestamp type") - - -def key_id(key: Union[DNSKEY, CDNSKEY]) -> int: - """Return the key id (a 16-bit number) for the specified key. - - *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` - - Returns an ``int`` between 0 and 65535 - """ - - rdata = key.to_wire() - if key.algorithm == Algorithm.RSAMD5: - return (rdata[-3] << 8) + rdata[-2] - else: - total = 0 - for i in range(len(rdata) // 2): - total += (rdata[2 * i] << 8) + rdata[2 * i + 1] - if len(rdata) % 2 != 0: - total += rdata[len(rdata) - 1] << 8 - total += (total >> 16) & 0xFFFF - return total & 0xFFFF - - -class Policy: - def __init__(self): - pass - - def ok_to_sign(self, _: DNSKEY) -> bool: # pragma: no cover - return False - - def ok_to_validate(self, _: DNSKEY) -> bool: # pragma: no cover - return False - - def ok_to_create_ds(self, _: DSDigest) -> bool: # pragma: no cover - return False - - def ok_to_validate_ds(self, _: DSDigest) -> bool: # pragma: no cover - return False - - -class SimpleDeny(Policy): - def __init__(self, deny_sign, deny_validate, deny_create_ds, deny_validate_ds): - super().__init__() - self._deny_sign = deny_sign - self._deny_validate = deny_validate - self._deny_create_ds = deny_create_ds - self._deny_validate_ds = deny_validate_ds - - def ok_to_sign(self, key: DNSKEY) -> bool: - return key.algorithm not in self._deny_sign - - def ok_to_validate(self, key: DNSKEY) -> bool: - return key.algorithm not in self._deny_validate - - def ok_to_create_ds(self, algorithm: DSDigest) -> bool: - return algorithm not in self._deny_create_ds - - def ok_to_validate_ds(self, algorithm: DSDigest) -> bool: - return algorithm not in self._deny_validate_ds - - -rfc_8624_policy = SimpleDeny( - {Algorithm.RSAMD5, Algorithm.DSA, Algorithm.DSANSEC3SHA1, Algorithm.ECCGOST}, - {Algorithm.RSAMD5, Algorithm.DSA, Algorithm.DSANSEC3SHA1}, - {DSDigest.NULL, DSDigest.SHA1, DSDigest.GOST}, - {DSDigest.NULL}, -) - -allow_all_policy = SimpleDeny(set(), set(), set(), set()) - - -default_policy = rfc_8624_policy - - -def make_ds( - name: Union[dns.name.Name, str], - key: dns.rdata.Rdata, - algorithm: Union[DSDigest, str], - origin: Optional[dns.name.Name] = None, - policy: Optional[Policy] = None, - validating: bool = False, -) -> DS: - """Create a DS record for a DNSSEC key. - - *name*, a ``dns.name.Name`` or ``str``, the owner name of the DS record. - - *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` or ``dns.rdtypes.ANY.DNSKEY.CDNSKEY``, - the key the DS is about. - - *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. - - *origin*, a ``dns.name.Name`` or ``None``. If *key* is a relative name, - then it will be made absolute using the specified origin. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - *validating*, a ``bool``. If ``True``, then policy is checked in - validating mode, i.e. "Is it ok to validate using this digest algorithm?". - Otherwise the policy is checked in creating mode, i.e. "Is it ok to create a DS with - this digest algorithm?". - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Raises ``DeniedByPolicy`` if the algorithm is denied by policy. - - Returns a ``dns.rdtypes.ANY.DS.DS`` - """ - - if policy is None: - policy = default_policy - try: - if isinstance(algorithm, str): - algorithm = DSDigest[algorithm.upper()] - except Exception: - raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm) - if validating: - check = policy.ok_to_validate_ds - else: - check = policy.ok_to_create_ds - if not check(algorithm): - raise DeniedByPolicy - if not isinstance(key, (DNSKEY, CDNSKEY)): - raise ValueError("key is not a DNSKEY/CDNSKEY") - if algorithm == DSDigest.SHA1: - dshash = hashlib.sha1() - elif algorithm == DSDigest.SHA256: - dshash = hashlib.sha256() - elif algorithm == DSDigest.SHA384: - dshash = hashlib.sha384() - else: - raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm) - - if isinstance(name, str): - name = dns.name.from_text(name, origin) - wire = name.canonicalize().to_wire() - assert wire is not None - dshash.update(wire) - dshash.update(key.to_wire(origin=origin)) - digest = dshash.digest() - - dsrdata = struct.pack("!HBB", key_id(key), key.algorithm, algorithm) + digest - ds = dns.rdata.from_wire( - dns.rdataclass.IN, dns.rdatatype.DS, dsrdata, 0, len(dsrdata) - ) - return cast(DS, ds) - - -def make_cds( - name: Union[dns.name.Name, str], - key: dns.rdata.Rdata, - algorithm: Union[DSDigest, str], - origin: Optional[dns.name.Name] = None, -) -> CDS: - """Create a CDS record for a DNSSEC key. - - *name*, a ``dns.name.Name`` or ``str``, the owner name of the DS record. - - *key*, a ``dns.rdtypes.ANY.DNSKEY.DNSKEY`` or ``dns.rdtypes.ANY.DNSKEY.CDNSKEY``, - the key the DS is about. - - *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. - - *origin*, a ``dns.name.Name`` or ``None``. If *key* is a relative name, - then it will be made absolute using the specified origin. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Returns a ``dns.rdtypes.ANY.DS.CDS`` - """ - - ds = make_ds(name, key, algorithm, origin) - return CDS( - rdclass=ds.rdclass, - rdtype=dns.rdatatype.CDS, - key_tag=ds.key_tag, - algorithm=ds.algorithm, - digest_type=ds.digest_type, - digest=ds.digest, - ) - - -def _find_candidate_keys( - keys: Dict[dns.name.Name, Union[dns.rdataset.Rdataset, dns.node.Node]], rrsig: RRSIG -) -> Optional[List[DNSKEY]]: - value = keys.get(rrsig.signer) - if isinstance(value, dns.node.Node): - rdataset = value.get_rdataset(dns.rdataclass.IN, dns.rdatatype.DNSKEY) - else: - rdataset = value - if rdataset is None: - return None - return [ - cast(DNSKEY, rd) - for rd in rdataset - if rd.algorithm == rrsig.algorithm - and key_id(rd) == rrsig.key_tag - and (rd.flags & Flag.ZONE) == Flag.ZONE # RFC 4034 2.1.1 - and rd.protocol == 3 # RFC 4034 2.1.2 - ] - - -def _get_rrname_rdataset( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], -) -> Tuple[dns.name.Name, dns.rdataset.Rdataset]: - if isinstance(rrset, tuple): - return rrset[0], rrset[1] - else: - return rrset.name, rrset - - -def _validate_signature(sig: bytes, data: bytes, key: DNSKEY) -> None: - public_cls = get_algorithm_cls_from_dnskey(key).public_cls - try: - public_key = public_cls.from_dnskey(key) - except ValueError: - raise ValidationFailure("invalid public key") - public_key.verify(sig, data) - - -def _validate_rrsig( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - rrsig: RRSIG, - keys: Dict[dns.name.Name, Union[dns.node.Node, dns.rdataset.Rdataset]], - origin: Optional[dns.name.Name] = None, - now: Optional[float] = None, - policy: Optional[Policy] = None, -) -> None: - """Validate an RRset against a single signature rdata, throwing an - exception if validation is not successful. - - *rrset*, the RRset to validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *rrsig*, a ``dns.rdata.Rdata``, the signature to validate. - - *keys*, the key dictionary, used to find the DNSKEY associated - with a given name. The dictionary is keyed by a - ``dns.name.Name``, and has ``dns.node.Node`` or - ``dns.rdataset.Rdataset`` values. - - *origin*, a ``dns.name.Name`` or ``None``, the origin to use for relative - names. - - *now*, a ``float`` or ``None``, the time, in seconds since the epoch, to - use as the current time when validating. If ``None``, the actual current - time is used. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - Raises ``ValidationFailure`` if the signature is expired, not yet valid, - the public key is invalid, the algorithm is unknown, the verification - fails, etc. - - Raises ``UnsupportedAlgorithm`` if the algorithm is recognized by - dnspython but not implemented. - """ - - if policy is None: - policy = default_policy - - candidate_keys = _find_candidate_keys(keys, rrsig) - if candidate_keys is None: - raise ValidationFailure("unknown key") - - if now is None: - now = time.time() - if rrsig.expiration < now: - raise ValidationFailure("expired") - if rrsig.inception > now: - raise ValidationFailure("not yet valid") - - data = _make_rrsig_signature_data(rrset, rrsig, origin) - - for candidate_key in candidate_keys: - if not policy.ok_to_validate(candidate_key): - continue - try: - _validate_signature(rrsig.signature, data, candidate_key) - return - except (InvalidSignature, ValidationFailure): - # this happens on an individual validation failure - continue - # nothing verified -- raise failure: - raise ValidationFailure("verify failure") - - -def _validate( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - rrsigset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - keys: Dict[dns.name.Name, Union[dns.node.Node, dns.rdataset.Rdataset]], - origin: Optional[dns.name.Name] = None, - now: Optional[float] = None, - policy: Optional[Policy] = None, -) -> None: - """Validate an RRset against a signature RRset, throwing an exception - if none of the signatures validate. - - *rrset*, the RRset to validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *rrsigset*, the signature RRset. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *keys*, the key dictionary, used to find the DNSKEY associated - with a given name. The dictionary is keyed by a - ``dns.name.Name``, and has ``dns.node.Node`` or - ``dns.rdataset.Rdataset`` values. - - *origin*, a ``dns.name.Name``, the origin to use for relative names; - defaults to None. - - *now*, an ``int`` or ``None``, the time, in seconds since the epoch, to - use as the current time when validating. If ``None``, the actual current - time is used. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - Raises ``ValidationFailure`` if the signature is expired, not yet valid, - the public key is invalid, the algorithm is unknown, the verification - fails, etc. - """ - - if policy is None: - policy = default_policy - - if isinstance(origin, str): - origin = dns.name.from_text(origin, dns.name.root) - - if isinstance(rrset, tuple): - rrname = rrset[0] - else: - rrname = rrset.name - - if isinstance(rrsigset, tuple): - rrsigname = rrsigset[0] - rrsigrdataset = rrsigset[1] - else: - rrsigname = rrsigset.name - rrsigrdataset = rrsigset - - rrname = rrname.choose_relativity(origin) - rrsigname = rrsigname.choose_relativity(origin) - if rrname != rrsigname: - raise ValidationFailure("owner names do not match") - - for rrsig in rrsigrdataset: - if not isinstance(rrsig, RRSIG): - raise ValidationFailure("expected an RRSIG") - try: - _validate_rrsig(rrset, rrsig, keys, origin, now, policy) - return - except (ValidationFailure, UnsupportedAlgorithm): - pass - raise ValidationFailure("no RRSIGs validated") - - -def _sign( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - private_key: PrivateKey, - signer: dns.name.Name, - dnskey: DNSKEY, - inception: Optional[Union[datetime, str, int, float]] = None, - expiration: Optional[Union[datetime, str, int, float]] = None, - lifetime: Optional[int] = None, - verify: bool = False, - policy: Optional[Policy] = None, - origin: Optional[dns.name.Name] = None, -) -> RRSIG: - """Sign RRset using private key. - - *rrset*, the RRset to validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *private_key*, the private key to use for signing, a - ``cryptography.hazmat.primitives.asymmetric`` private key class applicable - for DNSSEC. - - *signer*, a ``dns.name.Name``, the Signer's name. - - *dnskey*, a ``DNSKEY`` matching ``private_key``. - - *inception*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the - signature inception time. If ``None``, the current time is used. If a ``str``, the - format is "YYYYMMDDHHMMSS" or alternatively the number of seconds since the UNIX - epoch in text form; this is the same the RRSIG rdata's text form. - Values of type `int` or `float` are interpreted as seconds since the UNIX epoch. - - *expiration*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature - expiration time. If ``None``, the expiration time will be the inception time plus - the value of the *lifetime* parameter. See the description of *inception* above - for how the various parameter types are interpreted. - - *lifetime*, an ``int`` or ``None``, the signature lifetime in seconds. This - parameter is only meaningful if *expiration* is ``None``. - - *verify*, a ``bool``. If set to ``True``, the signer will verify signatures - after they are created; the default is ``False``. - - *policy*, a ``dns.dnssec.Policy`` or ``None``. If ``None``, the default policy, - ``dns.dnssec.default_policy`` is used; this policy defaults to that of RFC 8624. - - *origin*, a ``dns.name.Name`` or ``None``. If ``None``, the default, then all - names in the rrset (including its owner name) must be absolute; otherwise the - specified origin will be used to make names absolute when signing. - - Raises ``DeniedByPolicy`` if the signature is denied by policy. - """ - - if policy is None: - policy = default_policy - if not policy.ok_to_sign(dnskey): - raise DeniedByPolicy - - if isinstance(rrset, tuple): - rdclass = rrset[1].rdclass - rdtype = rrset[1].rdtype - rrname = rrset[0] - original_ttl = rrset[1].ttl - else: - rdclass = rrset.rdclass - rdtype = rrset.rdtype - rrname = rrset.name - original_ttl = rrset.ttl - - if inception is not None: - rrsig_inception = to_timestamp(inception) - else: - rrsig_inception = int(time.time()) - - if expiration is not None: - rrsig_expiration = to_timestamp(expiration) - elif lifetime is not None: - rrsig_expiration = rrsig_inception + lifetime - else: - raise ValueError("expiration or lifetime must be specified") - - # Derelativize now because we need a correct labels length for the - # rrsig_template. - if origin is not None: - rrname = rrname.derelativize(origin) - labels = len(rrname) - 1 - - # Adjust labels appropriately for wildcards. - if rrname.is_wild(): - labels -= 1 - - rrsig_template = RRSIG( - rdclass=rdclass, - rdtype=dns.rdatatype.RRSIG, - type_covered=rdtype, - algorithm=dnskey.algorithm, - labels=labels, - original_ttl=original_ttl, - expiration=rrsig_expiration, - inception=rrsig_inception, - key_tag=key_id(dnskey), - signer=signer, - signature=b"", - ) - - data = dns.dnssec._make_rrsig_signature_data(rrset, rrsig_template, origin) - - if isinstance(private_key, GenericPrivateKey): - signing_key = private_key - else: - try: - private_cls = get_algorithm_cls_from_dnskey(dnskey) - signing_key = private_cls(key=private_key) - except UnsupportedAlgorithm: - raise TypeError("Unsupported key algorithm") - - signature = signing_key.sign(data, verify) - - return cast(RRSIG, rrsig_template.replace(signature=signature)) - - -def _make_rrsig_signature_data( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - rrsig: RRSIG, - origin: Optional[dns.name.Name] = None, -) -> bytes: - """Create signature rdata. - - *rrset*, the RRset to sign/validate. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *rrsig*, a ``dns.rdata.Rdata``, the signature to validate, or the - signature template used when signing. - - *origin*, a ``dns.name.Name`` or ``None``, the origin to use for relative - names. - - Raises ``UnsupportedAlgorithm`` if the algorithm is recognized by - dnspython but not implemented. - """ - - if isinstance(origin, str): - origin = dns.name.from_text(origin, dns.name.root) - - signer = rrsig.signer - if not signer.is_absolute(): - if origin is None: - raise ValidationFailure("relative RR name without an origin specified") - signer = signer.derelativize(origin) - - # For convenience, allow the rrset to be specified as a (name, - # rdataset) tuple as well as a proper rrset - rrname, rdataset = _get_rrname_rdataset(rrset) - - data = b"" - data += rrsig.to_wire(origin=signer)[:18] - data += rrsig.signer.to_digestable(signer) - - # Derelativize the name before considering labels. - if not rrname.is_absolute(): - if origin is None: - raise ValidationFailure("relative RR name without an origin specified") - rrname = rrname.derelativize(origin) - - name_len = len(rrname) - if rrname.is_wild() and rrsig.labels != name_len - 2: - raise ValidationFailure("wild owner name has wrong label length") - if name_len - 1 < rrsig.labels: - raise ValidationFailure("owner name longer than RRSIG labels") - elif rrsig.labels < name_len - 1: - suffix = rrname.split(rrsig.labels + 1)[1] - rrname = dns.name.from_text("*", suffix) - rrnamebuf = rrname.to_digestable() - rrfixed = struct.pack("!HHI", rdataset.rdtype, rdataset.rdclass, rrsig.original_ttl) - rdatas = [rdata.to_digestable(origin) for rdata in rdataset] - for rdata in sorted(rdatas): - data += rrnamebuf - data += rrfixed - rrlen = struct.pack("!H", len(rdata)) - data += rrlen - data += rdata - - return data - - -def _make_dnskey( - public_key: PublicKey, - algorithm: Union[int, str], - flags: int = Flag.ZONE, - protocol: int = 3, -) -> DNSKEY: - """Convert a public key to DNSKEY Rdata - - *public_key*, a ``PublicKey`` (``GenericPublicKey`` or - ``cryptography.hazmat.primitives.asymmetric``) to convert. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - *flags*: DNSKEY flags field as an integer. - - *protocol*: DNSKEY protocol field as an integer. - - Raises ``ValueError`` if the specified key algorithm parameters are not - unsupported, ``TypeError`` if the key type is unsupported, - `UnsupportedAlgorithm` if the algorithm is unknown and - `AlgorithmKeyMismatch` if the algorithm does not match the key type. - - Return DNSKEY ``Rdata``. - """ - - algorithm = Algorithm.make(algorithm) - - if isinstance(public_key, GenericPublicKey): - return public_key.to_dnskey(flags=flags, protocol=protocol) - else: - public_cls = get_algorithm_cls(algorithm).public_cls - return public_cls(key=public_key).to_dnskey(flags=flags, protocol=protocol) - - -def _make_cdnskey( - public_key: PublicKey, - algorithm: Union[int, str], - flags: int = Flag.ZONE, - protocol: int = 3, -) -> CDNSKEY: - """Convert a public key to CDNSKEY Rdata - - *public_key*, the public key to convert, a - ``cryptography.hazmat.primitives.asymmetric`` public key class applicable - for DNSSEC. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - *flags*: DNSKEY flags field as an integer. - - *protocol*: DNSKEY protocol field as an integer. - - Raises ``ValueError`` if the specified key algorithm parameters are not - unsupported, ``TypeError`` if the key type is unsupported, - `UnsupportedAlgorithm` if the algorithm is unknown and - `AlgorithmKeyMismatch` if the algorithm does not match the key type. - - Return CDNSKEY ``Rdata``. - """ - - dnskey = _make_dnskey(public_key, algorithm, flags, protocol) - - return CDNSKEY( - rdclass=dnskey.rdclass, - rdtype=dns.rdatatype.CDNSKEY, - flags=dnskey.flags, - protocol=dnskey.protocol, - algorithm=dnskey.algorithm, - key=dnskey.key, - ) - - -def nsec3_hash( - domain: Union[dns.name.Name, str], - salt: Optional[Union[str, bytes]], - iterations: int, - algorithm: Union[int, str], -) -> str: - """ - Calculate the NSEC3 hash, according to - https://tools.ietf.org/html/rfc5155#section-5 - - *domain*, a ``dns.name.Name`` or ``str``, the name to hash. - - *salt*, a ``str``, ``bytes``, or ``None``, the hash salt. If a - string, it is decoded as a hex string. - - *iterations*, an ``int``, the number of iterations. - - *algorithm*, a ``str`` or ``int``, the hash algorithm. - The only defined algorithm is SHA1. - - Returns a ``str``, the encoded NSEC3 hash. - """ - - b32_conversion = str.maketrans( - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "0123456789ABCDEFGHIJKLMNOPQRSTUV" - ) - - try: - if isinstance(algorithm, str): - algorithm = NSEC3Hash[algorithm.upper()] - except Exception: - raise ValueError("Wrong hash algorithm (only SHA1 is supported)") - - if algorithm != NSEC3Hash.SHA1: - raise ValueError("Wrong hash algorithm (only SHA1 is supported)") - - if salt is None: - salt_encoded = b"" - elif isinstance(salt, str): - if len(salt) % 2 == 0: - salt_encoded = bytes.fromhex(salt) - else: - raise ValueError("Invalid salt length") - else: - salt_encoded = salt - - if not isinstance(domain, dns.name.Name): - domain = dns.name.from_text(domain) - domain_encoded = domain.canonicalize().to_wire() - assert domain_encoded is not None - - digest = hashlib.sha1(domain_encoded + salt_encoded).digest() - for _ in range(iterations): - digest = hashlib.sha1(digest + salt_encoded).digest() - - output = base64.b32encode(digest).decode("utf-8") - output = output.translate(b32_conversion) - - return output - - -def make_ds_rdataset( - rrset: Union[dns.rrset.RRset, Tuple[dns.name.Name, dns.rdataset.Rdataset]], - algorithms: Set[Union[DSDigest, str]], - origin: Optional[dns.name.Name] = None, -) -> dns.rdataset.Rdataset: - """Create a DS record from DNSKEY/CDNSKEY/CDS. - - *rrset*, the RRset to create DS Rdataset for. This can be a - ``dns.rrset.RRset`` or a (``dns.name.Name``, ``dns.rdataset.Rdataset``) - tuple. - - *algorithms*, a set of ``str`` or ``int`` specifying the hash algorithms. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. If the RRset is a CDS, only digest - algorithms matching algorithms are accepted. - - *origin*, a ``dns.name.Name`` or ``None``. If `key` is a relative name, - then it will be made absolute using the specified origin. - - Raises ``UnsupportedAlgorithm`` if any of the algorithms are unknown and - ``ValueError`` if the given RRset is not usable. - - Returns a ``dns.rdataset.Rdataset`` - """ - - rrname, rdataset = _get_rrname_rdataset(rrset) - - if rdataset.rdtype not in ( - dns.rdatatype.DNSKEY, - dns.rdatatype.CDNSKEY, - dns.rdatatype.CDS, - ): - raise ValueError("rrset not a DNSKEY/CDNSKEY/CDS") - - _algorithms = set() - for algorithm in algorithms: - try: - if isinstance(algorithm, str): - algorithm = DSDigest[algorithm.upper()] - except Exception: - raise UnsupportedAlgorithm('unsupported algorithm "%s"' % algorithm) - _algorithms.add(algorithm) - - if rdataset.rdtype == dns.rdatatype.CDS: - res = [] - for rdata in cds_rdataset_to_ds_rdataset(rdataset): - if rdata.digest_type in _algorithms: - res.append(rdata) - if len(res) == 0: - raise ValueError("no acceptable CDS rdata found") - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - res = [] - for algorithm in _algorithms: - res.extend(dnskey_rdataset_to_cds_rdataset(rrname, rdataset, algorithm, origin)) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def cds_rdataset_to_ds_rdataset( - rdataset: dns.rdataset.Rdataset, -) -> dns.rdataset.Rdataset: - """Create a CDS record from DS. - - *rdataset*, a ``dns.rdataset.Rdataset``, to create DS Rdataset for. - - Raises ``ValueError`` if the rdataset is not CDS. - - Returns a ``dns.rdataset.Rdataset`` - """ - - if rdataset.rdtype != dns.rdatatype.CDS: - raise ValueError("rdataset not a CDS") - res = [] - for rdata in rdataset: - res.append( - CDS( - rdclass=rdata.rdclass, - rdtype=dns.rdatatype.DS, - key_tag=rdata.key_tag, - algorithm=rdata.algorithm, - digest_type=rdata.digest_type, - digest=rdata.digest, - ) - ) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def dnskey_rdataset_to_cds_rdataset( - name: Union[dns.name.Name, str], - rdataset: dns.rdataset.Rdataset, - algorithm: Union[DSDigest, str], - origin: Optional[dns.name.Name] = None, -) -> dns.rdataset.Rdataset: - """Create a CDS record from DNSKEY/CDNSKEY. - - *name*, a ``dns.name.Name`` or ``str``, the owner name of the CDS record. - - *rdataset*, a ``dns.rdataset.Rdataset``, to create DS Rdataset for. - - *algorithm*, a ``str`` or ``int`` specifying the hash algorithm. - The currently supported hashes are "SHA1", "SHA256", and "SHA384". Case - does not matter for these strings. - - *origin*, a ``dns.name.Name`` or ``None``. If `key` is a relative name, - then it will be made absolute using the specified origin. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown or - ``ValueError`` if the rdataset is not DNSKEY/CDNSKEY. - - Returns a ``dns.rdataset.Rdataset`` - """ - - if rdataset.rdtype not in (dns.rdatatype.DNSKEY, dns.rdatatype.CDNSKEY): - raise ValueError("rdataset not a DNSKEY/CDNSKEY") - res = [] - for rdata in rdataset: - res.append(make_cds(name, rdata, algorithm, origin)) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def dnskey_rdataset_to_cdnskey_rdataset( - rdataset: dns.rdataset.Rdataset, -) -> dns.rdataset.Rdataset: - """Create a CDNSKEY record from DNSKEY. - - *rdataset*, a ``dns.rdataset.Rdataset``, to create CDNSKEY Rdataset for. - - Returns a ``dns.rdataset.Rdataset`` - """ - - if rdataset.rdtype != dns.rdatatype.DNSKEY: - raise ValueError("rdataset not a DNSKEY") - res = [] - for rdata in rdataset: - res.append( - CDNSKEY( - rdclass=rdataset.rdclass, - rdtype=rdataset.rdtype, - flags=rdata.flags, - protocol=rdata.protocol, - algorithm=rdata.algorithm, - key=rdata.key, - ) - ) - return dns.rdataset.from_rdata_list(rdataset.ttl, res) - - -def default_rrset_signer( - txn: dns.transaction.Transaction, - rrset: dns.rrset.RRset, - signer: dns.name.Name, - ksks: List[Tuple[PrivateKey, DNSKEY]], - zsks: List[Tuple[PrivateKey, DNSKEY]], - inception: Optional[Union[datetime, str, int, float]] = None, - expiration: Optional[Union[datetime, str, int, float]] = None, - lifetime: Optional[int] = None, - policy: Optional[Policy] = None, - origin: Optional[dns.name.Name] = None, -) -> None: - """Default RRset signer""" - - if rrset.rdtype in set( - [ - dns.rdatatype.RdataType.DNSKEY, - dns.rdatatype.RdataType.CDS, - dns.rdatatype.RdataType.CDNSKEY, - ] - ): - keys = ksks - else: - keys = zsks - - for private_key, dnskey in keys: - rrsig = dns.dnssec.sign( - rrset=rrset, - private_key=private_key, - dnskey=dnskey, - inception=inception, - expiration=expiration, - lifetime=lifetime, - signer=signer, - policy=policy, - origin=origin, - ) - txn.add(rrset.name, rrset.ttl, rrsig) - - -def sign_zone( - zone: dns.zone.Zone, - txn: Optional[dns.transaction.Transaction] = None, - keys: Optional[List[Tuple[PrivateKey, DNSKEY]]] = None, - add_dnskey: bool = True, - dnskey_ttl: Optional[int] = None, - inception: Optional[Union[datetime, str, int, float]] = None, - expiration: Optional[Union[datetime, str, int, float]] = None, - lifetime: Optional[int] = None, - nsec3: Optional[NSEC3PARAM] = None, - rrset_signer: Optional[RRsetSigner] = None, - policy: Optional[Policy] = None, -) -> None: - """Sign zone. - - *zone*, a ``dns.zone.Zone``, the zone to sign. - - *txn*, a ``dns.transaction.Transaction``, an optional transaction to use for - signing. - - *keys*, a list of (``PrivateKey``, ``DNSKEY``) tuples, to use for signing. KSK/ZSK - roles are assigned automatically if the SEP flag is used, otherwise all RRsets are - signed by all keys. - - *add_dnskey*, a ``bool``. If ``True``, the default, all specified DNSKEYs are - automatically added to the zone on signing. - - *dnskey_ttl*, a``int``, specifies the TTL for DNSKEY RRs. If not specified the TTL - of the existing DNSKEY RRset used or the TTL of the SOA RRset. - - *inception*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature - inception time. If ``None``, the current time is used. If a ``str``, the format is - "YYYYMMDDHHMMSS" or alternatively the number of seconds since the UNIX epoch in text - form; this is the same the RRSIG rdata's text form. Values of type `int` or `float` - are interpreted as seconds since the UNIX epoch. - - *expiration*, a ``datetime``, ``str``, ``int``, ``float`` or ``None``, the signature - expiration time. If ``None``, the expiration time will be the inception time plus - the value of the *lifetime* parameter. See the description of *inception* above for - how the various parameter types are interpreted. - - *lifetime*, an ``int`` or ``None``, the signature lifetime in seconds. This - parameter is only meaningful if *expiration* is ``None``. - - *nsec3*, a ``NSEC3PARAM`` Rdata, configures signing using NSEC3. Not yet - implemented. - - *rrset_signer*, a ``Callable``, an optional function for signing RRsets. The - function requires two arguments: transaction and RRset. If the not specified, - ``dns.dnssec.default_rrset_signer`` will be used. - - Returns ``None``. - """ - - ksks = [] - zsks = [] - - # if we have both KSKs and ZSKs, split by SEP flag. if not, sign all - # records with all keys - if keys: - for key in keys: - if key[1].flags & Flag.SEP: - ksks.append(key) - else: - zsks.append(key) - if not ksks: - ksks = keys - if not zsks: - zsks = keys - else: - keys = [] - - if txn: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(txn) - else: - cm = zone.writer() - - with cm as _txn: - if add_dnskey: - if dnskey_ttl is None: - dnskey = _txn.get(zone.origin, dns.rdatatype.DNSKEY) - if dnskey: - dnskey_ttl = dnskey.ttl - else: - soa = _txn.get(zone.origin, dns.rdatatype.SOA) - dnskey_ttl = soa.ttl - for _, dnskey in keys: - _txn.add(zone.origin, dnskey_ttl, dnskey) - - if nsec3: - raise NotImplementedError("Signing with NSEC3 not yet implemented") - else: - _rrset_signer = rrset_signer or functools.partial( - default_rrset_signer, - signer=zone.origin, - ksks=ksks, - zsks=zsks, - inception=inception, - expiration=expiration, - lifetime=lifetime, - policy=policy, - origin=zone.origin, - ) - return _sign_zone_nsec(zone, _txn, _rrset_signer) - - -def _sign_zone_nsec( - zone: dns.zone.Zone, - txn: dns.transaction.Transaction, - rrset_signer: Optional[RRsetSigner] = None, -) -> None: - """NSEC zone signer""" - - def _txn_add_nsec( - txn: dns.transaction.Transaction, - name: dns.name.Name, - next_secure: Optional[dns.name.Name], - rdclass: dns.rdataclass.RdataClass, - ttl: int, - rrset_signer: Optional[RRsetSigner] = None, - ) -> None: - """NSEC zone signer helper""" - mandatory_types = set( - [dns.rdatatype.RdataType.RRSIG, dns.rdatatype.RdataType.NSEC] - ) - node = txn.get_node(name) - if node and next_secure: - types = ( - set([rdataset.rdtype for rdataset in node.rdatasets]) | mandatory_types - ) - windows = Bitmap.from_rdtypes(list(types)) - rrset = dns.rrset.from_rdata( - name, - ttl, - NSEC( - rdclass=rdclass, - rdtype=dns.rdatatype.RdataType.NSEC, - next=next_secure, - windows=windows, - ), - ) - txn.add(rrset) - if rrset_signer: - rrset_signer(txn, rrset) - - rrsig_ttl = zone.get_soa().minimum - delegation = None - last_secure = None - - for name in sorted(txn.iterate_names()): - if delegation and name.is_subdomain(delegation): - # names below delegations are not secure - continue - elif txn.get(name, dns.rdatatype.NS) and name != zone.origin: - # inside delegation - delegation = name - else: - # outside delegation - delegation = None - - if rrset_signer: - node = txn.get_node(name) - if node: - for rdataset in node.rdatasets: - if rdataset.rdtype == dns.rdatatype.RRSIG: - # do not sign RRSIGs - continue - elif delegation and rdataset.rdtype != dns.rdatatype.DS: - # do not sign delegations except DS records - continue - else: - rrset = dns.rrset.from_rdata(name, rdataset.ttl, *rdataset) - rrset_signer(txn, rrset) - - # We need "is not None" as the empty name is False because its length is 0. - if last_secure is not None: - _txn_add_nsec(txn, last_secure, name, zone.rdclass, rrsig_ttl, rrset_signer) - last_secure = name - - if last_secure: - _txn_add_nsec( - txn, last_secure, zone.origin, zone.rdclass, rrsig_ttl, rrset_signer - ) - - -def _need_pyca(*args, **kwargs): - raise ImportError( - "DNSSEC validation requires python cryptography" - ) # pragma: no cover - - -if dns._features.have("dnssec"): - from cryptography.exceptions import InvalidSignature - from cryptography.hazmat.primitives.asymmetric import dsa # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import ec # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import ed448 # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import rsa # pylint: disable=W0611 - from cryptography.hazmat.primitives.asymmetric import ( # pylint: disable=W0611 - ed25519, - ) - - from dns.dnssecalgs import ( # pylint: disable=C0412 - get_algorithm_cls, - get_algorithm_cls_from_dnskey, - ) - from dns.dnssecalgs.base import GenericPrivateKey, GenericPublicKey - - validate = _validate # type: ignore - validate_rrsig = _validate_rrsig # type: ignore - sign = _sign - make_dnskey = _make_dnskey - make_cdnskey = _make_cdnskey - _have_pyca = True -else: # pragma: no cover - validate = _need_pyca - validate_rrsig = _need_pyca - sign = _need_pyca - make_dnskey = _need_pyca - make_cdnskey = _need_pyca - _have_pyca = False - -### BEGIN generated Algorithm constants - -RSAMD5 = Algorithm.RSAMD5 -DH = Algorithm.DH -DSA = Algorithm.DSA -ECC = Algorithm.ECC -RSASHA1 = Algorithm.RSASHA1 -DSANSEC3SHA1 = Algorithm.DSANSEC3SHA1 -RSASHA1NSEC3SHA1 = Algorithm.RSASHA1NSEC3SHA1 -RSASHA256 = Algorithm.RSASHA256 -RSASHA512 = Algorithm.RSASHA512 -ECCGOST = Algorithm.ECCGOST -ECDSAP256SHA256 = Algorithm.ECDSAP256SHA256 -ECDSAP384SHA384 = Algorithm.ECDSAP384SHA384 -ED25519 = Algorithm.ED25519 -ED448 = Algorithm.ED448 -INDIRECT = Algorithm.INDIRECT -PRIVATEDNS = Algorithm.PRIVATEDNS -PRIVATEOID = Algorithm.PRIVATEOID - -### END generated Algorithm constants diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__init__.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__init__.py deleted file mode 100644 index 3d9181a..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__init__.py +++ /dev/null @@ -1,120 +0,0 @@ -from typing import Dict, Optional, Tuple, Type, Union - -import dns.name -from dns.dnssecalgs.base import GenericPrivateKey -from dns.dnssectypes import Algorithm -from dns.exception import UnsupportedAlgorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - -if dns._features.have("dnssec"): - from dns.dnssecalgs.dsa import PrivateDSA, PrivateDSANSEC3SHA1 - from dns.dnssecalgs.ecdsa import PrivateECDSAP256SHA256, PrivateECDSAP384SHA384 - from dns.dnssecalgs.eddsa import PrivateED448, PrivateED25519 - from dns.dnssecalgs.rsa import ( - PrivateRSAMD5, - PrivateRSASHA1, - PrivateRSASHA1NSEC3SHA1, - PrivateRSASHA256, - PrivateRSASHA512, - ) - - _have_cryptography = True -else: - _have_cryptography = False - -AlgorithmPrefix = Optional[Union[bytes, dns.name.Name]] - -algorithms: Dict[Tuple[Algorithm, AlgorithmPrefix], Type[GenericPrivateKey]] = {} -if _have_cryptography: - algorithms.update( - { - (Algorithm.RSAMD5, None): PrivateRSAMD5, - (Algorithm.DSA, None): PrivateDSA, - (Algorithm.RSASHA1, None): PrivateRSASHA1, - (Algorithm.DSANSEC3SHA1, None): PrivateDSANSEC3SHA1, - (Algorithm.RSASHA1NSEC3SHA1, None): PrivateRSASHA1NSEC3SHA1, - (Algorithm.RSASHA256, None): PrivateRSASHA256, - (Algorithm.RSASHA512, None): PrivateRSASHA512, - (Algorithm.ECDSAP256SHA256, None): PrivateECDSAP256SHA256, - (Algorithm.ECDSAP384SHA384, None): PrivateECDSAP384SHA384, - (Algorithm.ED25519, None): PrivateED25519, - (Algorithm.ED448, None): PrivateED448, - } - ) - - -def get_algorithm_cls( - algorithm: Union[int, str], prefix: AlgorithmPrefix = None -) -> Type[GenericPrivateKey]: - """Get Private Key class from Algorithm. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Returns a ``dns.dnssecalgs.GenericPrivateKey`` - """ - algorithm = Algorithm.make(algorithm) - cls = algorithms.get((algorithm, prefix)) - if cls: - return cls - raise UnsupportedAlgorithm( - 'algorithm "%s" not supported by dnspython' % Algorithm.to_text(algorithm) - ) - - -def get_algorithm_cls_from_dnskey(dnskey: DNSKEY) -> Type[GenericPrivateKey]: - """Get Private Key class from DNSKEY. - - *dnskey*, a ``DNSKEY`` to get Algorithm class for. - - Raises ``UnsupportedAlgorithm`` if the algorithm is unknown. - - Returns a ``dns.dnssecalgs.GenericPrivateKey`` - """ - prefix: AlgorithmPrefix = None - if dnskey.algorithm == Algorithm.PRIVATEDNS: - prefix, _ = dns.name.from_wire(dnskey.key, 0) - elif dnskey.algorithm == Algorithm.PRIVATEOID: - length = int(dnskey.key[0]) - prefix = dnskey.key[0 : length + 1] - return get_algorithm_cls(dnskey.algorithm, prefix) - - -def register_algorithm_cls( - algorithm: Union[int, str], - algorithm_cls: Type[GenericPrivateKey], - name: Optional[Union[dns.name.Name, str]] = None, - oid: Optional[bytes] = None, -) -> None: - """Register Algorithm Private Key class. - - *algorithm*, a ``str`` or ``int`` specifying the DNSKEY algorithm. - - *algorithm_cls*: A `GenericPrivateKey` class. - - *name*, an optional ``dns.name.Name`` or ``str``, for for PRIVATEDNS algorithms. - - *oid*: an optional BER-encoded `bytes` for PRIVATEOID algorithms. - - Raises ``ValueError`` if a name or oid is specified incorrectly. - """ - if not issubclass(algorithm_cls, GenericPrivateKey): - raise TypeError("Invalid algorithm class") - algorithm = Algorithm.make(algorithm) - prefix: AlgorithmPrefix = None - if algorithm == Algorithm.PRIVATEDNS: - if name is None: - raise ValueError("Name required for PRIVATEDNS algorithms") - if isinstance(name, str): - name = dns.name.from_text(name) - prefix = name - elif algorithm == Algorithm.PRIVATEOID: - if oid is None: - raise ValueError("OID required for PRIVATEOID algorithms") - prefix = bytes([len(oid)]) + oid - elif name: - raise ValueError("Name only supported for PRIVATEDNS algorithm") - elif oid: - raise ValueError("OID only supported for PRIVATEOID algorithm") - algorithms[(algorithm, prefix)] = algorithm_cls diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d1aa1e88998b154cbb0e82b05daf81bf2f62e133..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5516 zcmc&1T~8cGa%N}0KUnsI#ejh^7|deW4cOq1Bo`6$VaEw^fS_cQ+_K(jVDNrj&p@(h zx#OZp2TuVh5=3Vy8|kDY@E#>9%M&z7M%F|WP?(8gJ?oPUg z8)>?$tE#K2yQ`|J|KjmDDfkAb9?bu_o}#`d1N}Qw0iI7W6tzRWOYsy>C#e+8(J2#W zN--RhGIQpXg|pCvW=dK)8}v-lo^o&w4QEa|Q!dV>!Iq>u<>5RUY)#gsyquS&kZp<8 z6&n3grvd8_Z+k$OVH^f6_M|@*-~y>27t|y=k`1X47fLm9joR3mJd}m zTT*Aav#D0DHPyzorP{gn6w9$db93j?Cf>uHLndTF9bnP8uG8q3ZW^!-&6}D4BtO?B zP+T{n5rbT)tBglx`~YFQ;` zLklz9`i#-bm#?aI)zRS4(7-iVr>BCMnjU#;bg0SzL!we`G{|IJwMDHzLLfFA#S9G$ z&Wx+hxV{sj>Zro1HH*=_@Ud#eNLt3}gsH}ttN=r9qn`f(njK22f~1ygI%M>DDo@Xws@$f$3Bx~4Ne-Qdr)zX+8Ca*3dqdYM*r-eTgbtfl|2}~v zwz45C_yC(o#zm2x$C(scJ@bgu$->uL-G<&iHqOq?i4vZhV>1{)AuRzYW>G?z&k5-T zR$4@C*-zN&w2GXmxF8}CCcVhNK#DN0&8|*k1(97&-%DpcO6wA|%@;KzzzHK|M}!Gt zy6v@%UF8%yobTN}cQJjS-nH3?i7)@k&6n#q&6Vb>DO0Ptd z!rf?D)uqV5U{n+&)Srzf?!_077zH!P4}nG26OF}$v>?S|k!()&ffcdpGGd?ZofXfe(S&){4BB=*$o$F<)*%UXaD+*$2Mol>)&u5INM70 zOMeoI!xPHz#CMMU;VH#F^_(%etcR4zVtoz*DJ{nDpdWV0YjaU!(KX z5@A2~NmajJs(QG=a<2!rFCvks(Y__S4jKKD%YbzVbh&ld=y}tFinb4+U!&Jd>52bn zRh= zOAkx%7C53xOqNLyMN)wd8BuY2I$$9VuA%d|x?b-^C5?@g=3(4P zEcYA@`7psT8G%pK29*V{T2Owc1*LHzn_Pt)yC9l^Xk~VNrLA>`O|I0?UG-|Ed_PR=$9G|ScmdQ zuE{lXdiscs`V&NFhhDR>JI@@k(OhF=i_R>y>QKJAhK1_h<+U&ejm>a9*RI!M$jUQE zDApPZEwxppQ+e~ei4^xx)Y;-2aGX@Pvzbcm33oh%SA-BElh8m~(y9fmaOh*_VL*N(_KjLW)TM%>gZf&b zua8ajqcpixd3Nq@PD0{btr*A+C&g52j@fu}8I56_A*Bmc8gZ6bN6Z36q|zwYFapRH zKnKPsAtiGWCrKo$jknS(@g(q7ce%U+ldBiV@>q<1wG6q6uQdlUn!zrvb`$en#CO!} z)jmyNCY{XHR4J!z*6Kwv`*h|bvcvVNQxL`FyIQG-$*rq8$m2lU6|MBa#Mki!LU9@! zMy3gAQHrM%kdEODG2Gx3*78v~QIo<5_mOE9?Oap_Qn6M+g)QY|KcLURg9IYglSon# z+1^T`4&jQsA1if8NRVf%s6Ydy&Uwlm+Dbj@8h+%ua!}VOpBdP%8;?WcS2@t6_`3f8&vhJqUz$u5Mo~2D+3$ zSApLTT-+Ef`5HE@C5QiWp&05_LcI?iy(L%O#><+7v3>uz>>fYxh3+%EH-2y5THW_` z6s8ql&!+XU!*ft~MsAweue&MRZ$5Uowc+@F-GpqPD0N@@^o=dY?y&6bcxdj_Qhk`7 z44Wn=!`D=+l*>XHjY&$!B+p~AOPCZ(m?UY{t_PNO2x@=~;jfg9k-Kpbm5F`^C%8a9t2nOVtfUuenT!q11J~c zn5213iYhgDjEWM3P7*#Qnf@f5Cv;MEVUqPvNXI}W^%EvH0d62oh&1F9Q2jAtQ7nOT zSt+xCT6tA6 zWcb^&WroRuI+4u4)?_)n2u#WsE#_aX&HsDrq`M zLuwOD-2ZDza}%wg`Pu$TD#I@)(JwFuBvMg{CMd2B8Jeb_QbT{EhQ6n~PpE+>RM!(K z@`MUMW30b>^|!AUnFfVv*jjwVv_Cb!{?t7E)I9P}lhtDV2L;VxJ=NT%m_yH++GJ?` z&AUT|hCSvR``7kwJYRd{b3;nYu;Rb6?tS+5JL}Gk(XEE9Tp=nq4k*6CZ%5^8Zzz{< z%f8!+`^|mooySZ=$y`@7Hz?+YXP!{e)2et{cO}_^@hx4x^%{ zP4To9%(Aio$Ckme)*0&5UcI{*Lx diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/base.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/base.cpython-312.pyc deleted file mode 100644 index 6e73eb8712d07ae1db6d6454cdf68a803ff8492a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4560 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 CXAHmq diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-312.pyc deleted file mode 100644 index af6ac052401980001de1f601fbf32950eff92af2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5977 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 vJzEU4TNl`N=38XrJ*#N|fJCdmI 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 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/base.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/base.py deleted file mode 100644 index e990575..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/base.py +++ /dev/null @@ -1,84 +0,0 @@ -from abc import ABC, abstractmethod # pylint: disable=no-name-in-module -from typing import Any, Optional, Type - -import dns.rdataclass -import dns.rdatatype -from dns.dnssectypes import Algorithm -from dns.exception import AlgorithmKeyMismatch -from dns.rdtypes.ANY.DNSKEY import DNSKEY -from dns.rdtypes.dnskeybase import Flag - - -class GenericPublicKey(ABC): - algorithm: Algorithm - - @abstractmethod - def __init__(self, key: Any) -> None: - pass - - @abstractmethod - def verify(self, signature: bytes, data: bytes) -> None: - """Verify signed DNSSEC data""" - - @abstractmethod - def encode_key_bytes(self) -> bytes: - """Encode key as bytes for DNSKEY""" - - @classmethod - def _ensure_algorithm_key_combination(cls, key: DNSKEY) -> None: - if key.algorithm != cls.algorithm: - raise AlgorithmKeyMismatch - - def to_dnskey(self, flags: int = Flag.ZONE, protocol: int = 3) -> DNSKEY: - """Return public key as DNSKEY""" - return DNSKEY( - rdclass=dns.rdataclass.IN, - rdtype=dns.rdatatype.DNSKEY, - flags=flags, - protocol=protocol, - algorithm=self.algorithm, - key=self.encode_key_bytes(), - ) - - @classmethod - @abstractmethod - def from_dnskey(cls, key: DNSKEY) -> "GenericPublicKey": - """Create public key from DNSKEY""" - - @classmethod - @abstractmethod - def from_pem(cls, public_pem: bytes) -> "GenericPublicKey": - """Create public key from PEM-encoded SubjectPublicKeyInfo as specified - in RFC 5280""" - - @abstractmethod - def to_pem(self) -> bytes: - """Return public-key as PEM-encoded SubjectPublicKeyInfo as specified - in RFC 5280""" - - -class GenericPrivateKey(ABC): - public_cls: Type[GenericPublicKey] - - @abstractmethod - def __init__(self, key: Any) -> None: - pass - - @abstractmethod - def sign(self, data: bytes, verify: bool = False) -> bytes: - """Sign DNSSEC data""" - - @abstractmethod - def public_key(self) -> "GenericPublicKey": - """Return public key instance""" - - @classmethod - @abstractmethod - def from_pem( - cls, private_pem: bytes, password: Optional[bytes] = None - ) -> "GenericPrivateKey": - """Create private key from PEM-encoded PKCS#8""" - - @abstractmethod - def to_pem(self, password: Optional[bytes] = None) -> bytes: - """Return private key as PEM-encoded PKCS#8""" diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/cryptography.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/cryptography.py deleted file mode 100644 index 5a31a81..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/cryptography.py +++ /dev/null @@ -1,68 +0,0 @@ -from typing import Any, Optional, Type - -from cryptography.hazmat.primitives import serialization - -from dns.dnssecalgs.base import GenericPrivateKey, GenericPublicKey -from dns.exception import AlgorithmKeyMismatch - - -class CryptographyPublicKey(GenericPublicKey): - key: Any = None - key_cls: Any = None - - def __init__(self, key: Any) -> None: # pylint: disable=super-init-not-called - if self.key_cls is None: - raise TypeError("Undefined private key class") - if not isinstance( # pylint: disable=isinstance-second-argument-not-valid-type - key, self.key_cls - ): - raise AlgorithmKeyMismatch - self.key = key - - @classmethod - def from_pem(cls, public_pem: bytes) -> "GenericPublicKey": - key = serialization.load_pem_public_key(public_pem) - return cls(key=key) - - def to_pem(self) -> bytes: - return self.key.public_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo, - ) - - -class CryptographyPrivateKey(GenericPrivateKey): - key: Any = None - key_cls: Any = None - public_cls: Type[CryptographyPublicKey] - - def __init__(self, key: Any) -> None: # pylint: disable=super-init-not-called - if self.key_cls is None: - raise TypeError("Undefined private key class") - if not isinstance( # pylint: disable=isinstance-second-argument-not-valid-type - key, self.key_cls - ): - raise AlgorithmKeyMismatch - self.key = key - - def public_key(self) -> "CryptographyPublicKey": - return self.public_cls(key=self.key.public_key()) - - @classmethod - def from_pem( - cls, private_pem: bytes, password: Optional[bytes] = None - ) -> "GenericPrivateKey": - key = serialization.load_pem_private_key(private_pem, password=password) - return cls(key=key) - - def to_pem(self, password: Optional[bytes] = None) -> bytes: - encryption_algorithm: serialization.KeySerializationEncryption - if password: - encryption_algorithm = serialization.BestAvailableEncryption(password) - else: - encryption_algorithm = serialization.NoEncryption() - return self.key.private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.PKCS8, - encryption_algorithm=encryption_algorithm, - ) diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/dsa.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/dsa.py deleted file mode 100644 index 0fe4690..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/dsa.py +++ /dev/null @@ -1,101 +0,0 @@ -import struct - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import dsa, utils - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicDSA(CryptographyPublicKey): - key: dsa.DSAPublicKey - key_cls = dsa.DSAPublicKey - algorithm = Algorithm.DSA - chosen_hash = hashes.SHA1() - - def verify(self, signature: bytes, data: bytes) -> None: - sig_r = signature[1:21] - sig_s = signature[21:] - sig = utils.encode_dss_signature( - int.from_bytes(sig_r, "big"), int.from_bytes(sig_s, "big") - ) - self.key.verify(sig, data, self.chosen_hash) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 2536, section 2.""" - pn = self.key.public_numbers() - dsa_t = (self.key.key_size // 8 - 64) // 8 - if dsa_t > 8: - raise ValueError("unsupported DSA key size") - octets = 64 + dsa_t * 8 - res = struct.pack("!B", dsa_t) - res += pn.parameter_numbers.q.to_bytes(20, "big") - res += pn.parameter_numbers.p.to_bytes(octets, "big") - res += pn.parameter_numbers.g.to_bytes(octets, "big") - res += pn.y.to_bytes(octets, "big") - return res - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicDSA": - cls._ensure_algorithm_key_combination(key) - keyptr = key.key - (t,) = struct.unpack("!B", keyptr[0:1]) - keyptr = keyptr[1:] - octets = 64 + t * 8 - dsa_q = keyptr[0:20] - keyptr = keyptr[20:] - dsa_p = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_g = keyptr[0:octets] - keyptr = keyptr[octets:] - dsa_y = keyptr[0:octets] - return cls( - key=dsa.DSAPublicNumbers( # type: ignore - int.from_bytes(dsa_y, "big"), - dsa.DSAParameterNumbers( - int.from_bytes(dsa_p, "big"), - int.from_bytes(dsa_q, "big"), - int.from_bytes(dsa_g, "big"), - ), - ).public_key(default_backend()), - ) - - -class PrivateDSA(CryptographyPrivateKey): - key: dsa.DSAPrivateKey - key_cls = dsa.DSAPrivateKey - public_cls = PublicDSA - - def sign(self, data: bytes, verify: bool = False) -> bytes: - """Sign using a private key per RFC 2536, section 3.""" - public_dsa_key = self.key.public_key() - if public_dsa_key.key_size > 1024: - raise ValueError("DSA key size overflow") - der_signature = self.key.sign(data, self.public_cls.chosen_hash) - dsa_r, dsa_s = utils.decode_dss_signature(der_signature) - dsa_t = (public_dsa_key.key_size // 8 - 64) // 8 - octets = 20 - signature = ( - struct.pack("!B", dsa_t) - + int.to_bytes(dsa_r, length=octets, byteorder="big") - + int.to_bytes(dsa_s, length=octets, byteorder="big") - ) - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls, key_size: int) -> "PrivateDSA": - return cls( - key=dsa.generate_private_key(key_size=key_size), - ) - - -class PublicDSANSEC3SHA1(PublicDSA): - algorithm = Algorithm.DSANSEC3SHA1 - - -class PrivateDSANSEC3SHA1(PrivateDSA): - public_cls = PublicDSANSEC3SHA1 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/ecdsa.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/ecdsa.py deleted file mode 100644 index a31d79f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/ecdsa.py +++ /dev/null @@ -1,89 +0,0 @@ -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import ec, utils - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicECDSA(CryptographyPublicKey): - key: ec.EllipticCurvePublicKey - key_cls = ec.EllipticCurvePublicKey - algorithm: Algorithm - chosen_hash: hashes.HashAlgorithm - curve: ec.EllipticCurve - octets: int - - def verify(self, signature: bytes, data: bytes) -> None: - sig_r = signature[0 : self.octets] - sig_s = signature[self.octets :] - sig = utils.encode_dss_signature( - int.from_bytes(sig_r, "big"), int.from_bytes(sig_s, "big") - ) - self.key.verify(sig, data, ec.ECDSA(self.chosen_hash)) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 6605, section 4.""" - pn = self.key.public_numbers() - return pn.x.to_bytes(self.octets, "big") + pn.y.to_bytes(self.octets, "big") - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicECDSA": - cls._ensure_algorithm_key_combination(key) - ecdsa_x = key.key[0 : cls.octets] - ecdsa_y = key.key[cls.octets : cls.octets * 2] - return cls( - key=ec.EllipticCurvePublicNumbers( - curve=cls.curve, - x=int.from_bytes(ecdsa_x, "big"), - y=int.from_bytes(ecdsa_y, "big"), - ).public_key(default_backend()), - ) - - -class PrivateECDSA(CryptographyPrivateKey): - key: ec.EllipticCurvePrivateKey - key_cls = ec.EllipticCurvePrivateKey - public_cls = PublicECDSA - - def sign(self, data: bytes, verify: bool = False) -> bytes: - """Sign using a private key per RFC 6605, section 4.""" - der_signature = self.key.sign(data, ec.ECDSA(self.public_cls.chosen_hash)) - dsa_r, dsa_s = utils.decode_dss_signature(der_signature) - signature = int.to_bytes( - dsa_r, length=self.public_cls.octets, byteorder="big" - ) + int.to_bytes(dsa_s, length=self.public_cls.octets, byteorder="big") - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls) -> "PrivateECDSA": - return cls( - key=ec.generate_private_key( - curve=cls.public_cls.curve, backend=default_backend() - ), - ) - - -class PublicECDSAP256SHA256(PublicECDSA): - algorithm = Algorithm.ECDSAP256SHA256 - chosen_hash = hashes.SHA256() - curve = ec.SECP256R1() - octets = 32 - - -class PrivateECDSAP256SHA256(PrivateECDSA): - public_cls = PublicECDSAP256SHA256 - - -class PublicECDSAP384SHA384(PublicECDSA): - algorithm = Algorithm.ECDSAP384SHA384 - chosen_hash = hashes.SHA384() - curve = ec.SECP384R1() - octets = 48 - - -class PrivateECDSAP384SHA384(PrivateECDSA): - public_cls = PublicECDSAP384SHA384 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/eddsa.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/eddsa.py deleted file mode 100644 index 7050534..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/eddsa.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import Type - -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ed448, ed25519 - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicEDDSA(CryptographyPublicKey): - def verify(self, signature: bytes, data: bytes) -> None: - self.key.verify(signature, data) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 8080, section 3.""" - return self.key.public_bytes( - encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw - ) - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicEDDSA": - cls._ensure_algorithm_key_combination(key) - return cls( - key=cls.key_cls.from_public_bytes(key.key), - ) - - -class PrivateEDDSA(CryptographyPrivateKey): - public_cls: Type[PublicEDDSA] - - def sign(self, data: bytes, verify: bool = False) -> bytes: - """Sign using a private key per RFC 8080, section 4.""" - signature = self.key.sign(data) - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls) -> "PrivateEDDSA": - return cls(key=cls.key_cls.generate()) - - -class PublicED25519(PublicEDDSA): - key: ed25519.Ed25519PublicKey - key_cls = ed25519.Ed25519PublicKey - algorithm = Algorithm.ED25519 - - -class PrivateED25519(PrivateEDDSA): - key: ed25519.Ed25519PrivateKey - key_cls = ed25519.Ed25519PrivateKey - public_cls = PublicED25519 - - -class PublicED448(PublicEDDSA): - key: ed448.Ed448PublicKey - key_cls = ed448.Ed448PublicKey - algorithm = Algorithm.ED448 - - -class PrivateED448(PrivateEDDSA): - key: ed448.Ed448PrivateKey - key_cls = ed448.Ed448PrivateKey - public_cls = PublicED448 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/rsa.py b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/rsa.py deleted file mode 100644 index e95dcf1..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/rsa.py +++ /dev/null @@ -1,119 +0,0 @@ -import math -import struct - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric import padding, rsa - -from dns.dnssecalgs.cryptography import CryptographyPrivateKey, CryptographyPublicKey -from dns.dnssectypes import Algorithm -from dns.rdtypes.ANY.DNSKEY import DNSKEY - - -class PublicRSA(CryptographyPublicKey): - key: rsa.RSAPublicKey - key_cls = rsa.RSAPublicKey - algorithm: Algorithm - chosen_hash: hashes.HashAlgorithm - - def verify(self, signature: bytes, data: bytes) -> None: - self.key.verify(signature, data, padding.PKCS1v15(), self.chosen_hash) - - def encode_key_bytes(self) -> bytes: - """Encode a public key per RFC 3110, section 2.""" - pn = self.key.public_numbers() - _exp_len = math.ceil(int.bit_length(pn.e) / 8) - exp = int.to_bytes(pn.e, length=_exp_len, byteorder="big") - if _exp_len > 255: - exp_header = b"\0" + struct.pack("!H", _exp_len) - else: - exp_header = struct.pack("!B", _exp_len) - if pn.n.bit_length() < 512 or pn.n.bit_length() > 4096: - raise ValueError("unsupported RSA key length") - return exp_header + exp + pn.n.to_bytes((pn.n.bit_length() + 7) // 8, "big") - - @classmethod - def from_dnskey(cls, key: DNSKEY) -> "PublicRSA": - cls._ensure_algorithm_key_combination(key) - keyptr = key.key - (bytes_,) = struct.unpack("!B", keyptr[0:1]) - keyptr = keyptr[1:] - if bytes_ == 0: - (bytes_,) = struct.unpack("!H", keyptr[0:2]) - keyptr = keyptr[2:] - rsa_e = keyptr[0:bytes_] - rsa_n = keyptr[bytes_:] - return cls( - key=rsa.RSAPublicNumbers( - int.from_bytes(rsa_e, "big"), int.from_bytes(rsa_n, "big") - ).public_key(default_backend()) - ) - - -class PrivateRSA(CryptographyPrivateKey): - key: rsa.RSAPrivateKey - key_cls = rsa.RSAPrivateKey - public_cls = PublicRSA - default_public_exponent = 65537 - - def sign(self, data: bytes, verify: bool = False) -> bytes: - """Sign using a private key per RFC 3110, section 3.""" - signature = self.key.sign(data, padding.PKCS1v15(), self.public_cls.chosen_hash) - if verify: - self.public_key().verify(signature, data) - return signature - - @classmethod - def generate(cls, key_size: int) -> "PrivateRSA": - return cls( - key=rsa.generate_private_key( - public_exponent=cls.default_public_exponent, - key_size=key_size, - backend=default_backend(), - ) - ) - - -class PublicRSAMD5(PublicRSA): - algorithm = Algorithm.RSAMD5 - chosen_hash = hashes.MD5() - - -class PrivateRSAMD5(PrivateRSA): - public_cls = PublicRSAMD5 - - -class PublicRSASHA1(PublicRSA): - algorithm = Algorithm.RSASHA1 - chosen_hash = hashes.SHA1() - - -class PrivateRSASHA1(PrivateRSA): - public_cls = PublicRSASHA1 - - -class PublicRSASHA1NSEC3SHA1(PublicRSA): - algorithm = Algorithm.RSASHA1NSEC3SHA1 - chosen_hash = hashes.SHA1() - - -class PrivateRSASHA1NSEC3SHA1(PrivateRSA): - public_cls = PublicRSASHA1NSEC3SHA1 - - -class PublicRSASHA256(PublicRSA): - algorithm = Algorithm.RSASHA256 - chosen_hash = hashes.SHA256() - - -class PrivateRSASHA256(PrivateRSA): - public_cls = PublicRSASHA256 - - -class PublicRSASHA512(PublicRSA): - algorithm = Algorithm.RSASHA512 - chosen_hash = hashes.SHA512() - - -class PrivateRSASHA512(PrivateRSA): - public_cls = PublicRSASHA512 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssectypes.py b/.venv/lib/python3.12/site-packages/dns/dnssectypes.py deleted file mode 100644 index 02131e0..0000000 --- a/.venv/lib/python3.12/site-packages/dns/dnssectypes.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNSSEC-related types.""" - -# This is a separate file to avoid import circularity between dns.dnssec and -# the implementations of the DS and DNSKEY types. - -import dns.enum - - -class Algorithm(dns.enum.IntEnum): - RSAMD5 = 1 - DH = 2 - DSA = 3 - ECC = 4 - RSASHA1 = 5 - DSANSEC3SHA1 = 6 - RSASHA1NSEC3SHA1 = 7 - RSASHA256 = 8 - RSASHA512 = 10 - ECCGOST = 12 - ECDSAP256SHA256 = 13 - ECDSAP384SHA384 = 14 - ED25519 = 15 - ED448 = 16 - INDIRECT = 252 - PRIVATEDNS = 253 - PRIVATEOID = 254 - - @classmethod - def _maximum(cls): - return 255 - - -class DSDigest(dns.enum.IntEnum): - """DNSSEC Delegation Signer Digest Algorithm""" - - NULL = 0 - SHA1 = 1 - SHA256 = 2 - GOST = 3 - SHA384 = 4 - - @classmethod - def _maximum(cls): - return 255 - - -class NSEC3Hash(dns.enum.IntEnum): - """NSEC3 hash algorithm""" - - SHA1 = 1 - - @classmethod - def _maximum(cls): - return 255 diff --git a/.venv/lib/python3.12/site-packages/dns/e164.py b/.venv/lib/python3.12/site-packages/dns/e164.py deleted file mode 100644 index 453736d..0000000 --- a/.venv/lib/python3.12/site-packages/dns/e164.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS E.164 helpers.""" - -from typing import Iterable, Optional, Union - -import dns.exception -import dns.name -import dns.resolver - -#: The public E.164 domain. -public_enum_domain = dns.name.from_text("e164.arpa.") - - -def from_e164( - text: str, origin: Optional[dns.name.Name] = public_enum_domain -) -> dns.name.Name: - """Convert an E.164 number in textual form into a Name object whose - value is the ENUM domain name for that number. - - Non-digits in the text are ignored, i.e. "16505551212", - "+1.650.555.1212" and "1 (650) 555-1212" are all the same. - - *text*, a ``str``, is an E.164 number in textual form. - - *origin*, a ``dns.name.Name``, the domain in which the number - should be constructed. The default is ``e164.arpa.``. - - Returns a ``dns.name.Name``. - """ - - parts = [d for d in text if d.isdigit()] - parts.reverse() - return dns.name.from_text(".".join(parts), origin=origin) - - -def to_e164( - name: dns.name.Name, - origin: Optional[dns.name.Name] = public_enum_domain, - want_plus_prefix: bool = True, -) -> str: - """Convert an ENUM domain name into an E.164 number. - - Note that dnspython does not have any information about preferred - number formats within national numbering plans, so all numbers are - emitted as a simple string of digits, prefixed by a '+' (unless - *want_plus_prefix* is ``False``). - - *name* is a ``dns.name.Name``, the ENUM domain name. - - *origin* is a ``dns.name.Name``, a domain containing the ENUM - domain name. The name is relativized to this domain before being - converted to text. If ``None``, no relativization is done. - - *want_plus_prefix* is a ``bool``. If True, add a '+' to the beginning of - the returned number. - - Returns a ``str``. - - """ - if origin is not None: - name = name.relativize(origin) - dlabels = [d for d in name.labels if d.isdigit() and len(d) == 1] - if len(dlabels) != len(name.labels): - raise dns.exception.SyntaxError("non-digit labels in ENUM domain name") - dlabels.reverse() - text = b"".join(dlabels) - if want_plus_prefix: - text = b"+" + text - return text.decode() - - -def query( - number: str, - domains: Iterable[Union[dns.name.Name, str]], - resolver: Optional[dns.resolver.Resolver] = None, -) -> dns.resolver.Answer: - """Look for NAPTR RRs for the specified number in the specified domains. - - e.g. lookup('16505551212', ['e164.dnspython.org.', 'e164.arpa.']) - - *number*, a ``str`` is the number to look for. - - *domains* is an iterable containing ``dns.name.Name`` values. - - *resolver*, a ``dns.resolver.Resolver``, is the resolver to use. If - ``None``, the default resolver is used. - """ - - if resolver is None: - resolver = dns.resolver.get_default_resolver() - e_nx = dns.resolver.NXDOMAIN() - for domain in domains: - if isinstance(domain, str): - domain = dns.name.from_text(domain) - qname = dns.e164.from_e164(number, domain) - try: - return resolver.resolve(qname, "NAPTR") - except dns.resolver.NXDOMAIN as e: - e_nx += e - raise e_nx diff --git a/.venv/lib/python3.12/site-packages/dns/edns.py b/.venv/lib/python3.12/site-packages/dns/edns.py deleted file mode 100644 index 776e5ee..0000000 --- a/.venv/lib/python3.12/site-packages/dns/edns.py +++ /dev/null @@ -1,516 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2009-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""EDNS Options""" - -import binascii -import math -import socket -import struct -from typing import Any, Dict, Optional, Union - -import dns.enum -import dns.inet -import dns.rdata -import dns.wire - - -class OptionType(dns.enum.IntEnum): - #: NSID - NSID = 3 - #: DAU - DAU = 5 - #: DHU - DHU = 6 - #: N3U - N3U = 7 - #: ECS (client-subnet) - ECS = 8 - #: EXPIRE - EXPIRE = 9 - #: COOKIE - COOKIE = 10 - #: KEEPALIVE - KEEPALIVE = 11 - #: PADDING - PADDING = 12 - #: CHAIN - CHAIN = 13 - #: EDE (extended-dns-error) - EDE = 15 - - @classmethod - def _maximum(cls): - return 65535 - - -class Option: - """Base class for all EDNS option types.""" - - def __init__(self, otype: Union[OptionType, str]): - """Initialize an option. - - *otype*, a ``dns.edns.OptionType``, is the option type. - """ - self.otype = OptionType.make(otype) - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - """Convert an option to wire format. - - Returns a ``bytes`` or ``None``. - - """ - raise NotImplementedError # pragma: no cover - - def to_text(self) -> str: - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser(cls, otype: OptionType, parser: "dns.wire.Parser") -> "Option": - """Build an EDNS option object from wire format. - - *otype*, a ``dns.edns.OptionType``, is the option type. - - *parser*, a ``dns.wire.Parser``, the parser, which should be - restructed to the option length. - - Returns a ``dns.edns.Option``. - """ - raise NotImplementedError # pragma: no cover - - def _cmp(self, other): - """Compare an EDNS option with another option of the same type. - - Returns < 0 if < *other*, 0 if == *other*, and > 0 if > *other*. - """ - wire = self.to_wire() - owire = other.to_wire() - if wire == owire: - return 0 - if wire > owire: - return 1 - return -1 - - def __eq__(self, other): - if not isinstance(other, Option): - return False - if self.otype != other.otype: - return False - return self._cmp(other) == 0 - - def __ne__(self, other): - if not isinstance(other, Option): - return True - if self.otype != other.otype: - return True - return self._cmp(other) != 0 - - def __lt__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) < 0 - - def __le__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) <= 0 - - def __ge__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) >= 0 - - def __gt__(self, other): - if not isinstance(other, Option) or self.otype != other.otype: - return NotImplemented - return self._cmp(other) > 0 - - def __str__(self): - return self.to_text() - - -class GenericOption(Option): # lgtm[py/missing-equals] - """Generic Option Class - - This class is used for EDNS option types for which we have no better - implementation. - """ - - def __init__(self, otype: Union[OptionType, str], data: Union[bytes, str]): - super().__init__(otype) - self.data = dns.rdata.Rdata._as_bytes(data, True) - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - if file: - file.write(self.data) - return None - else: - return self.data - - def to_text(self) -> str: - return "Generic %d" % self.otype - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" - ) -> Option: - return cls(otype, parser.get_remaining()) - - -class ECSOption(Option): # lgtm[py/missing-equals] - """EDNS Client Subnet (ECS, RFC7871)""" - - def __init__(self, address: str, srclen: Optional[int] = None, scopelen: int = 0): - """*address*, a ``str``, is the client address information. - - *srclen*, an ``int``, the source prefix length, which is the - leftmost number of bits of the address to be used for the - lookup. The default is 24 for IPv4 and 56 for IPv6. - - *scopelen*, an ``int``, the scope prefix length. This value - must be 0 in queries, and should be set in responses. - """ - - super().__init__(OptionType.ECS) - af = dns.inet.af_for_address(address) - - if af == socket.AF_INET6: - self.family = 2 - if srclen is None: - srclen = 56 - address = dns.rdata.Rdata._as_ipv6_address(address) - srclen = dns.rdata.Rdata._as_int(srclen, 0, 128) - scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 128) - elif af == socket.AF_INET: - self.family = 1 - if srclen is None: - srclen = 24 - address = dns.rdata.Rdata._as_ipv4_address(address) - srclen = dns.rdata.Rdata._as_int(srclen, 0, 32) - scopelen = dns.rdata.Rdata._as_int(scopelen, 0, 32) - else: # pragma: no cover (this will never happen) - raise ValueError("Bad address family") - - assert srclen is not None - self.address = address - self.srclen = srclen - self.scopelen = scopelen - - addrdata = dns.inet.inet_pton(af, address) - nbytes = int(math.ceil(srclen / 8.0)) - - # Truncate to srclen and pad to the end of the last octet needed - # See RFC section 6 - self.addrdata = addrdata[:nbytes] - nbits = srclen % 8 - if nbits != 0: - last = struct.pack("B", ord(self.addrdata[-1:]) & (0xFF << (8 - nbits))) - self.addrdata = self.addrdata[:-1] + last - - def to_text(self) -> str: - return "ECS {}/{} scope/{}".format(self.address, self.srclen, self.scopelen) - - @staticmethod - def from_text(text: str) -> Option: - """Convert a string into a `dns.edns.ECSOption` - - *text*, a `str`, the text form of the option. - - Returns a `dns.edns.ECSOption`. - - Examples: - - >>> import dns.edns - >>> - >>> # basic example - >>> dns.edns.ECSOption.from_text('1.2.3.4/24') - >>> - >>> # also understands scope - >>> dns.edns.ECSOption.from_text('1.2.3.4/24/32') - >>> - >>> # IPv6 - >>> dns.edns.ECSOption.from_text('2001:4b98::1/64/64') - >>> - >>> # it understands results from `dns.edns.ECSOption.to_text()` - >>> dns.edns.ECSOption.from_text('ECS 1.2.3.4/24/32') - """ - optional_prefix = "ECS" - tokens = text.split() - ecs_text = None - if len(tokens) == 1: - ecs_text = tokens[0] - elif len(tokens) == 2: - if tokens[0] != optional_prefix: - raise ValueError('could not parse ECS from "{}"'.format(text)) - ecs_text = tokens[1] - else: - raise ValueError('could not parse ECS from "{}"'.format(text)) - n_slashes = ecs_text.count("/") - if n_slashes == 1: - address, tsrclen = ecs_text.split("/") - tscope = "0" - elif n_slashes == 2: - address, tsrclen, tscope = ecs_text.split("/") - else: - raise ValueError('could not parse ECS from "{}"'.format(text)) - try: - scope = int(tscope) - except ValueError: - raise ValueError( - "invalid scope " + '"{}": scope must be an integer'.format(tscope) - ) - try: - srclen = int(tsrclen) - except ValueError: - raise ValueError( - "invalid srclen " + '"{}": srclen must be an integer'.format(tsrclen) - ) - return ECSOption(address, srclen, scope) - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - value = ( - struct.pack("!HBB", self.family, self.srclen, self.scopelen) + self.addrdata - ) - if file: - file.write(value) - return None - else: - return value - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" - ) -> Option: - family, src, scope = parser.get_struct("!HBB") - addrlen = int(math.ceil(src / 8.0)) - prefix = parser.get_bytes(addrlen) - if family == 1: - pad = 4 - addrlen - addr = dns.ipv4.inet_ntoa(prefix + b"\x00" * pad) - elif family == 2: - pad = 16 - addrlen - addr = dns.ipv6.inet_ntoa(prefix + b"\x00" * pad) - else: - raise ValueError("unsupported family") - - return cls(addr, src, scope) - - -class EDECode(dns.enum.IntEnum): - OTHER = 0 - UNSUPPORTED_DNSKEY_ALGORITHM = 1 - UNSUPPORTED_DS_DIGEST_TYPE = 2 - STALE_ANSWER = 3 - FORGED_ANSWER = 4 - DNSSEC_INDETERMINATE = 5 - DNSSEC_BOGUS = 6 - SIGNATURE_EXPIRED = 7 - SIGNATURE_NOT_YET_VALID = 8 - DNSKEY_MISSING = 9 - RRSIGS_MISSING = 10 - NO_ZONE_KEY_BIT_SET = 11 - NSEC_MISSING = 12 - CACHED_ERROR = 13 - NOT_READY = 14 - BLOCKED = 15 - CENSORED = 16 - FILTERED = 17 - PROHIBITED = 18 - STALE_NXDOMAIN_ANSWER = 19 - NOT_AUTHORITATIVE = 20 - NOT_SUPPORTED = 21 - NO_REACHABLE_AUTHORITY = 22 - NETWORK_ERROR = 23 - INVALID_DATA = 24 - - @classmethod - def _maximum(cls): - return 65535 - - -class EDEOption(Option): # lgtm[py/missing-equals] - """Extended DNS Error (EDE, RFC8914)""" - - _preserve_case = {"DNSKEY", "DS", "DNSSEC", "RRSIGs", "NSEC", "NXDOMAIN"} - - def __init__(self, code: Union[EDECode, str], text: Optional[str] = None): - """*code*, a ``dns.edns.EDECode`` or ``str``, the info code of the - extended error. - - *text*, a ``str`` or ``None``, specifying additional information about - the error. - """ - - super().__init__(OptionType.EDE) - - self.code = EDECode.make(code) - if text is not None and not isinstance(text, str): - raise ValueError("text must be string or None") - self.text = text - - def to_text(self) -> str: - output = f"EDE {self.code}" - if self.code in EDECode: - desc = EDECode.to_text(self.code) - desc = " ".join( - word if word in self._preserve_case else word.title() - for word in desc.split("_") - ) - output += f" ({desc})" - if self.text is not None: - output += f": {self.text}" - return output - - def to_wire(self, file: Optional[Any] = None) -> Optional[bytes]: - value = struct.pack("!H", self.code) - if self.text is not None: - value += self.text.encode("utf8") - - if file: - file.write(value) - return None - else: - return value - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: "dns.wire.Parser" - ) -> Option: - code = EDECode.make(parser.get_uint16()) - text = parser.get_remaining() - - if text: - if text[-1] == 0: # text MAY be null-terminated - text = text[:-1] - btext = text.decode("utf8") - else: - btext = None - - return cls(code, btext) - - -class NSIDOption(Option): - def __init__(self, nsid: bytes): - super().__init__(OptionType.NSID) - self.nsid = nsid - - def to_wire(self, file: Any = None) -> Optional[bytes]: - if file: - file.write(self.nsid) - return None - else: - return self.nsid - - def to_text(self) -> str: - if all(c >= 0x20 and c <= 0x7E for c in self.nsid): - # All ASCII printable, so it's probably a string. - value = self.nsid.decode() - else: - value = binascii.hexlify(self.nsid).decode() - return f"NSID {value}" - - @classmethod - def from_wire_parser( - cls, otype: Union[OptionType, str], parser: dns.wire.Parser - ) -> Option: - return cls(parser.get_remaining()) - - -_type_to_class: Dict[OptionType, Any] = { - OptionType.ECS: ECSOption, - OptionType.EDE: EDEOption, - OptionType.NSID: NSIDOption, -} - - -def get_option_class(otype: OptionType) -> Any: - """Return the class for the specified option type. - - The GenericOption class is used if a more specific class is not - known. - """ - - cls = _type_to_class.get(otype) - if cls is None: - cls = GenericOption - return cls - - -def option_from_wire_parser( - otype: Union[OptionType, str], parser: "dns.wire.Parser" -) -> Option: - """Build an EDNS option object from wire format. - - *otype*, an ``int``, is the option type. - - *parser*, a ``dns.wire.Parser``, the parser, which should be - restricted to the option length. - - Returns an instance of a subclass of ``dns.edns.Option``. - """ - otype = OptionType.make(otype) - cls = get_option_class(otype) - return cls.from_wire_parser(otype, parser) - - -def option_from_wire( - otype: Union[OptionType, str], wire: bytes, current: int, olen: int -) -> Option: - """Build an EDNS option object from wire format. - - *otype*, an ``int``, is the option type. - - *wire*, a ``bytes``, is the wire-format message. - - *current*, an ``int``, is the offset in *wire* of the beginning - of the rdata. - - *olen*, an ``int``, is the length of the wire-format option data - - Returns an instance of a subclass of ``dns.edns.Option``. - """ - parser = dns.wire.Parser(wire, current) - with parser.restrict_to(olen): - return option_from_wire_parser(otype, parser) - - -def register_type(implementation: Any, otype: OptionType) -> None: - """Register the implementation of an option type. - - *implementation*, a ``class``, is a subclass of ``dns.edns.Option``. - - *otype*, an ``int``, is the option type. - """ - - _type_to_class[otype] = implementation - - -### BEGIN generated OptionType constants - -NSID = OptionType.NSID -DAU = OptionType.DAU -DHU = OptionType.DHU -N3U = OptionType.N3U -ECS = OptionType.ECS -EXPIRE = OptionType.EXPIRE -COOKIE = OptionType.COOKIE -KEEPALIVE = OptionType.KEEPALIVE -PADDING = OptionType.PADDING -CHAIN = OptionType.CHAIN -EDE = OptionType.EDE - -### END generated OptionType constants diff --git a/.venv/lib/python3.12/site-packages/dns/entropy.py b/.venv/lib/python3.12/site-packages/dns/entropy.py deleted file mode 100644 index 4dcdc62..0000000 --- a/.venv/lib/python3.12/site-packages/dns/entropy.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2009-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import hashlib -import os -import random -import threading -import time -from typing import Any, Optional - - -class EntropyPool: - # This is an entropy pool for Python implementations that do not - # have a working SystemRandom. I'm not sure there are any, but - # leaving this code doesn't hurt anything as the library code - # is used if present. - - def __init__(self, seed: Optional[bytes] = None): - self.pool_index = 0 - self.digest: Optional[bytearray] = None - self.next_byte = 0 - self.lock = threading.Lock() - self.hash = hashlib.sha1() - self.hash_len = 20 - self.pool = bytearray(b"\0" * self.hash_len) - if seed is not None: - self._stir(seed) - self.seeded = True - self.seed_pid = os.getpid() - else: - self.seeded = False - self.seed_pid = 0 - - def _stir(self, entropy: bytes) -> None: - for c in entropy: - if self.pool_index == self.hash_len: - self.pool_index = 0 - b = c & 0xFF - self.pool[self.pool_index] ^= b - self.pool_index += 1 - - def stir(self, entropy: bytes) -> None: - with self.lock: - self._stir(entropy) - - def _maybe_seed(self) -> None: - if not self.seeded or self.seed_pid != os.getpid(): - try: - seed = os.urandom(16) - except Exception: # pragma: no cover - try: - with open("/dev/urandom", "rb", 0) as r: - seed = r.read(16) - except Exception: - seed = str(time.time()).encode() - self.seeded = True - self.seed_pid = os.getpid() - self.digest = None - seed = bytearray(seed) - self._stir(seed) - - def random_8(self) -> int: - with self.lock: - self._maybe_seed() - if self.digest is None or self.next_byte == self.hash_len: - self.hash.update(bytes(self.pool)) - self.digest = bytearray(self.hash.digest()) - self._stir(self.digest) - self.next_byte = 0 - value = self.digest[self.next_byte] - self.next_byte += 1 - return value - - def random_16(self) -> int: - return self.random_8() * 256 + self.random_8() - - def random_32(self) -> int: - return self.random_16() * 65536 + self.random_16() - - def random_between(self, first: int, last: int) -> int: - size = last - first + 1 - if size > 4294967296: - raise ValueError("too big") - if size > 65536: - rand = self.random_32 - max = 4294967295 - elif size > 256: - rand = self.random_16 - max = 65535 - else: - rand = self.random_8 - max = 255 - return first + size * rand() // (max + 1) - - -pool = EntropyPool() - -system_random: Optional[Any] -try: - system_random = random.SystemRandom() -except Exception: # pragma: no cover - system_random = None - - -def random_16() -> int: - if system_random is not None: - return system_random.randrange(0, 65536) - else: - return pool.random_16() - - -def between(first: int, last: int) -> int: - if system_random is not None: - return system_random.randrange(first, last + 1) - else: - return pool.random_between(first, last) diff --git a/.venv/lib/python3.12/site-packages/dns/enum.py b/.venv/lib/python3.12/site-packages/dns/enum.py deleted file mode 100644 index 71461f1..0000000 --- a/.venv/lib/python3.12/site-packages/dns/enum.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import enum -from typing import Type, TypeVar, Union - -TIntEnum = TypeVar("TIntEnum", bound="IntEnum") - - -class IntEnum(enum.IntEnum): - @classmethod - def _missing_(cls, value): - cls._check_value(value) - val = int.__new__(cls, value) - val._name_ = cls._extra_to_text(value, None) or f"{cls._prefix()}{value}" - val._value_ = value - return val - - @classmethod - def _check_value(cls, value): - max = cls._maximum() - if not isinstance(value, int): - raise TypeError - if value < 0 or value > max: - name = cls._short_name() - raise ValueError(f"{name} must be an int between >= 0 and <= {max}") - - @classmethod - def from_text(cls: Type[TIntEnum], text: str) -> TIntEnum: - text = text.upper() - try: - return cls[text] - except KeyError: - pass - value = cls._extra_from_text(text) - if value: - return value - prefix = cls._prefix() - if text.startswith(prefix) and text[len(prefix) :].isdigit(): - value = int(text[len(prefix) :]) - cls._check_value(value) - try: - return cls(value) - except ValueError: - return value - raise cls._unknown_exception_class() - - @classmethod - def to_text(cls: Type[TIntEnum], value: int) -> str: - cls._check_value(value) - try: - text = cls(value).name - except ValueError: - text = None - text = cls._extra_to_text(value, text) - if text is None: - text = f"{cls._prefix()}{value}" - return text - - @classmethod - def make(cls: Type[TIntEnum], value: Union[int, str]) -> TIntEnum: - """Convert text or a value into an enumerated type, if possible. - - *value*, the ``int`` or ``str`` to convert. - - Raises a class-specific exception if a ``str`` is provided that - cannot be converted. - - Raises ``ValueError`` if the value is out of range. - - Returns an enumeration from the calling class corresponding to the - value, if one is defined, or an ``int`` otherwise. - """ - - if isinstance(value, str): - return cls.from_text(value) - cls._check_value(value) - return cls(value) - - @classmethod - def _maximum(cls): - raise NotImplementedError # pragma: no cover - - @classmethod - def _short_name(cls): - return cls.__name__.lower() - - @classmethod - def _prefix(cls): - return "" - - @classmethod - def _extra_from_text(cls, text): # pylint: disable=W0613 - return None - - @classmethod - def _extra_to_text(cls, value, current_text): # pylint: disable=W0613 - return current_text - - @classmethod - def _unknown_exception_class(cls): - return ValueError diff --git a/.venv/lib/python3.12/site-packages/dns/exception.py b/.venv/lib/python3.12/site-packages/dns/exception.py deleted file mode 100644 index 6982373..0000000 --- a/.venv/lib/python3.12/site-packages/dns/exception.py +++ /dev/null @@ -1,169 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Common DNS Exceptions. - -Dnspython modules may also define their own exceptions, which will -always be subclasses of ``DNSException``. -""" - - -from typing import Optional, Set - - -class DNSException(Exception): - """Abstract base class shared by all dnspython exceptions. - - It supports two basic modes of operation: - - a) Old/compatible mode is used if ``__init__`` was called with - empty *kwargs*. In compatible mode all *args* are passed - to the standard Python Exception class as before and all *args* are - printed by the standard ``__str__`` implementation. Class variable - ``msg`` (or doc string if ``msg`` is ``None``) is returned from ``str()`` - if *args* is empty. - - b) New/parametrized mode is used if ``__init__`` was called with - non-empty *kwargs*. - In the new mode *args* must be empty and all kwargs must match - those set in class variable ``supp_kwargs``. All kwargs are stored inside - ``self.kwargs`` and used in a new ``__str__`` implementation to construct - a formatted message based on the ``fmt`` class variable, a ``string``. - - In the simplest case it is enough to override the ``supp_kwargs`` - and ``fmt`` class variables to get nice parametrized messages. - """ - - msg: Optional[str] = None # non-parametrized message - supp_kwargs: Set[str] = set() # accepted parameters for _fmt_kwargs (sanity check) - fmt: Optional[str] = None # message parametrized with results from _fmt_kwargs - - def __init__(self, *args, **kwargs): - self._check_params(*args, **kwargs) - if kwargs: - # This call to a virtual method from __init__ is ok in our usage - self.kwargs = self._check_kwargs(**kwargs) # lgtm[py/init-calls-subclass] - self.msg = str(self) - else: - self.kwargs = dict() # defined but empty for old mode exceptions - if self.msg is None: - # doc string is better implicit message than empty string - self.msg = self.__doc__ - if args: - super().__init__(*args) - else: - super().__init__(self.msg) - - def _check_params(self, *args, **kwargs): - """Old exceptions supported only args and not kwargs. - - For sanity we do not allow to mix old and new behavior.""" - if args or kwargs: - assert bool(args) != bool( - kwargs - ), "keyword arguments are mutually exclusive with positional args" - - def _check_kwargs(self, **kwargs): - if kwargs: - assert ( - set(kwargs.keys()) == self.supp_kwargs - ), "following set of keyword args is required: %s" % (self.supp_kwargs) - return kwargs - - def _fmt_kwargs(self, **kwargs): - """Format kwargs before printing them. - - Resulting dictionary has to have keys necessary for str.format call - on fmt class variable. - """ - fmtargs = {} - for kw, data in kwargs.items(): - if isinstance(data, (list, set)): - # convert list of to list of str() - fmtargs[kw] = list(map(str, data)) - if len(fmtargs[kw]) == 1: - # remove list brackets [] from single-item lists - fmtargs[kw] = fmtargs[kw].pop() - else: - fmtargs[kw] = data - return fmtargs - - def __str__(self): - if self.kwargs and self.fmt: - # provide custom message constructed from keyword arguments - fmtargs = self._fmt_kwargs(**self.kwargs) - return self.fmt.format(**fmtargs) - else: - # print *args directly in the same way as old DNSException - return super().__str__() - - -class FormError(DNSException): - """DNS message is malformed.""" - - -class SyntaxError(DNSException): - """Text input is malformed.""" - - -class UnexpectedEnd(SyntaxError): - """Text input ended unexpectedly.""" - - -class TooBig(DNSException): - """The DNS message is too big.""" - - -class Timeout(DNSException): - """The DNS operation timed out.""" - - supp_kwargs = {"timeout"} - fmt = "The DNS operation timed out after {timeout:.3f} seconds" - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - -class UnsupportedAlgorithm(DNSException): - """The DNSSEC algorithm is not supported.""" - - -class AlgorithmKeyMismatch(UnsupportedAlgorithm): - """The DNSSEC algorithm is not supported for the given key type.""" - - -class ValidationFailure(DNSException): - """The DNSSEC signature is invalid.""" - - -class DeniedByPolicy(DNSException): - """Denied by DNSSEC policy.""" - - -class ExceptionWrapper: - def __init__(self, exception_class): - self.exception_class = exception_class - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if exc_type is not None and not isinstance(exc_val, self.exception_class): - raise self.exception_class(str(exc_val)) from exc_val - return False diff --git a/.venv/lib/python3.12/site-packages/dns/flags.py b/.venv/lib/python3.12/site-packages/dns/flags.py deleted file mode 100644 index 4c60be1..0000000 --- a/.venv/lib/python3.12/site-packages/dns/flags.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Message Flags.""" - -import enum -from typing import Any - -# Standard DNS flags - - -class Flag(enum.IntFlag): - #: Query Response - QR = 0x8000 - #: Authoritative Answer - AA = 0x0400 - #: Truncated Response - TC = 0x0200 - #: Recursion Desired - RD = 0x0100 - #: Recursion Available - RA = 0x0080 - #: Authentic Data - AD = 0x0020 - #: Checking Disabled - CD = 0x0010 - - -# EDNS flags - - -class EDNSFlag(enum.IntFlag): - #: DNSSEC answer OK - DO = 0x8000 - - -def _from_text(text: str, enum_class: Any) -> int: - flags = 0 - tokens = text.split() - for t in tokens: - flags |= enum_class[t.upper()] - return flags - - -def _to_text(flags: int, enum_class: Any) -> str: - text_flags = [] - for k, v in enum_class.__members__.items(): - if flags & v != 0: - text_flags.append(k) - return " ".join(text_flags) - - -def from_text(text: str) -> int: - """Convert a space-separated list of flag text values into a flags - value. - - Returns an ``int`` - """ - - return _from_text(text, Flag) - - -def to_text(flags: int) -> str: - """Convert a flags value into a space-separated list of flag text - values. - - Returns a ``str``. - """ - - return _to_text(flags, Flag) - - -def edns_from_text(text: str) -> int: - """Convert a space-separated list of EDNS flag text values into a EDNS - flags value. - - Returns an ``int`` - """ - - return _from_text(text, EDNSFlag) - - -def edns_to_text(flags: int) -> str: - """Convert an EDNS flags value into a space-separated list of EDNS flag - text values. - - Returns a ``str``. - """ - - return _to_text(flags, EDNSFlag) - - -### BEGIN generated Flag constants - -QR = Flag.QR -AA = Flag.AA -TC = Flag.TC -RD = Flag.RD -RA = Flag.RA -AD = Flag.AD -CD = Flag.CD - -### END generated Flag constants - -### BEGIN generated EDNSFlag constants - -DO = EDNSFlag.DO - -### END generated EDNSFlag constants diff --git a/.venv/lib/python3.12/site-packages/dns/grange.py b/.venv/lib/python3.12/site-packages/dns/grange.py deleted file mode 100644 index 3a52278..0000000 --- a/.venv/lib/python3.12/site-packages/dns/grange.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2012-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS GENERATE range conversion.""" - -from typing import Tuple - -import dns - - -def from_text(text: str) -> Tuple[int, int, int]: - """Convert the text form of a range in a ``$GENERATE`` statement to an - integer. - - *text*, a ``str``, the textual range in ``$GENERATE`` form. - - Returns a tuple of three ``int`` values ``(start, stop, step)``. - """ - - start = -1 - stop = -1 - step = 1 - cur = "" - state = 0 - # state 0 1 2 - # x - y / z - - if text and text[0] == "-": - raise dns.exception.SyntaxError("Start cannot be a negative number") - - for c in text: - if c == "-" and state == 0: - start = int(cur) - cur = "" - state = 1 - elif c == "/": - stop = int(cur) - cur = "" - state = 2 - elif c.isdigit(): - cur += c - else: - raise dns.exception.SyntaxError("Could not parse %s" % (c)) - - if state == 0: - raise dns.exception.SyntaxError("no stop value specified") - elif state == 1: - stop = int(cur) - else: - assert state == 2 - step = int(cur) - - assert step >= 1 - assert start >= 0 - if start > stop: - raise dns.exception.SyntaxError("start must be <= stop") - - return (start, stop, step) diff --git a/.venv/lib/python3.12/site-packages/dns/immutable.py b/.venv/lib/python3.12/site-packages/dns/immutable.py deleted file mode 100644 index 36b0362..0000000 --- a/.venv/lib/python3.12/site-packages/dns/immutable.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import collections.abc -from typing import Any, Callable - -from dns._immutable_ctx import immutable - - -@immutable -class Dict(collections.abc.Mapping): # lgtm[py/missing-equals] - def __init__( - self, - dictionary: Any, - no_copy: bool = False, - map_factory: Callable[[], collections.abc.MutableMapping] = dict, - ): - """Make an immutable dictionary from the specified dictionary. - - If *no_copy* is `True`, then *dictionary* will be wrapped instead - of copied. Only set this if you are sure there will be no external - references to the dictionary. - """ - if no_copy and isinstance(dictionary, collections.abc.MutableMapping): - self._odict = dictionary - else: - self._odict = map_factory() - self._odict.update(dictionary) - self._hash = None - - def __getitem__(self, key): - return self._odict.__getitem__(key) - - def __hash__(self): # pylint: disable=invalid-hash-returned - if self._hash is None: - h = 0 - for key in sorted(self._odict.keys()): - h ^= hash(key) - object.__setattr__(self, "_hash", h) - # this does return an int, but pylint doesn't figure that out - return self._hash - - def __len__(self): - return len(self._odict) - - def __iter__(self): - return iter(self._odict) - - -def constify(o: Any) -> Any: - """ - Convert mutable types to immutable types. - """ - if isinstance(o, bytearray): - return bytes(o) - if isinstance(o, tuple): - try: - hash(o) - return o - except Exception: - return tuple(constify(elt) for elt in o) - if isinstance(o, list): - return tuple(constify(elt) for elt in o) - if isinstance(o, dict): - cdict = dict() - for k, v in o.items(): - cdict[k] = constify(v) - return Dict(cdict, True) - return o diff --git a/.venv/lib/python3.12/site-packages/dns/inet.py b/.venv/lib/python3.12/site-packages/dns/inet.py deleted file mode 100644 index 4a03f99..0000000 --- a/.venv/lib/python3.12/site-packages/dns/inet.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Generic Internet address helper functions.""" - -import socket -from typing import Any, Optional, Tuple - -import dns.ipv4 -import dns.ipv6 - -# We assume that AF_INET and AF_INET6 are always defined. We keep -# these here for the benefit of any old code (unlikely though that -# is!). -AF_INET = socket.AF_INET -AF_INET6 = socket.AF_INET6 - - -def inet_pton(family: int, text: str) -> bytes: - """Convert the textual form of a network address into its binary form. - - *family* is an ``int``, the address family. - - *text* is a ``str``, the textual address. - - Raises ``NotImplementedError`` if the address family specified is not - implemented. - - Returns a ``bytes``. - """ - - if family == AF_INET: - return dns.ipv4.inet_aton(text) - elif family == AF_INET6: - return dns.ipv6.inet_aton(text, True) - else: - raise NotImplementedError - - -def inet_ntop(family: int, address: bytes) -> str: - """Convert the binary form of a network address into its textual form. - - *family* is an ``int``, the address family. - - *address* is a ``bytes``, the network address in binary form. - - Raises ``NotImplementedError`` if the address family specified is not - implemented. - - Returns a ``str``. - """ - - if family == AF_INET: - return dns.ipv4.inet_ntoa(address) - elif family == AF_INET6: - return dns.ipv6.inet_ntoa(address) - else: - raise NotImplementedError - - -def af_for_address(text: str) -> int: - """Determine the address family of a textual-form network address. - - *text*, a ``str``, the textual address. - - Raises ``ValueError`` if the address family cannot be determined - from the input. - - Returns an ``int``. - """ - - try: - dns.ipv4.inet_aton(text) - return AF_INET - except Exception: - try: - dns.ipv6.inet_aton(text, True) - return AF_INET6 - except Exception: - raise ValueError - - -def is_multicast(text: str) -> bool: - """Is the textual-form network address a multicast address? - - *text*, a ``str``, the textual address. - - Raises ``ValueError`` if the address family cannot be determined - from the input. - - Returns a ``bool``. - """ - - try: - first = dns.ipv4.inet_aton(text)[0] - return first >= 224 and first <= 239 - except Exception: - try: - first = dns.ipv6.inet_aton(text, True)[0] - return first == 255 - except Exception: - raise ValueError - - -def is_address(text: str) -> bool: - """Is the specified string an IPv4 or IPv6 address? - - *text*, a ``str``, the textual address. - - Returns a ``bool``. - """ - - try: - dns.ipv4.inet_aton(text) - return True - except Exception: - try: - dns.ipv6.inet_aton(text, True) - return True - except Exception: - return False - - -def low_level_address_tuple( - high_tuple: Tuple[str, int], af: Optional[int] = None -) -> Any: - """Given a "high-level" address tuple, i.e. - an (address, port) return the appropriate "low-level" address tuple - suitable for use in socket calls. - - If an *af* other than ``None`` is provided, it is assumed the - address in the high-level tuple is valid and has that af. If af - is ``None``, then af_for_address will be called. - """ - address, port = high_tuple - if af is None: - af = af_for_address(address) - if af == AF_INET: - return (address, port) - elif af == AF_INET6: - i = address.find("%") - if i < 0: - # no scope, shortcut! - return (address, port, 0, 0) - # try to avoid getaddrinfo() - addrpart = address[:i] - scope = address[i + 1 :] - if scope.isdigit(): - return (addrpart, port, 0, int(scope)) - try: - return (addrpart, port, 0, socket.if_nametoindex(scope)) - except AttributeError: # pragma: no cover (we can't really test this) - ai_flags = socket.AI_NUMERICHOST - ((*_, tup), *_) = socket.getaddrinfo(address, port, flags=ai_flags) - return tup - else: - raise NotImplementedError(f"unknown address family {af}") - - -def any_for_af(af): - """Return the 'any' address for the specified address family.""" - if af == socket.AF_INET: - return "0.0.0.0" - elif af == socket.AF_INET6: - return "::" - raise NotImplementedError(f"unknown address family {af}") - - -def canonicalize(text: str) -> str: - """Verify that *address* is a valid text form IPv4 or IPv6 address and return its - canonical text form. IPv6 addresses with scopes are rejected. - - *text*, a ``str``, the address in textual form. - - Raises ``ValueError`` if the text is not valid. - """ - try: - return dns.ipv6.canonicalize(text) - except Exception: - try: - return dns.ipv4.canonicalize(text) - except Exception: - raise ValueError diff --git a/.venv/lib/python3.12/site-packages/dns/ipv4.py b/.venv/lib/python3.12/site-packages/dns/ipv4.py deleted file mode 100644 index 65ee69c..0000000 --- a/.venv/lib/python3.12/site-packages/dns/ipv4.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""IPv4 helper functions.""" - -import struct -from typing import Union - -import dns.exception - - -def inet_ntoa(address: bytes) -> str: - """Convert an IPv4 address in binary form to text form. - - *address*, a ``bytes``, the IPv4 address in binary form. - - Returns a ``str``. - """ - - if len(address) != 4: - raise dns.exception.SyntaxError - return "%u.%u.%u.%u" % (address[0], address[1], address[2], address[3]) - - -def inet_aton(text: Union[str, bytes]) -> bytes: - """Convert an IPv4 address in text form to binary form. - - *text*, a ``str`` or ``bytes``, the IPv4 address in textual form. - - Returns a ``bytes``. - """ - - if not isinstance(text, bytes): - btext = text.encode() - else: - btext = text - parts = btext.split(b".") - if len(parts) != 4: - raise dns.exception.SyntaxError - for part in parts: - if not part.isdigit(): - raise dns.exception.SyntaxError - if len(part) > 1 and part[0] == ord("0"): - # No leading zeros - raise dns.exception.SyntaxError - try: - b = [int(part) for part in parts] - return struct.pack("BBBB", *b) - except Exception: - raise dns.exception.SyntaxError - - -def canonicalize(text: Union[str, bytes]) -> str: - """Verify that *address* is a valid text form IPv4 address and return its - canonical text form. - - *text*, a ``str`` or ``bytes``, the IPv4 address in textual form. - - Raises ``dns.exception.SyntaxError`` if the text is not valid. - """ - # Note that inet_aton() only accepts canonial form, but we still run through - # inet_ntoa() to ensure the output is a str. - return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) diff --git a/.venv/lib/python3.12/site-packages/dns/ipv6.py b/.venv/lib/python3.12/site-packages/dns/ipv6.py deleted file mode 100644 index 44a1063..0000000 --- a/.venv/lib/python3.12/site-packages/dns/ipv6.py +++ /dev/null @@ -1,219 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""IPv6 helper functions.""" - -import binascii -import re -from typing import List, Union - -import dns.exception -import dns.ipv4 - -_leading_zero = re.compile(r"0+([0-9a-f]+)") - - -def inet_ntoa(address: bytes) -> str: - """Convert an IPv6 address in binary form to text form. - - *address*, a ``bytes``, the IPv6 address in binary form. - - Raises ``ValueError`` if the address isn't 16 bytes long. - Returns a ``str``. - """ - - if len(address) != 16: - raise ValueError("IPv6 addresses are 16 bytes long") - hex = binascii.hexlify(address) - chunks = [] - i = 0 - l = len(hex) - while i < l: - chunk = hex[i : i + 4].decode() - # strip leading zeros. we do this with an re instead of - # with lstrip() because lstrip() didn't support chars until - # python 2.2.2 - m = _leading_zero.match(chunk) - if m is not None: - chunk = m.group(1) - chunks.append(chunk) - i += 4 - # - # Compress the longest subsequence of 0-value chunks to :: - # - best_start = 0 - best_len = 0 - start = -1 - last_was_zero = False - for i in range(8): - if chunks[i] != "0": - if last_was_zero: - end = i - current_len = end - start - if current_len > best_len: - best_start = start - best_len = current_len - last_was_zero = False - elif not last_was_zero: - start = i - last_was_zero = True - if last_was_zero: - end = 8 - current_len = end - start - if current_len > best_len: - best_start = start - best_len = current_len - if best_len > 1: - if best_start == 0 and (best_len == 6 or best_len == 5 and chunks[5] == "ffff"): - # We have an embedded IPv4 address - if best_len == 6: - prefix = "::" - else: - prefix = "::ffff:" - thex = prefix + dns.ipv4.inet_ntoa(address[12:]) - else: - thex = ( - ":".join(chunks[:best_start]) - + "::" - + ":".join(chunks[best_start + best_len :]) - ) - else: - thex = ":".join(chunks) - return thex - - -_v4_ending = re.compile(rb"(.*):(\d+\.\d+\.\d+\.\d+)$") -_colon_colon_start = re.compile(rb"::.*") -_colon_colon_end = re.compile(rb".*::$") - - -def inet_aton(text: Union[str, bytes], ignore_scope: bool = False) -> bytes: - """Convert an IPv6 address in text form to binary form. - - *text*, a ``str`` or ``bytes``, the IPv6 address in textual form. - - *ignore_scope*, a ``bool``. If ``True``, a scope will be ignored. - If ``False``, the default, it is an error for a scope to be present. - - Returns a ``bytes``. - """ - - # - # Our aim here is not something fast; we just want something that works. - # - if not isinstance(text, bytes): - btext = text.encode() - else: - btext = text - - if ignore_scope: - parts = btext.split(b"%") - l = len(parts) - if l == 2: - btext = parts[0] - elif l > 2: - raise dns.exception.SyntaxError - - if btext == b"": - raise dns.exception.SyntaxError - elif btext.endswith(b":") and not btext.endswith(b"::"): - raise dns.exception.SyntaxError - elif btext.startswith(b":") and not btext.startswith(b"::"): - raise dns.exception.SyntaxError - elif btext == b"::": - btext = b"0::" - # - # Get rid of the icky dot-quad syntax if we have it. - # - m = _v4_ending.match(btext) - if m is not None: - b = dns.ipv4.inet_aton(m.group(2)) - btext = ( - "{}:{:02x}{:02x}:{:02x}{:02x}".format( - m.group(1).decode(), b[0], b[1], b[2], b[3] - ) - ).encode() - # - # Try to turn '::' into ':'; if no match try to - # turn '::' into ':' - # - m = _colon_colon_start.match(btext) - if m is not None: - btext = btext[1:] - else: - m = _colon_colon_end.match(btext) - if m is not None: - btext = btext[:-1] - # - # Now canonicalize into 8 chunks of 4 hex digits each - # - chunks = btext.split(b":") - l = len(chunks) - if l > 8: - raise dns.exception.SyntaxError - seen_empty = False - canonical: List[bytes] = [] - for c in chunks: - if c == b"": - if seen_empty: - raise dns.exception.SyntaxError - seen_empty = True - for _ in range(0, 8 - l + 1): - canonical.append(b"0000") - else: - lc = len(c) - if lc > 4: - raise dns.exception.SyntaxError - if lc != 4: - c = (b"0" * (4 - lc)) + c - canonical.append(c) - if l < 8 and not seen_empty: - raise dns.exception.SyntaxError - btext = b"".join(canonical) - - # - # Finally we can go to binary. - # - try: - return binascii.unhexlify(btext) - except (binascii.Error, TypeError): - raise dns.exception.SyntaxError - - -_mapped_prefix = b"\x00" * 10 + b"\xff\xff" - - -def is_mapped(address: bytes) -> bool: - """Is the specified address a mapped IPv4 address? - - *address*, a ``bytes`` is an IPv6 address in binary form. - - Returns a ``bool``. - """ - - return address.startswith(_mapped_prefix) - - -def canonicalize(text: Union[str, bytes]) -> str: - """Verify that *address* is a valid text form IPv6 address and return its - canonical text form. Addresses with scopes are rejected. - - *text*, a ``str`` or ``bytes``, the IPv6 address in textual form. - - Raises ``dns.exception.SyntaxError`` if the text is not valid. - """ - return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) diff --git a/.venv/lib/python3.12/site-packages/dns/message.py b/.venv/lib/python3.12/site-packages/dns/message.py deleted file mode 100644 index 44cacbd..0000000 --- a/.venv/lib/python3.12/site-packages/dns/message.py +++ /dev/null @@ -1,1888 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Messages""" - -import contextlib -import io -import time -from typing import Any, Dict, List, Optional, Tuple, Union - -import dns.edns -import dns.entropy -import dns.enum -import dns.exception -import dns.flags -import dns.name -import dns.opcode -import dns.rcode -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.ANY.OPT -import dns.rdtypes.ANY.TSIG -import dns.renderer -import dns.rrset -import dns.tsig -import dns.ttl -import dns.wire - - -class ShortHeader(dns.exception.FormError): - """The DNS packet passed to from_wire() is too short.""" - - -class TrailingJunk(dns.exception.FormError): - """The DNS packet passed to from_wire() has extra junk at the end of it.""" - - -class UnknownHeaderField(dns.exception.DNSException): - """The header field name was not recognized when converting from text - into a message.""" - - -class BadEDNS(dns.exception.FormError): - """An OPT record occurred somewhere other than - the additional data section.""" - - -class BadTSIG(dns.exception.FormError): - """A TSIG record occurred somewhere other than the end of - the additional data section.""" - - -class UnknownTSIGKey(dns.exception.DNSException): - """A TSIG with an unknown key was received.""" - - -class Truncated(dns.exception.DNSException): - """The truncated flag is set.""" - - supp_kwargs = {"message"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def message(self): - """As much of the message as could be processed. - - Returns a ``dns.message.Message``. - """ - return self.kwargs["message"] - - -class NotQueryResponse(dns.exception.DNSException): - """Message is not a response to a query.""" - - -class ChainTooLong(dns.exception.DNSException): - """The CNAME chain is too long.""" - - -class AnswerForNXDOMAIN(dns.exception.DNSException): - """The rcode is NXDOMAIN but an answer was found.""" - - -class NoPreviousName(dns.exception.SyntaxError): - """No previous name was known.""" - - -class MessageSection(dns.enum.IntEnum): - """Message sections""" - - QUESTION = 0 - ANSWER = 1 - AUTHORITY = 2 - ADDITIONAL = 3 - - @classmethod - def _maximum(cls): - return 3 - - -class MessageError: - def __init__(self, exception: Exception, offset: int): - self.exception = exception - self.offset = offset - - -DEFAULT_EDNS_PAYLOAD = 1232 -MAX_CHAIN = 16 - -IndexKeyType = Tuple[ - int, - dns.name.Name, - dns.rdataclass.RdataClass, - dns.rdatatype.RdataType, - Optional[dns.rdatatype.RdataType], - Optional[dns.rdataclass.RdataClass], -] -IndexType = Dict[IndexKeyType, dns.rrset.RRset] -SectionType = Union[int, str, List[dns.rrset.RRset]] - - -class Message: - """A DNS message.""" - - _section_enum = MessageSection - - def __init__(self, id: Optional[int] = None): - if id is None: - self.id = dns.entropy.random_16() - else: - self.id = id - self.flags = 0 - self.sections: List[List[dns.rrset.RRset]] = [[], [], [], []] - self.opt: Optional[dns.rrset.RRset] = None - self.request_payload = 0 - self.pad = 0 - self.keyring: Any = None - self.tsig: Optional[dns.rrset.RRset] = None - self.request_mac = b"" - self.xfr = False - self.origin: Optional[dns.name.Name] = None - self.tsig_ctx: Optional[Any] = None - self.index: IndexType = {} - self.errors: List[MessageError] = [] - self.time = 0.0 - - @property - def question(self) -> List[dns.rrset.RRset]: - """The question section.""" - return self.sections[0] - - @question.setter - def question(self, v): - self.sections[0] = v - - @property - def answer(self) -> List[dns.rrset.RRset]: - """The answer section.""" - return self.sections[1] - - @answer.setter - def answer(self, v): - self.sections[1] = v - - @property - def authority(self) -> List[dns.rrset.RRset]: - """The authority section.""" - return self.sections[2] - - @authority.setter - def authority(self, v): - self.sections[2] = v - - @property - def additional(self) -> List[dns.rrset.RRset]: - """The additional data section.""" - return self.sections[3] - - @additional.setter - def additional(self, v): - self.sections[3] = v - - def __repr__(self): - return "" - - def __str__(self): - return self.to_text() - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - """Convert the message to text. - - The *origin*, *relativize*, and any other keyword - arguments are passed to the RRset ``to_wire()`` method. - - Returns a ``str``. - """ - - s = io.StringIO() - s.write("id %d\n" % self.id) - s.write("opcode %s\n" % dns.opcode.to_text(self.opcode())) - s.write("rcode %s\n" % dns.rcode.to_text(self.rcode())) - s.write("flags %s\n" % dns.flags.to_text(self.flags)) - if self.edns >= 0: - s.write("edns %s\n" % self.edns) - if self.ednsflags != 0: - s.write("eflags %s\n" % dns.flags.edns_to_text(self.ednsflags)) - s.write("payload %d\n" % self.payload) - for opt in self.options: - s.write("option %s\n" % opt.to_text()) - for name, which in self._section_enum.__members__.items(): - s.write(f";{name}\n") - for rrset in self.section_from_number(which): - s.write(rrset.to_text(origin, relativize, **kw)) - s.write("\n") - # - # We strip off the final \n so the caller can print the result without - # doing weird things to get around eccentricities in Python print - # formatting - # - return s.getvalue()[:-1] - - def __eq__(self, other): - """Two messages are equal if they have the same content in the - header, question, answer, and authority sections. - - Returns a ``bool``. - """ - - if not isinstance(other, Message): - return False - if self.id != other.id: - return False - if self.flags != other.flags: - return False - for i, section in enumerate(self.sections): - other_section = other.sections[i] - for n in section: - if n not in other_section: - return False - for n in other_section: - if n not in section: - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def is_response(self, other: "Message") -> bool: - """Is *other*, also a ``dns.message.Message``, a response to this - message? - - Returns a ``bool``. - """ - - if ( - other.flags & dns.flags.QR == 0 - or self.id != other.id - or dns.opcode.from_flags(self.flags) != dns.opcode.from_flags(other.flags) - ): - return False - if other.rcode() in { - dns.rcode.FORMERR, - dns.rcode.SERVFAIL, - dns.rcode.NOTIMP, - dns.rcode.REFUSED, - }: - # We don't check the question section in these cases if - # the other question section is empty, even though they - # still really ought to have a question section. - if len(other.question) == 0: - return True - if dns.opcode.is_update(self.flags): - # This is assuming the "sender doesn't include anything - # from the update", but we don't care to check the other - # case, which is that all the sections are returned and - # identical. - return True - for n in self.question: - if n not in other.question: - return False - for n in other.question: - if n not in self.question: - return False - return True - - def section_number(self, section: List[dns.rrset.RRset]) -> int: - """Return the "section number" of the specified section for use - in indexing. - - *section* is one of the section attributes of this message. - - Raises ``ValueError`` if the section isn't known. - - Returns an ``int``. - """ - - for i, our_section in enumerate(self.sections): - if section is our_section: - return self._section_enum(i) - raise ValueError("unknown section") - - def section_from_number(self, number: int) -> List[dns.rrset.RRset]: - """Return the section list associated with the specified section - number. - - *number* is a section number `int` or the text form of a section - name. - - Raises ``ValueError`` if the section isn't known. - - Returns a ``list``. - """ - - section = self._section_enum.make(number) - return self.sections[section] - - def find_rrset( - self, - section: SectionType, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - deleting: Optional[dns.rdataclass.RdataClass] = None, - create: bool = False, - force_unique: bool = False, - idna_codec: Optional[dns.name.IDNACodec] = None, - ) -> dns.rrset.RRset: - """Find the RRset with the given attributes in the specified section. - - *section*, an ``int`` section number, a ``str`` section name, or one of - the section attributes of this message. This specifies the - the section of the message to search. For example:: - - my_message.find_rrset(my_message.answer, name, rdclass, rdtype) - my_message.find_rrset(dns.message.ANSWER, name, rdclass, rdtype) - my_message.find_rrset("ANSWER", name, rdclass, rdtype) - - *name*, a ``dns.name.Name`` or ``str``, the name of the RRset. - - *rdclass*, an ``int`` or ``str``, the class of the RRset. - - *rdtype*, an ``int`` or ``str``, the type of the RRset. - - *covers*, an ``int`` or ``str``, the covers value of the RRset. - The default is ``dns.rdatatype.NONE``. - - *deleting*, an ``int``, ``str``, or ``None``, the deleting value of the - RRset. The default is ``None``. - - *create*, a ``bool``. If ``True``, create the RRset if it is not found. - The created RRset is appended to *section*. - - *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, - create a new RRset regardless of whether a matching RRset exists - already. The default is ``False``. This is useful when creating - DDNS Update messages, as order matters for them. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Raises ``KeyError`` if the RRset was not found and create was - ``False``. - - Returns a ``dns.rrset.RRset object``. - """ - - if isinstance(section, int): - section_number = section - section = self.section_from_number(section_number) - elif isinstance(section, str): - section_number = self._section_enum.from_text(section) - section = self.section_from_number(section_number) - else: - section_number = self.section_number(section) - if isinstance(name, str): - name = dns.name.from_text(name, idna_codec=idna_codec) - rdtype = dns.rdatatype.RdataType.make(rdtype) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - covers = dns.rdatatype.RdataType.make(covers) - if deleting is not None: - deleting = dns.rdataclass.RdataClass.make(deleting) - key = (section_number, name, rdclass, rdtype, covers, deleting) - if not force_unique: - if self.index is not None: - rrset = self.index.get(key) - if rrset is not None: - return rrset - else: - for rrset in section: - if rrset.full_match(name, rdclass, rdtype, covers, deleting): - return rrset - if not create: - raise KeyError - rrset = dns.rrset.RRset(name, rdclass, rdtype, covers, deleting) - section.append(rrset) - if self.index is not None: - self.index[key] = rrset - return rrset - - def get_rrset( - self, - section: SectionType, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - deleting: Optional[dns.rdataclass.RdataClass] = None, - create: bool = False, - force_unique: bool = False, - idna_codec: Optional[dns.name.IDNACodec] = None, - ) -> Optional[dns.rrset.RRset]: - """Get the RRset with the given attributes in the specified section. - - If the RRset is not found, None is returned. - - *section*, an ``int`` section number, a ``str`` section name, or one of - the section attributes of this message. This specifies the - the section of the message to search. For example:: - - my_message.get_rrset(my_message.answer, name, rdclass, rdtype) - my_message.get_rrset(dns.message.ANSWER, name, rdclass, rdtype) - my_message.get_rrset("ANSWER", name, rdclass, rdtype) - - *name*, a ``dns.name.Name`` or ``str``, the name of the RRset. - - *rdclass*, an ``int`` or ``str``, the class of the RRset. - - *rdtype*, an ``int`` or ``str``, the type of the RRset. - - *covers*, an ``int`` or ``str``, the covers value of the RRset. - The default is ``dns.rdatatype.NONE``. - - *deleting*, an ``int``, ``str``, or ``None``, the deleting value of the - RRset. The default is ``None``. - - *create*, a ``bool``. If ``True``, create the RRset if it is not found. - The created RRset is appended to *section*. - - *force_unique*, a ``bool``. If ``True`` and *create* is also ``True``, - create a new RRset regardless of whether a matching RRset exists - already. The default is ``False``. This is useful when creating - DDNS Update messages, as order matters for them. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Returns a ``dns.rrset.RRset object`` or ``None``. - """ - - try: - rrset = self.find_rrset( - section, - name, - rdclass, - rdtype, - covers, - deleting, - create, - force_unique, - idna_codec, - ) - except KeyError: - rrset = None - return rrset - - def section_count(self, section: SectionType) -> int: - """Returns the number of records in the specified section. - - *section*, an ``int`` section number, a ``str`` section name, or one of - the section attributes of this message. This specifies the - the section of the message to count. For example:: - - my_message.section_count(my_message.answer) - my_message.section_count(dns.message.ANSWER) - my_message.section_count("ANSWER") - """ - - if isinstance(section, int): - section_number = section - section = self.section_from_number(section_number) - elif isinstance(section, str): - section_number = self._section_enum.from_text(section) - section = self.section_from_number(section_number) - else: - section_number = self.section_number(section) - count = sum(max(1, len(rrs)) for rrs in section) - if section_number == MessageSection.ADDITIONAL: - if self.opt is not None: - count += 1 - if self.tsig is not None: - count += 1 - return count - - def _compute_opt_reserve(self) -> int: - """Compute the size required for the OPT RR, padding excluded""" - if not self.opt: - return 0 - # 1 byte for the root name, 10 for the standard RR fields - size = 11 - # This would be more efficient if options had a size() method, but we won't - # worry about that for now. We also don't worry if there is an existing padding - # option, as it is unlikely and probably harmless, as the worst case is that we - # may add another, and this seems to be legal. - for option in self.opt[0].options: - wire = option.to_wire() - # We add 4 here to account for the option type and length - size += len(wire) + 4 - if self.pad: - # Padding will be added, so again add the option type and length. - size += 4 - return size - - def _compute_tsig_reserve(self) -> int: - """Compute the size required for the TSIG RR""" - # This would be more efficient if TSIGs had a size method, but we won't - # worry about for now. Also, we can't really cope with the potential - # compressibility of the TSIG owner name, so we estimate with the uncompressed - # size. We will disable compression when TSIG and padding are both is active - # so that the padding comes out right. - if not self.tsig: - return 0 - f = io.BytesIO() - self.tsig.to_wire(f) - return len(f.getvalue()) - - def to_wire( - self, - origin: Optional[dns.name.Name] = None, - max_size: int = 0, - multi: bool = False, - tsig_ctx: Optional[Any] = None, - prepend_length: bool = False, - prefer_truncation: bool = False, - **kw: Dict[str, Any], - ) -> bytes: - """Return a string containing the message in DNS compressed wire - format. - - Additional keyword arguments are passed to the RRset ``to_wire()`` - method. - - *origin*, a ``dns.name.Name`` or ``None``, the origin to be appended - to any relative names. If ``None``, and the message has an origin - attribute that is not ``None``, then it will be used. - - *max_size*, an ``int``, the maximum size of the wire format - output; default is 0, which means "the message's request - payload, if nonzero, or 65535". - - *multi*, a ``bool``, should be set to ``True`` if this message is - part of a multiple message sequence. - - *tsig_ctx*, a ``dns.tsig.HMACTSig`` or ``dns.tsig.GSSTSig`` object, the - ongoing TSIG context, used when signing zone transfers. - - *prepend_length*, a ``bool``, should be set to ``True`` if the caller - wants the message length prepended to the message itself. This is - useful for messages sent over TCP, TLS (DoT), or QUIC (DoQ). - - *prefer_truncation*, a ``bool``, should be set to ``True`` if the caller - wants the message to be truncated if it would otherwise exceed the - maximum length. If the truncation occurs before the additional section, - the TC bit will be set. - - Raises ``dns.exception.TooBig`` if *max_size* was exceeded. - - Returns a ``bytes``. - """ - - if origin is None and self.origin is not None: - origin = self.origin - if max_size == 0: - if self.request_payload != 0: - max_size = self.request_payload - else: - max_size = 65535 - if max_size < 512: - max_size = 512 - elif max_size > 65535: - max_size = 65535 - r = dns.renderer.Renderer(self.id, self.flags, max_size, origin) - opt_reserve = self._compute_opt_reserve() - r.reserve(opt_reserve) - tsig_reserve = self._compute_tsig_reserve() - r.reserve(tsig_reserve) - try: - for rrset in self.question: - r.add_question(rrset.name, rrset.rdtype, rrset.rdclass) - for rrset in self.answer: - r.add_rrset(dns.renderer.ANSWER, rrset, **kw) - for rrset in self.authority: - r.add_rrset(dns.renderer.AUTHORITY, rrset, **kw) - for rrset in self.additional: - r.add_rrset(dns.renderer.ADDITIONAL, rrset, **kw) - except dns.exception.TooBig: - if prefer_truncation: - if r.section < dns.renderer.ADDITIONAL: - r.flags |= dns.flags.TC - else: - raise - r.release_reserved() - if self.opt is not None: - r.add_opt(self.opt, self.pad, opt_reserve, tsig_reserve) - r.write_header() - if self.tsig is not None: - (new_tsig, ctx) = dns.tsig.sign( - r.get_wire(), - self.keyring, - self.tsig[0], - int(time.time()), - self.request_mac, - tsig_ctx, - multi, - ) - self.tsig.clear() - self.tsig.add(new_tsig) - r.add_rrset(dns.renderer.ADDITIONAL, self.tsig) - r.write_header() - if multi: - self.tsig_ctx = ctx - wire = r.get_wire() - if prepend_length: - wire = len(wire).to_bytes(2, "big") + wire - return wire - - @staticmethod - def _make_tsig( - keyname, algorithm, time_signed, fudge, mac, original_id, error, other - ): - tsig = dns.rdtypes.ANY.TSIG.TSIG( - dns.rdataclass.ANY, - dns.rdatatype.TSIG, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ) - return dns.rrset.from_rdata(keyname, 0, tsig) - - def use_tsig( - self, - keyring: Any, - keyname: Optional[Union[dns.name.Name, str]] = None, - fudge: int = 300, - original_id: Optional[int] = None, - tsig_error: int = 0, - other_data: bytes = b"", - algorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, - ) -> None: - """When sending, a TSIG signature using the specified key - should be added. - - *key*, a ``dns.tsig.Key`` is the key to use. If a key is specified, - the *keyring* and *algorithm* fields are not used. - - *keyring*, a ``dict``, ``callable`` or ``dns.tsig.Key``, is either - the TSIG keyring or key to use. - - The format of a keyring dict is a mapping from TSIG key name, as - ``dns.name.Name`` to ``dns.tsig.Key`` or a TSIG secret, a ``bytes``. - If a ``dict`` *keyring* is specified but a *keyname* is not, the key - used will be the first key in the *keyring*. Note that the order of - keys in a dictionary is not defined, so applications should supply a - keyname when a ``dict`` keyring is used, unless they know the keyring - contains only one key. If a ``callable`` keyring is specified, the - callable will be called with the message and the keyname, and is - expected to return a key. - - *keyname*, a ``dns.name.Name``, ``str`` or ``None``, the name of - this TSIG key to use; defaults to ``None``. If *keyring* is a - ``dict``, the key must be defined in it. If *keyring* is a - ``dns.tsig.Key``, this is ignored. - - *fudge*, an ``int``, the TSIG time fudge. - - *original_id*, an ``int``, the TSIG original id. If ``None``, - the message's id is used. - - *tsig_error*, an ``int``, the TSIG error code. - - *other_data*, a ``bytes``, the TSIG other data. - - *algorithm*, a ``dns.name.Name`` or ``str``, the TSIG algorithm to use. This is - only used if *keyring* is a ``dict``, and the key entry is a ``bytes``. - """ - - if isinstance(keyring, dns.tsig.Key): - key = keyring - keyname = key.name - elif callable(keyring): - key = keyring(self, keyname) - else: - if isinstance(keyname, str): - keyname = dns.name.from_text(keyname) - if keyname is None: - keyname = next(iter(keyring)) - key = keyring[keyname] - if isinstance(key, bytes): - key = dns.tsig.Key(keyname, key, algorithm) - self.keyring = key - if original_id is None: - original_id = self.id - self.tsig = self._make_tsig( - keyname, - self.keyring.algorithm, - 0, - fudge, - b"\x00" * dns.tsig.mac_sizes[self.keyring.algorithm], - original_id, - tsig_error, - other_data, - ) - - @property - def keyname(self) -> Optional[dns.name.Name]: - if self.tsig: - return self.tsig.name - else: - return None - - @property - def keyalgorithm(self) -> Optional[dns.name.Name]: - if self.tsig: - return self.tsig[0].algorithm - else: - return None - - @property - def mac(self) -> Optional[bytes]: - if self.tsig: - return self.tsig[0].mac - else: - return None - - @property - def tsig_error(self) -> Optional[int]: - if self.tsig: - return self.tsig[0].error - else: - return None - - @property - def had_tsig(self) -> bool: - return bool(self.tsig) - - @staticmethod - def _make_opt(flags=0, payload=DEFAULT_EDNS_PAYLOAD, options=None): - opt = dns.rdtypes.ANY.OPT.OPT(payload, dns.rdatatype.OPT, options or ()) - return dns.rrset.from_rdata(dns.name.root, int(flags), opt) - - def use_edns( - self, - edns: Optional[Union[int, bool]] = 0, - ednsflags: int = 0, - payload: int = DEFAULT_EDNS_PAYLOAD, - request_payload: Optional[int] = None, - options: Optional[List[dns.edns.Option]] = None, - pad: int = 0, - ) -> None: - """Configure EDNS behavior. - - *edns*, an ``int``, is the EDNS level to use. Specifying ``None``, ``False``, - or ``-1`` means "do not use EDNS", and in this case the other parameters are - ignored. Specifying ``True`` is equivalent to specifying 0, i.e. "use EDNS0". - - *ednsflags*, an ``int``, the EDNS flag values. - - *payload*, an ``int``, is the EDNS sender's payload field, which is the maximum - size of UDP datagram the sender can handle. I.e. how big a response to this - message can be. - - *request_payload*, an ``int``, is the EDNS payload size to use when sending this - message. If not specified, defaults to the value of *payload*. - - *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS options. - - *pad*, a non-negative ``int``. If 0, the default, do not pad; otherwise add - padding bytes to make the message size a multiple of *pad*. Note that if - padding is non-zero, an EDNS PADDING option will always be added to the - message. - """ - - if edns is None or edns is False: - edns = -1 - elif edns is True: - edns = 0 - if edns < 0: - self.opt = None - self.request_payload = 0 - else: - # make sure the EDNS version in ednsflags agrees with edns - ednsflags &= 0xFF00FFFF - ednsflags |= edns << 16 - if options is None: - options = [] - self.opt = self._make_opt(ednsflags, payload, options) - if request_payload is None: - request_payload = payload - self.request_payload = request_payload - if pad < 0: - raise ValueError("pad must be non-negative") - self.pad = pad - - @property - def edns(self) -> int: - if self.opt: - return (self.ednsflags & 0xFF0000) >> 16 - else: - return -1 - - @property - def ednsflags(self) -> int: - if self.opt: - return self.opt.ttl - else: - return 0 - - @ednsflags.setter - def ednsflags(self, v): - if self.opt: - self.opt.ttl = v - elif v: - self.opt = self._make_opt(v) - - @property - def payload(self) -> int: - if self.opt: - return self.opt[0].payload - else: - return 0 - - @property - def options(self) -> Tuple: - if self.opt: - return self.opt[0].options - else: - return () - - def want_dnssec(self, wanted: bool = True) -> None: - """Enable or disable 'DNSSEC desired' flag in requests. - - *wanted*, a ``bool``. If ``True``, then DNSSEC data is - desired in the response, EDNS is enabled if required, and then - the DO bit is set. If ``False``, the DO bit is cleared if - EDNS is enabled. - """ - - if wanted: - self.ednsflags |= dns.flags.DO - elif self.opt: - self.ednsflags &= ~int(dns.flags.DO) - - def rcode(self) -> dns.rcode.Rcode: - """Return the rcode. - - Returns a ``dns.rcode.Rcode``. - """ - return dns.rcode.from_flags(int(self.flags), int(self.ednsflags)) - - def set_rcode(self, rcode: dns.rcode.Rcode) -> None: - """Set the rcode. - - *rcode*, a ``dns.rcode.Rcode``, is the rcode to set. - """ - (value, evalue) = dns.rcode.to_flags(rcode) - self.flags &= 0xFFF0 - self.flags |= value - self.ednsflags &= 0x00FFFFFF - self.ednsflags |= evalue - - def opcode(self) -> dns.opcode.Opcode: - """Return the opcode. - - Returns a ``dns.opcode.Opcode``. - """ - return dns.opcode.from_flags(int(self.flags)) - - def set_opcode(self, opcode: dns.opcode.Opcode) -> None: - """Set the opcode. - - *opcode*, a ``dns.opcode.Opcode``, is the opcode to set. - """ - self.flags &= 0x87FF - self.flags |= dns.opcode.to_flags(opcode) - - def _get_one_rr_per_rrset(self, value): - # What the caller picked is fine. - return value - - # pylint: disable=unused-argument - - def _parse_rr_header(self, section, name, rdclass, rdtype): - return (rdclass, rdtype, None, False) - - # pylint: enable=unused-argument - - def _parse_special_rr_header(self, section, count, position, name, rdclass, rdtype): - if rdtype == dns.rdatatype.OPT: - if ( - section != MessageSection.ADDITIONAL - or self.opt - or name != dns.name.root - ): - raise BadEDNS - elif rdtype == dns.rdatatype.TSIG: - if ( - section != MessageSection.ADDITIONAL - or rdclass != dns.rdatatype.ANY - or position != count - 1 - ): - raise BadTSIG - return (rdclass, rdtype, None, False) - - -class ChainingResult: - """The result of a call to dns.message.QueryMessage.resolve_chaining(). - - The ``answer`` attribute is the answer RRSet, or ``None`` if it doesn't - exist. - - The ``canonical_name`` attribute is the canonical name after all - chaining has been applied (this is the same name as ``rrset.name`` in cases - where rrset is not ``None``). - - The ``minimum_ttl`` attribute is the minimum TTL, i.e. the TTL to - use if caching the data. It is the smallest of all the CNAME TTLs - and either the answer TTL if it exists or the SOA TTL and SOA - minimum values for negative answers. - - The ``cnames`` attribute is a list of all the CNAME RRSets followed to - get to the canonical name. - """ - - def __init__( - self, - canonical_name: dns.name.Name, - answer: Optional[dns.rrset.RRset], - minimum_ttl: int, - cnames: List[dns.rrset.RRset], - ): - self.canonical_name = canonical_name - self.answer = answer - self.minimum_ttl = minimum_ttl - self.cnames = cnames - - -class QueryMessage(Message): - def resolve_chaining(self) -> ChainingResult: - """Follow the CNAME chain in the response to determine the answer - RRset. - - Raises ``dns.message.NotQueryResponse`` if the message is not - a response. - - Raises ``dns.message.ChainTooLong`` if the CNAME chain is too long. - - Raises ``dns.message.AnswerForNXDOMAIN`` if the rcode is NXDOMAIN - but an answer was found. - - Raises ``dns.exception.FormError`` if the question count is not 1. - - Returns a ChainingResult object. - """ - if self.flags & dns.flags.QR == 0: - raise NotQueryResponse - if len(self.question) != 1: - raise dns.exception.FormError - question = self.question[0] - qname = question.name - min_ttl = dns.ttl.MAX_TTL - answer = None - count = 0 - cnames = [] - while count < MAX_CHAIN: - try: - answer = self.find_rrset( - self.answer, qname, question.rdclass, question.rdtype - ) - min_ttl = min(min_ttl, answer.ttl) - break - except KeyError: - if question.rdtype != dns.rdatatype.CNAME: - try: - crrset = self.find_rrset( - self.answer, qname, question.rdclass, dns.rdatatype.CNAME - ) - cnames.append(crrset) - min_ttl = min(min_ttl, crrset.ttl) - for rd in crrset: - qname = rd.target - break - count += 1 - continue - except KeyError: - # Exit the chaining loop - break - else: - # Exit the chaining loop - break - if count >= MAX_CHAIN: - raise ChainTooLong - if self.rcode() == dns.rcode.NXDOMAIN and answer is not None: - raise AnswerForNXDOMAIN - if answer is None: - # Further minimize the TTL with NCACHE. - auname = qname - while True: - # Look for an SOA RR whose owner name is a superdomain - # of qname. - try: - srrset = self.find_rrset( - self.authority, auname, question.rdclass, dns.rdatatype.SOA - ) - min_ttl = min(min_ttl, srrset.ttl, srrset[0].minimum) - break - except KeyError: - try: - auname = auname.parent() - except dns.name.NoParent: - break - return ChainingResult(qname, answer, min_ttl, cnames) - - def canonical_name(self) -> dns.name.Name: - """Return the canonical name of the first name in the question - section. - - Raises ``dns.message.NotQueryResponse`` if the message is not - a response. - - Raises ``dns.message.ChainTooLong`` if the CNAME chain is too long. - - Raises ``dns.message.AnswerForNXDOMAIN`` if the rcode is NXDOMAIN - but an answer was found. - - Raises ``dns.exception.FormError`` if the question count is not 1. - """ - return self.resolve_chaining().canonical_name - - -def _maybe_import_update(): - # We avoid circular imports by doing this here. We do it in another - # function as doing it in _message_factory_from_opcode() makes "dns" - # a local symbol, and the first line fails :) - - # pylint: disable=redefined-outer-name,import-outside-toplevel,unused-import - import dns.update # noqa: F401 - - -def _message_factory_from_opcode(opcode): - if opcode == dns.opcode.QUERY: - return QueryMessage - elif opcode == dns.opcode.UPDATE: - _maybe_import_update() - return dns.update.UpdateMessage - else: - return Message - - -class _WireReader: - """Wire format reader. - - parser: the binary parser - message: The message object being built - initialize_message: Callback to set message parsing options - question_only: Are we only reading the question? - one_rr_per_rrset: Put each RR into its own RRset? - keyring: TSIG keyring - ignore_trailing: Ignore trailing junk at end of request? - multi: Is this message part of a multi-message sequence? - DNS dynamic updates. - continue_on_error: try to extract as much information as possible from - the message, accumulating MessageErrors in the *errors* attribute instead of - raising them. - """ - - def __init__( - self, - wire, - initialize_message, - question_only=False, - one_rr_per_rrset=False, - ignore_trailing=False, - keyring=None, - multi=False, - continue_on_error=False, - ): - self.parser = dns.wire.Parser(wire) - self.message = None - self.initialize_message = initialize_message - self.question_only = question_only - self.one_rr_per_rrset = one_rr_per_rrset - self.ignore_trailing = ignore_trailing - self.keyring = keyring - self.multi = multi - self.continue_on_error = continue_on_error - self.errors = [] - - def _get_question(self, section_number, qcount): - """Read the next *qcount* records from the wire data and add them to - the question section. - """ - assert self.message is not None - section = self.message.sections[section_number] - for _ in range(qcount): - qname = self.parser.get_name(self.message.origin) - (rdtype, rdclass) = self.parser.get_struct("!HH") - (rdclass, rdtype, _, _) = self.message._parse_rr_header( - section_number, qname, rdclass, rdtype - ) - self.message.find_rrset( - section, qname, rdclass, rdtype, create=True, force_unique=True - ) - - def _add_error(self, e): - self.errors.append(MessageError(e, self.parser.current)) - - def _get_section(self, section_number, count): - """Read the next I{count} records from the wire data and add them to - the specified section. - - section_number: the section of the message to which to add records - count: the number of records to read - """ - assert self.message is not None - section = self.message.sections[section_number] - force_unique = self.one_rr_per_rrset - for i in range(count): - rr_start = self.parser.current - absolute_name = self.parser.get_name() - if self.message.origin is not None: - name = absolute_name.relativize(self.message.origin) - else: - name = absolute_name - (rdtype, rdclass, ttl, rdlen) = self.parser.get_struct("!HHIH") - if rdtype in (dns.rdatatype.OPT, dns.rdatatype.TSIG): - ( - rdclass, - rdtype, - deleting, - empty, - ) = self.message._parse_special_rr_header( - section_number, count, i, name, rdclass, rdtype - ) - else: - (rdclass, rdtype, deleting, empty) = self.message._parse_rr_header( - section_number, name, rdclass, rdtype - ) - rdata_start = self.parser.current - try: - if empty: - if rdlen > 0: - raise dns.exception.FormError - rd = None - covers = dns.rdatatype.NONE - else: - with self.parser.restrict_to(rdlen): - rd = dns.rdata.from_wire_parser( - rdclass, rdtype, self.parser, self.message.origin - ) - covers = rd.covers() - if self.message.xfr and rdtype == dns.rdatatype.SOA: - force_unique = True - if rdtype == dns.rdatatype.OPT: - self.message.opt = dns.rrset.from_rdata(name, ttl, rd) - elif rdtype == dns.rdatatype.TSIG: - if self.keyring is None: - raise UnknownTSIGKey("got signed message without keyring") - if isinstance(self.keyring, dict): - key = self.keyring.get(absolute_name) - if isinstance(key, bytes): - key = dns.tsig.Key(absolute_name, key, rd.algorithm) - elif callable(self.keyring): - key = self.keyring(self.message, absolute_name) - else: - key = self.keyring - if key is None: - raise UnknownTSIGKey("key '%s' unknown" % name) - self.message.keyring = key - self.message.tsig_ctx = dns.tsig.validate( - self.parser.wire, - key, - absolute_name, - rd, - int(time.time()), - self.message.request_mac, - rr_start, - self.message.tsig_ctx, - self.multi, - ) - self.message.tsig = dns.rrset.from_rdata(absolute_name, 0, rd) - else: - rrset = self.message.find_rrset( - section, - name, - rdclass, - rdtype, - covers, - deleting, - True, - force_unique, - ) - if rd is not None: - if ttl > 0x7FFFFFFF: - ttl = 0 - rrset.add(rd, ttl) - except Exception as e: - if self.continue_on_error: - self._add_error(e) - self.parser.seek(rdata_start + rdlen) - else: - raise - - def read(self): - """Read a wire format DNS message and build a dns.message.Message - object.""" - - if self.parser.remaining() < 12: - raise ShortHeader - (id, flags, qcount, ancount, aucount, adcount) = self.parser.get_struct( - "!HHHHHH" - ) - factory = _message_factory_from_opcode(dns.opcode.from_flags(flags)) - self.message = factory(id=id) - self.message.flags = dns.flags.Flag(flags) - self.initialize_message(self.message) - self.one_rr_per_rrset = self.message._get_one_rr_per_rrset( - self.one_rr_per_rrset - ) - try: - self._get_question(MessageSection.QUESTION, qcount) - if self.question_only: - return self.message - self._get_section(MessageSection.ANSWER, ancount) - self._get_section(MessageSection.AUTHORITY, aucount) - self._get_section(MessageSection.ADDITIONAL, adcount) - if not self.ignore_trailing and self.parser.remaining() != 0: - raise TrailingJunk - if self.multi and self.message.tsig_ctx and not self.message.had_tsig: - self.message.tsig_ctx.update(self.parser.wire) - except Exception as e: - if self.continue_on_error: - self._add_error(e) - else: - raise - return self.message - - -def from_wire( - wire: bytes, - keyring: Optional[Any] = None, - request_mac: Optional[bytes] = b"", - xfr: bool = False, - origin: Optional[dns.name.Name] = None, - tsig_ctx: Optional[Union[dns.tsig.HMACTSig, dns.tsig.GSSTSig]] = None, - multi: bool = False, - question_only: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - continue_on_error: bool = False, -) -> Message: - """Convert a DNS wire format message into a message object. - - *keyring*, a ``dns.tsig.Key`` or ``dict``, the key or keyring to use if the message - is signed. - - *request_mac*, a ``bytes`` or ``None``. If the message is a response to a - TSIG-signed request, *request_mac* should be set to the MAC of that request. - - *xfr*, a ``bool``, should be set to ``True`` if this message is part of a zone - transfer. - - *origin*, a ``dns.name.Name`` or ``None``. If the message is part of a zone - transfer, *origin* should be the origin name of the zone. If not ``None``, names - will be relativized to the origin. - - *tsig_ctx*, a ``dns.tsig.HMACTSig`` or ``dns.tsig.GSSTSig`` object, the ongoing TSIG - context, used when validating zone transfers. - - *multi*, a ``bool``, should be set to ``True`` if this message is part of a multiple - message sequence. - - *question_only*, a ``bool``. If ``True``, read only up to the end of the question - section. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - message. - - *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if the TC bit is - set. - - *continue_on_error*, a ``bool``. If ``True``, try to continue parsing even if - errors occur. Erroneous rdata will be ignored. Errors will be accumulated as a - list of MessageError objects in the message's ``errors`` attribute. This option is - recommended only for DNS analysis tools, or for use in a server as part of an error - handling path. The default is ``False``. - - Raises ``dns.message.ShortHeader`` if the message is less than 12 octets long. - - Raises ``dns.message.TrailingJunk`` if there were octets in the message past the end - of the proper DNS message, and *ignore_trailing* is ``False``. - - Raises ``dns.message.BadEDNS`` if an OPT record was in the wrong section, or - occurred more than once. - - Raises ``dns.message.BadTSIG`` if a TSIG record was not the last record of the - additional data section. - - Raises ``dns.message.Truncated`` if the TC flag is set and *raise_on_truncation* is - ``True``. - - Returns a ``dns.message.Message``. - """ - - # We permit None for request_mac solely for backwards compatibility - if request_mac is None: - request_mac = b"" - - def initialize_message(message): - message.request_mac = request_mac - message.xfr = xfr - message.origin = origin - message.tsig_ctx = tsig_ctx - - reader = _WireReader( - wire, - initialize_message, - question_only, - one_rr_per_rrset, - ignore_trailing, - keyring, - multi, - continue_on_error, - ) - try: - m = reader.read() - except dns.exception.FormError: - if ( - reader.message - and (reader.message.flags & dns.flags.TC) - and raise_on_truncation - ): - raise Truncated(message=reader.message) - else: - raise - # Reading a truncated message might not have any errors, so we - # have to do this check here too. - if m.flags & dns.flags.TC and raise_on_truncation: - raise Truncated(message=m) - if continue_on_error: - m.errors = reader.errors - - return m - - -class _TextReader: - """Text format reader. - - tok: the tokenizer. - message: The message object being built. - DNS dynamic updates. - last_name: The most recently read name when building a message object. - one_rr_per_rrset: Put each RR into its own RRset? - origin: The origin for relative names - relativize: relativize names? - relativize_to: the origin to relativize to. - """ - - def __init__( - self, - text, - idna_codec, - one_rr_per_rrset=False, - origin=None, - relativize=True, - relativize_to=None, - ): - self.message = None - self.tok = dns.tokenizer.Tokenizer(text, idna_codec=idna_codec) - self.last_name = None - self.one_rr_per_rrset = one_rr_per_rrset - self.origin = origin - self.relativize = relativize - self.relativize_to = relativize_to - self.id = None - self.edns = -1 - self.ednsflags = 0 - self.payload = DEFAULT_EDNS_PAYLOAD - self.rcode = None - self.opcode = dns.opcode.QUERY - self.flags = 0 - - def _header_line(self, _): - """Process one line from the text format header section.""" - - token = self.tok.get() - what = token.value - if what == "id": - self.id = self.tok.get_int() - elif what == "flags": - while True: - token = self.tok.get() - if not token.is_identifier(): - self.tok.unget(token) - break - self.flags = self.flags | dns.flags.from_text(token.value) - elif what == "edns": - self.edns = self.tok.get_int() - self.ednsflags = self.ednsflags | (self.edns << 16) - elif what == "eflags": - if self.edns < 0: - self.edns = 0 - while True: - token = self.tok.get() - if not token.is_identifier(): - self.tok.unget(token) - break - self.ednsflags = self.ednsflags | dns.flags.edns_from_text(token.value) - elif what == "payload": - self.payload = self.tok.get_int() - if self.edns < 0: - self.edns = 0 - elif what == "opcode": - text = self.tok.get_string() - self.opcode = dns.opcode.from_text(text) - self.flags = self.flags | dns.opcode.to_flags(self.opcode) - elif what == "rcode": - text = self.tok.get_string() - self.rcode = dns.rcode.from_text(text) - else: - raise UnknownHeaderField - self.tok.get_eol() - - def _question_line(self, section_number): - """Process one line from the text format question section.""" - - section = self.message.sections[section_number] - token = self.tok.get(want_leading=True) - if not token.is_whitespace(): - self.last_name = self.tok.as_name( - token, self.message.origin, self.relativize, self.relativize_to - ) - name = self.last_name - if name is None: - raise NoPreviousName - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - rdclass = dns.rdataclass.IN - # Type - rdtype = dns.rdatatype.from_text(token.value) - (rdclass, rdtype, _, _) = self.message._parse_rr_header( - section_number, name, rdclass, rdtype - ) - self.message.find_rrset( - section, name, rdclass, rdtype, create=True, force_unique=True - ) - self.tok.get_eol() - - def _rr_line(self, section_number): - """Process one line from the text format answer, authority, or - additional data sections. - """ - - section = self.message.sections[section_number] - # Name - token = self.tok.get(want_leading=True) - if not token.is_whitespace(): - self.last_name = self.tok.as_name( - token, self.message.origin, self.relativize, self.relativize_to - ) - name = self.last_name - if name is None: - raise NoPreviousName - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - # TTL - try: - ttl = int(token.value, 0) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - ttl = 0 - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - rdclass = dns.rdataclass.IN - # Type - rdtype = dns.rdatatype.from_text(token.value) - (rdclass, rdtype, deleting, empty) = self.message._parse_rr_header( - section_number, name, rdclass, rdtype - ) - token = self.tok.get() - if empty and not token.is_eol_or_eof(): - raise dns.exception.SyntaxError - if not empty and token.is_eol_or_eof(): - raise dns.exception.UnexpectedEnd - if not token.is_eol_or_eof(): - self.tok.unget(token) - rd = dns.rdata.from_text( - rdclass, - rdtype, - self.tok, - self.message.origin, - self.relativize, - self.relativize_to, - ) - covers = rd.covers() - else: - rd = None - covers = dns.rdatatype.NONE - rrset = self.message.find_rrset( - section, - name, - rdclass, - rdtype, - covers, - deleting, - True, - self.one_rr_per_rrset, - ) - if rd is not None: - rrset.add(rd, ttl) - - def _make_message(self): - factory = _message_factory_from_opcode(self.opcode) - message = factory(id=self.id) - message.flags = self.flags - if self.edns >= 0: - message.use_edns(self.edns, self.ednsflags, self.payload) - if self.rcode: - message.set_rcode(self.rcode) - if self.origin: - message.origin = self.origin - return message - - def read(self): - """Read a text format DNS message and build a dns.message.Message - object.""" - - line_method = self._header_line - section_number = None - while 1: - token = self.tok.get(True, True) - if token.is_eol_or_eof(): - break - if token.is_comment(): - u = token.value.upper() - if u == "HEADER": - line_method = self._header_line - - if self.message: - message = self.message - else: - # If we don't have a message, create one with the current - # opcode, so that we know which section names to parse. - message = self._make_message() - try: - section_number = message._section_enum.from_text(u) - # We found a section name. If we don't have a message, - # use the one we just created. - if not self.message: - self.message = message - self.one_rr_per_rrset = message._get_one_rr_per_rrset( - self.one_rr_per_rrset - ) - if section_number == MessageSection.QUESTION: - line_method = self._question_line - else: - line_method = self._rr_line - except Exception: - # It's just a comment. - pass - self.tok.get_eol() - continue - self.tok.unget(token) - line_method(section_number) - if not self.message: - self.message = self._make_message() - return self.message - - -def from_text( - text: str, - idna_codec: Optional[dns.name.IDNACodec] = None, - one_rr_per_rrset: bool = False, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, -) -> Message: - """Convert the text format message into a message object. - - The reader stops after reading the first blank line in the input to - facilitate reading multiple messages from a single file with - ``dns.message.from_file()``. - - *text*, a ``str``, the text format message. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *one_rr_per_rrset*, a ``bool``. If ``True``, then each RR is put - into its own rrset. The default is ``False``. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - Raises ``dns.message.UnknownHeaderField`` if a header is unknown. - - Raises ``dns.exception.SyntaxError`` if the text is badly formed. - - Returns a ``dns.message.Message object`` - """ - - # 'text' can also be a file, but we don't publish that fact - # since it's an implementation detail. The official file - # interface is from_file(). - - reader = _TextReader( - text, idna_codec, one_rr_per_rrset, origin, relativize, relativize_to - ) - return reader.read() - - -def from_file( - f: Any, - idna_codec: Optional[dns.name.IDNACodec] = None, - one_rr_per_rrset: bool = False, -) -> Message: - """Read the next text format message from the specified file. - - Message blocks are separated by a single blank line. - - *f*, a ``file`` or ``str``. If *f* is text, it is treated as the - pathname of a file to open. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *one_rr_per_rrset*, a ``bool``. If ``True``, then each RR is put - into its own rrset. The default is ``False``. - - Raises ``dns.message.UnknownHeaderField`` if a header is unknown. - - Raises ``dns.exception.SyntaxError`` if the text is badly formed. - - Returns a ``dns.message.Message object`` - """ - - if isinstance(f, str): - cm: contextlib.AbstractContextManager = open(f) - else: - cm = contextlib.nullcontext(f) - with cm as f: - return from_text(f, idna_codec, one_rr_per_rrset) - assert False # for mypy lgtm[py/unreachable-statement] - - -def make_query( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - use_edns: Optional[Union[int, bool]] = None, - want_dnssec: bool = False, - ednsflags: Optional[int] = None, - payload: Optional[int] = None, - request_payload: Optional[int] = None, - options: Optional[List[dns.edns.Option]] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, - id: Optional[int] = None, - flags: int = dns.flags.RD, - pad: int = 0, -) -> QueryMessage: - """Make a query message. - - The query name, type, and class may all be specified either - as objects of the appropriate type, or as strings. - - The query will have a randomly chosen query id, and its DNS flags - will be set to dns.flags.RD. - - qname, a ``dns.name.Name`` or ``str``, the query name. - - *rdtype*, an ``int`` or ``str``, the desired rdata type. - - *rdclass*, an ``int`` or ``str``, the desired rdata class; the default - is class IN. - - *use_edns*, an ``int``, ``bool`` or ``None``. The EDNS level to use; the - default is ``None``. If ``None``, EDNS will be enabled only if other - parameters (*ednsflags*, *payload*, *request_payload*, or *options*) are - set. - See the description of dns.message.Message.use_edns() for the possible - values for use_edns and their meanings. - - *want_dnssec*, a ``bool``. If ``True``, DNSSEC data is desired. - - *ednsflags*, an ``int``, the EDNS flag values. - - *payload*, an ``int``, is the EDNS sender's payload field, which is the - maximum size of UDP datagram the sender can handle. I.e. how big - a response to this message can be. - - *request_payload*, an ``int``, is the EDNS payload size to use when - sending this message. If not specified, defaults to the value of - *payload*. - - *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS - options. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *id*, an ``int`` or ``None``, the desired query id. The default is - ``None``, which generates a random query id. - - *flags*, an ``int``, the desired query flags. The default is - ``dns.flags.RD``. - - *pad*, a non-negative ``int``. If 0, the default, do not pad; otherwise add - padding bytes to make the message size a multiple of *pad*. Note that if - padding is non-zero, an EDNS PADDING option will always be added to the - message. - - Returns a ``dns.message.QueryMessage`` - """ - - if isinstance(qname, str): - qname = dns.name.from_text(qname, idna_codec=idna_codec) - rdtype = dns.rdatatype.RdataType.make(rdtype) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - m = QueryMessage(id=id) - m.flags = dns.flags.Flag(flags) - m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True) - # only pass keywords on to use_edns if they have been set to a - # non-None value. Setting a field will turn EDNS on if it hasn't - # been configured. - kwargs: Dict[str, Any] = {} - if ednsflags is not None: - kwargs["ednsflags"] = ednsflags - if payload is not None: - kwargs["payload"] = payload - if request_payload is not None: - kwargs["request_payload"] = request_payload - if options is not None: - kwargs["options"] = options - if kwargs and use_edns is None: - use_edns = 0 - kwargs["edns"] = use_edns - kwargs["pad"] = pad - m.use_edns(**kwargs) - m.want_dnssec(want_dnssec) - return m - - -def make_response( - query: Message, - recursion_available: bool = False, - our_payload: int = 8192, - fudge: int = 300, - tsig_error: int = 0, - pad: Optional[int] = None, -) -> Message: - """Make a message which is a response for the specified query. - The message returned is really a response skeleton; it has all of the infrastructure - required of a response, but none of the content. - - The response's question section is a shallow copy of the query's question section, - so the query's question RRsets should not be changed. - - *query*, a ``dns.message.Message``, the query to respond to. - - *recursion_available*, a ``bool``, should RA be set in the response? - - *our_payload*, an ``int``, the payload size to advertise in EDNS responses. - - *fudge*, an ``int``, the TSIG time fudge. - - *tsig_error*, an ``int``, the TSIG error. - - *pad*, a non-negative ``int`` or ``None``. If 0, the default, do not pad; otherwise - if not ``None`` add padding bytes to make the message size a multiple of *pad*. - Note that if padding is non-zero, an EDNS PADDING option will always be added to the - message. If ``None``, add padding following RFC 8467, namely if the request is - padded, pad the response to 468 otherwise do not pad. - - Returns a ``dns.message.Message`` object whose specific class is appropriate for the - query. For example, if query is a ``dns.update.UpdateMessage``, response will be - too. - """ - - if query.flags & dns.flags.QR: - raise dns.exception.FormError("specified query message is not a query") - factory = _message_factory_from_opcode(query.opcode()) - response = factory(id=query.id) - response.flags = dns.flags.QR | (query.flags & dns.flags.RD) - if recursion_available: - response.flags |= dns.flags.RA - response.set_opcode(query.opcode()) - response.question = list(query.question) - if query.edns >= 0: - if pad is None: - # Set response padding per RFC 8467 - pad = 0 - for option in query.options: - if option.otype == dns.edns.OptionType.PADDING: - pad = 468 - response.use_edns(0, 0, our_payload, query.payload, pad=pad) - if query.had_tsig: - response.use_tsig( - query.keyring, - query.keyname, - fudge, - None, - tsig_error, - b"", - query.keyalgorithm, - ) - response.request_mac = query.mac - return response - - -### BEGIN generated MessageSection constants - -QUESTION = MessageSection.QUESTION -ANSWER = MessageSection.ANSWER -AUTHORITY = MessageSection.AUTHORITY -ADDITIONAL = MessageSection.ADDITIONAL - -### END generated MessageSection constants diff --git a/.venv/lib/python3.12/site-packages/dns/name.py b/.venv/lib/python3.12/site-packages/dns/name.py deleted file mode 100644 index 22ccb39..0000000 --- a/.venv/lib/python3.12/site-packages/dns/name.py +++ /dev/null @@ -1,1283 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Names. -""" - -import copy -import encodings.idna # type: ignore -import functools -import struct -from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union - -import dns._features -import dns.enum -import dns.exception -import dns.immutable -import dns.wire - -if dns._features.have("idna"): - import idna # type: ignore - - have_idna_2008 = True -else: # pragma: no cover - have_idna_2008 = False - -CompressType = Dict["Name", int] - - -class NameRelation(dns.enum.IntEnum): - """Name relation result from fullcompare().""" - - # This is an IntEnum for backwards compatibility in case anyone - # has hardwired the constants. - - #: The compared names have no relationship to each other. - NONE = 0 - #: the first name is a superdomain of the second. - SUPERDOMAIN = 1 - #: The first name is a subdomain of the second. - SUBDOMAIN = 2 - #: The compared names are equal. - EQUAL = 3 - #: The compared names have a common ancestor. - COMMONANCESTOR = 4 - - @classmethod - def _maximum(cls): - return cls.COMMONANCESTOR - - @classmethod - def _short_name(cls): - return cls.__name__ - - -# Backwards compatibility -NAMERELN_NONE = NameRelation.NONE -NAMERELN_SUPERDOMAIN = NameRelation.SUPERDOMAIN -NAMERELN_SUBDOMAIN = NameRelation.SUBDOMAIN -NAMERELN_EQUAL = NameRelation.EQUAL -NAMERELN_COMMONANCESTOR = NameRelation.COMMONANCESTOR - - -class EmptyLabel(dns.exception.SyntaxError): - """A DNS label is empty.""" - - -class BadEscape(dns.exception.SyntaxError): - """An escaped code in a text format of DNS name is invalid.""" - - -class BadPointer(dns.exception.FormError): - """A DNS compression pointer points forward instead of backward.""" - - -class BadLabelType(dns.exception.FormError): - """The label type in DNS name wire format is unknown.""" - - -class NeedAbsoluteNameOrOrigin(dns.exception.DNSException): - """An attempt was made to convert a non-absolute name to - wire when there was also a non-absolute (or missing) origin.""" - - -class NameTooLong(dns.exception.FormError): - """A DNS name is > 255 octets long.""" - - -class LabelTooLong(dns.exception.SyntaxError): - """A DNS label is > 63 octets long.""" - - -class AbsoluteConcatenation(dns.exception.DNSException): - """An attempt was made to append anything other than the - empty name to an absolute DNS name.""" - - -class NoParent(dns.exception.DNSException): - """An attempt was made to get the parent of the root name - or the empty name.""" - - -class NoIDNA2008(dns.exception.DNSException): - """IDNA 2008 processing was requested but the idna module is not - available.""" - - -class IDNAException(dns.exception.DNSException): - """IDNA processing raised an exception.""" - - supp_kwargs = {"idna_exception"} - fmt = "IDNA processing exception: {idna_exception}" - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - -class NeedSubdomainOfOrigin(dns.exception.DNSException): - """An absolute name was provided that is not a subdomain of the specified origin.""" - - -_escaped = b'"().;\\@$' -_escaped_text = '"().;\\@$' - - -def _escapify(label: Union[bytes, str]) -> str: - """Escape the characters in label which need it. - @returns: the escaped string - @rtype: string""" - if isinstance(label, bytes): - # Ordinary DNS label mode. Escape special characters and values - # < 0x20 or > 0x7f. - text = "" - for c in label: - if c in _escaped: - text += "\\" + chr(c) - elif c > 0x20 and c < 0x7F: - text += chr(c) - else: - text += "\\%03d" % c - return text - - # Unicode label mode. Escape only special characters and values < 0x20 - text = "" - for uc in label: - if uc in _escaped_text: - text += "\\" + uc - elif uc <= "\x20": - text += "\\%03d" % ord(uc) - else: - text += uc - return text - - -class IDNACodec: - """Abstract base class for IDNA encoder/decoders.""" - - def __init__(self): - pass - - def is_idna(self, label: bytes) -> bool: - return label.lower().startswith(b"xn--") - - def encode(self, label: str) -> bytes: - raise NotImplementedError # pragma: no cover - - def decode(self, label: bytes) -> str: - # We do not apply any IDNA policy on decode. - if self.is_idna(label): - try: - slabel = label[4:].decode("punycode") - return _escapify(slabel) - except Exception as e: - raise IDNAException(idna_exception=e) - else: - return _escapify(label) - - -class IDNA2003Codec(IDNACodec): - """IDNA 2003 encoder/decoder.""" - - def __init__(self, strict_decode: bool = False): - """Initialize the IDNA 2003 encoder/decoder. - - *strict_decode* is a ``bool``. If `True`, then IDNA2003 checking - is done when decoding. This can cause failures if the name - was encoded with IDNA2008. The default is `False`. - """ - - super().__init__() - self.strict_decode = strict_decode - - def encode(self, label: str) -> bytes: - """Encode *label*.""" - - if label == "": - return b"" - try: - return encodings.idna.ToASCII(label) - except UnicodeError: - raise LabelTooLong - - def decode(self, label: bytes) -> str: - """Decode *label*.""" - if not self.strict_decode: - return super().decode(label) - if label == b"": - return "" - try: - return _escapify(encodings.idna.ToUnicode(label)) - except Exception as e: - raise IDNAException(idna_exception=e) - - -class IDNA2008Codec(IDNACodec): - """IDNA 2008 encoder/decoder.""" - - def __init__( - self, - uts_46: bool = False, - transitional: bool = False, - allow_pure_ascii: bool = False, - strict_decode: bool = False, - ): - """Initialize the IDNA 2008 encoder/decoder. - - *uts_46* is a ``bool``. If True, apply Unicode IDNA - compatibility processing as described in Unicode Technical - Standard #46 (https://unicode.org/reports/tr46/). - If False, do not apply the mapping. The default is False. - - *transitional* is a ``bool``: If True, use the - "transitional" mode described in Unicode Technical Standard - #46. The default is False. - - *allow_pure_ascii* is a ``bool``. If True, then a label which - consists of only ASCII characters is allowed. This is less - strict than regular IDNA 2008, but is also necessary for mixed - names, e.g. a name with starting with "_sip._tcp." and ending - in an IDN suffix which would otherwise be disallowed. The - default is False. - - *strict_decode* is a ``bool``: If True, then IDNA2008 checking - is done when decoding. This can cause failures if the name - was encoded with IDNA2003. The default is False. - """ - super().__init__() - self.uts_46 = uts_46 - self.transitional = transitional - self.allow_pure_ascii = allow_pure_ascii - self.strict_decode = strict_decode - - def encode(self, label: str) -> bytes: - if label == "": - return b"" - if self.allow_pure_ascii and is_all_ascii(label): - encoded = label.encode("ascii") - if len(encoded) > 63: - raise LabelTooLong - return encoded - if not have_idna_2008: - raise NoIDNA2008 - try: - if self.uts_46: - label = idna.uts46_remap(label, False, self.transitional) - return idna.alabel(label) - except idna.IDNAError as e: - if e.args[0] == "Label too long": - raise LabelTooLong - else: - raise IDNAException(idna_exception=e) - - def decode(self, label: bytes) -> str: - if not self.strict_decode: - return super().decode(label) - if label == b"": - return "" - if not have_idna_2008: - raise NoIDNA2008 - try: - ulabel = idna.ulabel(label) - if self.uts_46: - ulabel = idna.uts46_remap(ulabel, False, self.transitional) - return _escapify(ulabel) - except (idna.IDNAError, UnicodeError) as e: - raise IDNAException(idna_exception=e) - - -IDNA_2003_Practical = IDNA2003Codec(False) -IDNA_2003_Strict = IDNA2003Codec(True) -IDNA_2003 = IDNA_2003_Practical -IDNA_2008_Practical = IDNA2008Codec(True, False, True, False) -IDNA_2008_UTS_46 = IDNA2008Codec(True, False, False, False) -IDNA_2008_Strict = IDNA2008Codec(False, False, False, True) -IDNA_2008_Transitional = IDNA2008Codec(True, True, False, False) -IDNA_2008 = IDNA_2008_Practical - - -def _validate_labels(labels: Tuple[bytes, ...]) -> None: - """Check for empty labels in the middle of a label sequence, - labels that are too long, and for too many labels. - - Raises ``dns.name.NameTooLong`` if the name as a whole is too long. - - Raises ``dns.name.EmptyLabel`` if a label is empty (i.e. the root - label) and appears in a position other than the end of the label - sequence - - """ - - l = len(labels) - total = 0 - i = -1 - j = 0 - for label in labels: - ll = len(label) - total += ll + 1 - if ll > 63: - raise LabelTooLong - if i < 0 and label == b"": - i = j - j += 1 - if total > 255: - raise NameTooLong - if i >= 0 and i != l - 1: - raise EmptyLabel - - -def _maybe_convert_to_binary(label: Union[bytes, str]) -> bytes: - """If label is ``str``, convert it to ``bytes``. If it is already - ``bytes`` just return it. - - """ - - if isinstance(label, bytes): - return label - if isinstance(label, str): - return label.encode() - raise ValueError # pragma: no cover - - -@dns.immutable.immutable -class Name: - """A DNS name. - - The dns.name.Name class represents a DNS name as a tuple of - labels. Each label is a ``bytes`` in DNS wire format. Instances - of the class are immutable. - """ - - __slots__ = ["labels"] - - def __init__(self, labels: Iterable[Union[bytes, str]]): - """*labels* is any iterable whose values are ``str`` or ``bytes``.""" - - blabels = [_maybe_convert_to_binary(x) for x in labels] - self.labels = tuple(blabels) - _validate_labels(self.labels) - - def __copy__(self): - return Name(self.labels) - - def __deepcopy__(self, memo): - return Name(copy.deepcopy(self.labels, memo)) - - def __getstate__(self): - # Names can be pickled - return {"labels": self.labels} - - def __setstate__(self, state): - super().__setattr__("labels", state["labels"]) - _validate_labels(self.labels) - - def is_absolute(self) -> bool: - """Is the most significant label of this name the root label? - - Returns a ``bool``. - """ - - return len(self.labels) > 0 and self.labels[-1] == b"" - - def is_wild(self) -> bool: - """Is this name wild? (I.e. Is the least significant label '*'?) - - Returns a ``bool``. - """ - - return len(self.labels) > 0 and self.labels[0] == b"*" - - def __hash__(self) -> int: - """Return a case-insensitive hash of the name. - - Returns an ``int``. - """ - - h = 0 - for label in self.labels: - for c in label.lower(): - h += (h << 3) + c - return h - - def fullcompare(self, other: "Name") -> Tuple[NameRelation, int, int]: - """Compare two names, returning a 3-tuple - ``(relation, order, nlabels)``. - - *relation* describes the relation ship between the names, - and is one of: ``dns.name.NameRelation.NONE``, - ``dns.name.NameRelation.SUPERDOMAIN``, ``dns.name.NameRelation.SUBDOMAIN``, - ``dns.name.NameRelation.EQUAL``, or ``dns.name.NameRelation.COMMONANCESTOR``. - - *order* is < 0 if *self* < *other*, > 0 if *self* > *other*, and == - 0 if *self* == *other*. A relative name is always less than an - absolute name. If both names have the same relativity, then - the DNSSEC order relation is used to order them. - - *nlabels* is the number of significant labels that the two names - have in common. - - Here are some examples. Names ending in "." are absolute names, - those not ending in "." are relative names. - - ============= ============= =========== ===== ======= - self other relation order nlabels - ============= ============= =========== ===== ======= - www.example. www.example. equal 0 3 - www.example. example. subdomain > 0 2 - example. www.example. superdomain < 0 2 - example1.com. example2.com. common anc. < 0 2 - example1 example2. none < 0 0 - example1. example2 none > 0 0 - ============= ============= =========== ===== ======= - """ - - sabs = self.is_absolute() - oabs = other.is_absolute() - if sabs != oabs: - if sabs: - return (NameRelation.NONE, 1, 0) - else: - return (NameRelation.NONE, -1, 0) - l1 = len(self.labels) - l2 = len(other.labels) - ldiff = l1 - l2 - if ldiff < 0: - l = l1 - else: - l = l2 - - order = 0 - nlabels = 0 - namereln = NameRelation.NONE - while l > 0: - l -= 1 - l1 -= 1 - l2 -= 1 - label1 = self.labels[l1].lower() - label2 = other.labels[l2].lower() - if label1 < label2: - order = -1 - if nlabels > 0: - namereln = NameRelation.COMMONANCESTOR - return (namereln, order, nlabels) - elif label1 > label2: - order = 1 - if nlabels > 0: - namereln = NameRelation.COMMONANCESTOR - return (namereln, order, nlabels) - nlabels += 1 - order = ldiff - if ldiff < 0: - namereln = NameRelation.SUPERDOMAIN - elif ldiff > 0: - namereln = NameRelation.SUBDOMAIN - else: - namereln = NameRelation.EQUAL - return (namereln, order, nlabels) - - def is_subdomain(self, other: "Name") -> bool: - """Is self a subdomain of other? - - Note that the notion of subdomain includes equality, e.g. - "dnspython.org" is a subdomain of itself. - - Returns a ``bool``. - """ - - (nr, _, _) = self.fullcompare(other) - if nr == NameRelation.SUBDOMAIN or nr == NameRelation.EQUAL: - return True - return False - - def is_superdomain(self, other: "Name") -> bool: - """Is self a superdomain of other? - - Note that the notion of superdomain includes equality, e.g. - "dnspython.org" is a superdomain of itself. - - Returns a ``bool``. - """ - - (nr, _, _) = self.fullcompare(other) - if nr == NameRelation.SUPERDOMAIN or nr == NameRelation.EQUAL: - return True - return False - - def canonicalize(self) -> "Name": - """Return a name which is equal to the current name, but is in - DNSSEC canonical form. - """ - - return Name([x.lower() for x in self.labels]) - - def __eq__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] != 0 - else: - return True - - def __lt__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] < 0 - else: - return NotImplemented - - def __le__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] <= 0 - else: - return NotImplemented - - def __ge__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] >= 0 - else: - return NotImplemented - - def __gt__(self, other): - if isinstance(other, Name): - return self.fullcompare(other)[1] > 0 - else: - return NotImplemented - - def __repr__(self): - return "" - - def __str__(self): - return self.to_text(False) - - def to_text(self, omit_final_dot: bool = False) -> str: - """Convert name to DNS text format. - - *omit_final_dot* is a ``bool``. If True, don't emit the final - dot (denoting the root label) for absolute names. The default - is False. - - Returns a ``str``. - """ - - if len(self.labels) == 0: - return "@" - if len(self.labels) == 1 and self.labels[0] == b"": - return "." - if omit_final_dot and self.is_absolute(): - l = self.labels[:-1] - else: - l = self.labels - s = ".".join(map(_escapify, l)) - return s - - def to_unicode( - self, omit_final_dot: bool = False, idna_codec: Optional[IDNACodec] = None - ) -> str: - """Convert name to Unicode text format. - - IDN ACE labels are converted to Unicode. - - *omit_final_dot* is a ``bool``. If True, don't emit the final - dot (denoting the root label) for absolute names. The default - is False. - *idna_codec* specifies the IDNA encoder/decoder. If None, the - dns.name.IDNA_2003_Practical encoder/decoder is used. - The IDNA_2003_Practical decoder does - not impose any policy, it just decodes punycode, so if you - don't want checking for compliance, you can use this decoder - for IDNA2008 as well. - - Returns a ``str``. - """ - - if len(self.labels) == 0: - return "@" - if len(self.labels) == 1 and self.labels[0] == b"": - return "." - if omit_final_dot and self.is_absolute(): - l = self.labels[:-1] - else: - l = self.labels - if idna_codec is None: - idna_codec = IDNA_2003_Practical - return ".".join([idna_codec.decode(x) for x in l]) - - def to_digestable(self, origin: Optional["Name"] = None) -> bytes: - """Convert name to a format suitable for digesting in hashes. - - The name is canonicalized and converted to uncompressed wire - format. All names in wire format are absolute. If the name - is a relative name, then an origin must be supplied. - - *origin* is a ``dns.name.Name`` or ``None``. If the name is - relative and origin is not ``None``, then origin will be appended - to the name. - - Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is - relative and no origin was provided. - - Returns a ``bytes``. - """ - - digest = self.to_wire(origin=origin, canonicalize=True) - assert digest is not None - return digest - - def to_wire( - self, - file: Optional[Any] = None, - compress: Optional[CompressType] = None, - origin: Optional["Name"] = None, - canonicalize: bool = False, - ) -> Optional[bytes]: - """Convert name to wire format, possibly compressing it. - - *file* is the file where the name is emitted (typically an - io.BytesIO file). If ``None`` (the default), a ``bytes`` - containing the wire name will be returned. - - *compress*, a ``dict``, is the compression table to use. If - ``None`` (the default), names will not be compressed. Note that - the compression code assumes that compression offset 0 is the - start of *file*, and thus compression will not be correct - if this is not the case. - - *origin* is a ``dns.name.Name`` or ``None``. If the name is - relative and origin is not ``None``, then *origin* will be appended - to it. - - *canonicalize*, a ``bool``, indicates whether the name should - be canonicalized; that is, converted to a format suitable for - digesting in hashes. - - Raises ``dns.name.NeedAbsoluteNameOrOrigin`` if the name is - relative and no origin was provided. - - Returns a ``bytes`` or ``None``. - """ - - if file is None: - out = bytearray() - for label in self.labels: - out.append(len(label)) - if canonicalize: - out += label.lower() - else: - out += label - if not self.is_absolute(): - if origin is None or not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - for label in origin.labels: - out.append(len(label)) - if canonicalize: - out += label.lower() - else: - out += label - return bytes(out) - - labels: Iterable[bytes] - if not self.is_absolute(): - if origin is None or not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - labels = list(self.labels) - labels.extend(list(origin.labels)) - else: - labels = self.labels - i = 0 - for label in labels: - n = Name(labels[i:]) - i += 1 - if compress is not None: - pos = compress.get(n) - else: - pos = None - if pos is not None: - value = 0xC000 + pos - s = struct.pack("!H", value) - file.write(s) - break - else: - if compress is not None and len(n) > 1: - pos = file.tell() - if pos <= 0x3FFF: - compress[n] = pos - l = len(label) - file.write(struct.pack("!B", l)) - if l > 0: - if canonicalize: - file.write(label.lower()) - else: - file.write(label) - return None - - def __len__(self) -> int: - """The length of the name (in labels). - - Returns an ``int``. - """ - - return len(self.labels) - - def __getitem__(self, index): - return self.labels[index] - - def __add__(self, other): - return self.concatenate(other) - - def __sub__(self, other): - return self.relativize(other) - - def split(self, depth: int) -> Tuple["Name", "Name"]: - """Split a name into a prefix and suffix names at the specified depth. - - *depth* is an ``int`` specifying the number of labels in the suffix - - Raises ``ValueError`` if *depth* was not >= 0 and <= the length of the - name. - - Returns the tuple ``(prefix, suffix)``. - """ - - l = len(self.labels) - if depth == 0: - return (self, dns.name.empty) - elif depth == l: - return (dns.name.empty, self) - elif depth < 0 or depth > l: - raise ValueError("depth must be >= 0 and <= the length of the name") - return (Name(self[:-depth]), Name(self[-depth:])) - - def concatenate(self, other: "Name") -> "Name": - """Return a new name which is the concatenation of self and other. - - Raises ``dns.name.AbsoluteConcatenation`` if the name is - absolute and *other* is not the empty name. - - Returns a ``dns.name.Name``. - """ - - if self.is_absolute() and len(other) > 0: - raise AbsoluteConcatenation - labels = list(self.labels) - labels.extend(list(other.labels)) - return Name(labels) - - def relativize(self, origin: "Name") -> "Name": - """If the name is a subdomain of *origin*, return a new name which is - the name relative to origin. Otherwise return the name. - - For example, relativizing ``www.dnspython.org.`` to origin - ``dnspython.org.`` returns the name ``www``. Relativizing ``example.`` - to origin ``dnspython.org.`` returns ``example.``. - - Returns a ``dns.name.Name``. - """ - - if origin is not None and self.is_subdomain(origin): - return Name(self[: -len(origin)]) - else: - return self - - def derelativize(self, origin: "Name") -> "Name": - """If the name is a relative name, return a new name which is the - concatenation of the name and origin. Otherwise return the name. - - For example, derelativizing ``www`` to origin ``dnspython.org.`` - returns the name ``www.dnspython.org.``. Derelativizing ``example.`` - to origin ``dnspython.org.`` returns ``example.``. - - Returns a ``dns.name.Name``. - """ - - if not self.is_absolute(): - return self.concatenate(origin) - else: - return self - - def choose_relativity( - self, origin: Optional["Name"] = None, relativize: bool = True - ) -> "Name": - """Return a name with the relativity desired by the caller. - - If *origin* is ``None``, then the name is returned. - Otherwise, if *relativize* is ``True`` the name is - relativized, and if *relativize* is ``False`` the name is - derelativized. - - Returns a ``dns.name.Name``. - """ - - if origin: - if relativize: - return self.relativize(origin) - else: - return self.derelativize(origin) - else: - return self - - def parent(self) -> "Name": - """Return the parent of the name. - - For example, the parent of ``www.dnspython.org.`` is ``dnspython.org``. - - Raises ``dns.name.NoParent`` if the name is either the root name or the - empty name, and thus has no parent. - - Returns a ``dns.name.Name``. - """ - - if self == root or self == empty: - raise NoParent - return Name(self.labels[1:]) - - def predecessor(self, origin: "Name", prefix_ok: bool = True) -> "Name": - """Return the maximal predecessor of *name* in the DNSSEC ordering in the zone - whose origin is *origin*, or return the longest name under *origin* if the - name is origin (i.e. wrap around to the longest name, which may still be - *origin* due to length considerations. - - The relativity of the name is preserved, so if this name is relative - then the method will return a relative name, and likewise if this name - is absolute then the predecessor will be absolute. - - *prefix_ok* indicates if prefixing labels is allowed, and - defaults to ``True``. Normally it is good to allow this, but if computing - a maximal predecessor at a zone cut point then ``False`` must be specified. - """ - return _handle_relativity_and_call( - _absolute_predecessor, self, origin, prefix_ok - ) - - def successor(self, origin: "Name", prefix_ok: bool = True) -> "Name": - """Return the minimal successor of *name* in the DNSSEC ordering in the zone - whose origin is *origin*, or return *origin* if the successor cannot be - computed due to name length limitations. - - Note that *origin* is returned in the "too long" cases because wrapping - around to the origin is how NSEC records express "end of the zone". - - The relativity of the name is preserved, so if this name is relative - then the method will return a relative name, and likewise if this name - is absolute then the successor will be absolute. - - *prefix_ok* indicates if prefixing a new minimal label is allowed, and - defaults to ``True``. Normally it is good to allow this, but if computing - a minimal successor at a zone cut point then ``False`` must be specified. - """ - return _handle_relativity_and_call(_absolute_successor, self, origin, prefix_ok) - - -#: The root name, '.' -root = Name([b""]) - -#: The empty name. -empty = Name([]) - - -def from_unicode( - text: str, origin: Optional[Name] = root, idna_codec: Optional[IDNACodec] = None -) -> Name: - """Convert unicode text into a Name object. - - Labels are encoded in IDN ACE form according to rules specified by - the IDNA codec. - - *text*, a ``str``, is the text to convert into a name. - - *origin*, a ``dns.name.Name``, specifies the origin to - append to non-absolute names. The default is the root name. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Returns a ``dns.name.Name``. - """ - - if not isinstance(text, str): - raise ValueError("input to from_unicode() must be a unicode string") - if not (origin is None or isinstance(origin, Name)): - raise ValueError("origin must be a Name or None") - labels = [] - label = "" - escaping = False - edigits = 0 - total = 0 - if idna_codec is None: - idna_codec = IDNA_2003 - if text == "@": - text = "" - if text: - if text in [".", "\u3002", "\uff0e", "\uff61"]: - return Name([b""]) # no Unicode "u" on this constant! - for c in text: - if escaping: - if edigits == 0: - if c.isdigit(): - total = int(c) - edigits += 1 - else: - label += c - escaping = False - else: - if not c.isdigit(): - raise BadEscape - total *= 10 - total += int(c) - edigits += 1 - if edigits == 3: - escaping = False - label += chr(total) - elif c in [".", "\u3002", "\uff0e", "\uff61"]: - if len(label) == 0: - raise EmptyLabel - labels.append(idna_codec.encode(label)) - label = "" - elif c == "\\": - escaping = True - edigits = 0 - total = 0 - else: - label += c - if escaping: - raise BadEscape - if len(label) > 0: - labels.append(idna_codec.encode(label)) - else: - labels.append(b"") - - if (len(labels) == 0 or labels[-1] != b"") and origin is not None: - labels.extend(list(origin.labels)) - return Name(labels) - - -def is_all_ascii(text: str) -> bool: - for c in text: - if ord(c) > 0x7F: - return False - return True - - -def from_text( - text: Union[bytes, str], - origin: Optional[Name] = root, - idna_codec: Optional[IDNACodec] = None, -) -> Name: - """Convert text into a Name object. - - *text*, a ``bytes`` or ``str``, is the text to convert into a name. - - *origin*, a ``dns.name.Name``, specifies the origin to - append to non-absolute names. The default is the root name. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - Returns a ``dns.name.Name``. - """ - - if isinstance(text, str): - if not is_all_ascii(text): - # Some codepoint in the input text is > 127, so IDNA applies. - return from_unicode(text, origin, idna_codec) - # The input is all ASCII, so treat this like an ordinary non-IDNA - # domain name. Note that "all ASCII" is about the input text, - # not the codepoints in the domain name. E.g. if text has value - # - # r'\150\151\152\153\154\155\156\157\158\159' - # - # then it's still "all ASCII" even though the domain name has - # codepoints > 127. - text = text.encode("ascii") - if not isinstance(text, bytes): - raise ValueError("input to from_text() must be a string") - if not (origin is None or isinstance(origin, Name)): - raise ValueError("origin must be a Name or None") - labels = [] - label = b"" - escaping = False - edigits = 0 - total = 0 - if text == b"@": - text = b"" - if text: - if text == b".": - return Name([b""]) - for c in text: - byte_ = struct.pack("!B", c) - if escaping: - if edigits == 0: - if byte_.isdigit(): - total = int(byte_) - edigits += 1 - else: - label += byte_ - escaping = False - else: - if not byte_.isdigit(): - raise BadEscape - total *= 10 - total += int(byte_) - edigits += 1 - if edigits == 3: - escaping = False - label += struct.pack("!B", total) - elif byte_ == b".": - if len(label) == 0: - raise EmptyLabel - labels.append(label) - label = b"" - elif byte_ == b"\\": - escaping = True - edigits = 0 - total = 0 - else: - label += byte_ - if escaping: - raise BadEscape - if len(label) > 0: - labels.append(label) - else: - labels.append(b"") - if (len(labels) == 0 or labels[-1] != b"") and origin is not None: - labels.extend(list(origin.labels)) - return Name(labels) - - -# we need 'dns.wire.Parser' quoted as dns.name and dns.wire depend on each other. - - -def from_wire_parser(parser: "dns.wire.Parser") -> Name: - """Convert possibly compressed wire format into a Name. - - *parser* is a dns.wire.Parser. - - Raises ``dns.name.BadPointer`` if a compression pointer did not - point backwards in the message. - - Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. - - Returns a ``dns.name.Name`` - """ - - labels = [] - biggest_pointer = parser.current - with parser.restore_furthest(): - count = parser.get_uint8() - while count != 0: - if count < 64: - labels.append(parser.get_bytes(count)) - elif count >= 192: - current = (count & 0x3F) * 256 + parser.get_uint8() - if current >= biggest_pointer: - raise BadPointer - biggest_pointer = current - parser.seek(current) - else: - raise BadLabelType - count = parser.get_uint8() - labels.append(b"") - return Name(labels) - - -def from_wire(message: bytes, current: int) -> Tuple[Name, int]: - """Convert possibly compressed wire format into a Name. - - *message* is a ``bytes`` containing an entire DNS message in DNS - wire form. - - *current*, an ``int``, is the offset of the beginning of the name - from the start of the message - - Raises ``dns.name.BadPointer`` if a compression pointer did not - point backwards in the message. - - Raises ``dns.name.BadLabelType`` if an invalid label type was encountered. - - Returns a ``(dns.name.Name, int)`` tuple consisting of the name - that was read and the number of bytes of the wire format message - which were consumed reading it. - """ - - if not isinstance(message, bytes): - raise ValueError("input to from_wire() must be a byte string") - parser = dns.wire.Parser(message, current) - name = from_wire_parser(parser) - return (name, parser.current - current) - - -# RFC 4471 Support - -_MINIMAL_OCTET = b"\x00" -_MINIMAL_OCTET_VALUE = ord(_MINIMAL_OCTET) -_SUCCESSOR_PREFIX = Name([_MINIMAL_OCTET]) -_MAXIMAL_OCTET = b"\xff" -_MAXIMAL_OCTET_VALUE = ord(_MAXIMAL_OCTET) -_AT_SIGN_VALUE = ord("@") -_LEFT_SQUARE_BRACKET_VALUE = ord("[") - - -def _wire_length(labels): - return functools.reduce(lambda v, x: v + len(x) + 1, labels, 0) - - -def _pad_to_max_name(name): - needed = 255 - _wire_length(name.labels) - new_labels = [] - while needed > 64: - new_labels.append(_MAXIMAL_OCTET * 63) - needed -= 64 - if needed >= 2: - new_labels.append(_MAXIMAL_OCTET * (needed - 1)) - # Note we're already maximal in the needed == 1 case as while we'd like - # to add one more byte as a new label, we can't, as adding a new non-empty - # label requires at least 2 bytes. - new_labels = list(reversed(new_labels)) - new_labels.extend(name.labels) - return Name(new_labels) - - -def _pad_to_max_label(label, suffix_labels): - length = len(label) - # We have to subtract one here to account for the length byte of label. - remaining = 255 - _wire_length(suffix_labels) - length - 1 - if remaining <= 0: - # Shouldn't happen! - return label - needed = min(63 - length, remaining) - return label + _MAXIMAL_OCTET * needed - - -def _absolute_predecessor(name: Name, origin: Name, prefix_ok: bool) -> Name: - # This is the RFC 4471 predecessor algorithm using the "absolute method" of section - # 3.1.1. - # - # Our caller must ensure that the name and origin are absolute, and that name is a - # subdomain of origin. - if name == origin: - return _pad_to_max_name(name) - least_significant_label = name[0] - if least_significant_label == _MINIMAL_OCTET: - return name.parent() - least_octet = least_significant_label[-1] - suffix_labels = name.labels[1:] - if least_octet == _MINIMAL_OCTET_VALUE: - new_labels = [least_significant_label[:-1]] - else: - octets = bytearray(least_significant_label) - octet = octets[-1] - if octet == _LEFT_SQUARE_BRACKET_VALUE: - octet = _AT_SIGN_VALUE - else: - octet -= 1 - octets[-1] = octet - least_significant_label = bytes(octets) - new_labels = [_pad_to_max_label(least_significant_label, suffix_labels)] - new_labels.extend(suffix_labels) - name = Name(new_labels) - if prefix_ok: - return _pad_to_max_name(name) - else: - return name - - -def _absolute_successor(name: Name, origin: Name, prefix_ok: bool) -> Name: - # This is the RFC 4471 successor algorithm using the "absolute method" of section - # 3.1.2. - # - # Our caller must ensure that the name and origin are absolute, and that name is a - # subdomain of origin. - if prefix_ok: - # Try prefixing \000 as new label - try: - return _SUCCESSOR_PREFIX.concatenate(name) - except NameTooLong: - pass - while name != origin: - # Try extending the least significant label. - least_significant_label = name[0] - if len(least_significant_label) < 63: - # We may be able to extend the least label with a minimal additional byte. - # This is only "may" because we could have a maximal length name even though - # the least significant label isn't maximally long. - new_labels = [least_significant_label + _MINIMAL_OCTET] - new_labels.extend(name.labels[1:]) - try: - return dns.name.Name(new_labels) - except dns.name.NameTooLong: - pass - # We can't extend the label either, so we'll try to increment the least - # signficant non-maximal byte in it. - octets = bytearray(least_significant_label) - # We do this reversed iteration with an explicit indexing variable because - # if we find something to increment, we're going to want to truncate everything - # to the right of it. - for i in range(len(octets) - 1, -1, -1): - octet = octets[i] - if octet == _MAXIMAL_OCTET_VALUE: - # We can't increment this, so keep looking. - continue - # Finally, something we can increment. We have to apply a special rule for - # incrementing "@", sending it to "[", because RFC 4034 6.1 says that when - # comparing names, uppercase letters compare as if they were their - # lower-case equivalents. If we increment "@" to "A", then it would compare - # as "a", which is after "[", "\", "]", "^", "_", and "`", so we would have - # skipped the most minimal successor, namely "[". - if octet == _AT_SIGN_VALUE: - octet = _LEFT_SQUARE_BRACKET_VALUE - else: - octet += 1 - octets[i] = octet - # We can now truncate all of the maximal values we skipped (if any) - new_labels = [bytes(octets[: i + 1])] - new_labels.extend(name.labels[1:]) - # We haven't changed the length of the name, so the Name constructor will - # always work. - return Name(new_labels) - # We couldn't increment, so chop off the least significant label and try - # again. - name = name.parent() - - # We couldn't increment at all, so return the origin, as wrapping around is the - # DNSSEC way. - return origin - - -def _handle_relativity_and_call( - function: Callable[[Name, Name, bool], Name], - name: Name, - origin: Name, - prefix_ok: bool, -) -> Name: - # Make "name" absolute if needed, ensure that the origin is absolute, - # call function(), and then relativize the result if needed. - if not origin.is_absolute(): - raise NeedAbsoluteNameOrOrigin - relative = not name.is_absolute() - if relative: - name = name.derelativize(origin) - elif not name.is_subdomain(origin): - raise NeedSubdomainOfOrigin - result_name = function(name, origin, prefix_ok) - if relative: - result_name = result_name.relativize(origin) - return result_name diff --git a/.venv/lib/python3.12/site-packages/dns/namedict.py b/.venv/lib/python3.12/site-packages/dns/namedict.py deleted file mode 100644 index ca8b197..0000000 --- a/.venv/lib/python3.12/site-packages/dns/namedict.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# Copyright (C) 2016 Coresec Systems AB -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND CORESEC SYSTEMS AB DISCLAIMS ALL -# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CORESEC -# SYSTEMS AB BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR -# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS name dictionary""" - -# pylint seems to be confused about this one! -from collections.abc import MutableMapping # pylint: disable=no-name-in-module - -import dns.name - - -class NameDict(MutableMapping): - """A dictionary whose keys are dns.name.Name objects. - - In addition to being like a regular Python dictionary, this - dictionary can also get the deepest match for a given key. - """ - - __slots__ = ["max_depth", "max_depth_items", "__store"] - - def __init__(self, *args, **kwargs): - super().__init__() - self.__store = dict() - #: the maximum depth of the keys that have ever been added - self.max_depth = 0 - #: the number of items of maximum depth - self.max_depth_items = 0 - self.update(dict(*args, **kwargs)) - - def __update_max_depth(self, key): - if len(key) == self.max_depth: - self.max_depth_items = self.max_depth_items + 1 - elif len(key) > self.max_depth: - self.max_depth = len(key) - self.max_depth_items = 1 - - def __getitem__(self, key): - return self.__store[key] - - def __setitem__(self, key, value): - if not isinstance(key, dns.name.Name): - raise ValueError("NameDict key must be a name") - self.__store[key] = value - self.__update_max_depth(key) - - def __delitem__(self, key): - self.__store.pop(key) - if len(key) == self.max_depth: - self.max_depth_items = self.max_depth_items - 1 - if self.max_depth_items == 0: - self.max_depth = 0 - for k in self.__store: - self.__update_max_depth(k) - - def __iter__(self): - return iter(self.__store) - - def __len__(self): - return len(self.__store) - - def has_key(self, key): - return key in self.__store - - def get_deepest_match(self, name): - """Find the deepest match to *name* in the dictionary. - - The deepest match is the longest name in the dictionary which is - a superdomain of *name*. Note that *superdomain* includes matching - *name* itself. - - *name*, a ``dns.name.Name``, the name to find. - - Returns a ``(key, value)`` where *key* is the deepest - ``dns.name.Name``, and *value* is the value associated with *key*. - """ - - depth = len(name) - if depth > self.max_depth: - depth = self.max_depth - for i in range(-depth, 0): - n = dns.name.Name(name[i:]) - if n in self: - return (n, self[n]) - v = self[dns.name.empty] - return (dns.name.empty, v) diff --git a/.venv/lib/python3.12/site-packages/dns/nameserver.py b/.venv/lib/python3.12/site-packages/dns/nameserver.py deleted file mode 100644 index 5dbb4e8..0000000 --- a/.venv/lib/python3.12/site-packages/dns/nameserver.py +++ /dev/null @@ -1,359 +0,0 @@ -from typing import Optional, Union -from urllib.parse import urlparse - -import dns.asyncbackend -import dns.asyncquery -import dns.inet -import dns.message -import dns.query - - -class Nameserver: - def __init__(self): - pass - - def __str__(self): - raise NotImplementedError - - def kind(self) -> str: - raise NotImplementedError - - def is_always_max_size(self) -> bool: - raise NotImplementedError - - def answer_nameserver(self) -> str: - raise NotImplementedError - - def answer_port(self) -> int: - raise NotImplementedError - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - raise NotImplementedError - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - raise NotImplementedError - - -class AddressAndPortNameserver(Nameserver): - def __init__(self, address: str, port: int): - super().__init__() - self.address = address - self.port = port - - def kind(self) -> str: - raise NotImplementedError - - def is_always_max_size(self) -> bool: - return False - - def __str__(self): - ns_kind = self.kind() - return f"{ns_kind}:{self.address}@{self.port}" - - def answer_nameserver(self) -> str: - return self.address - - def answer_port(self) -> int: - return self.port - - -class Do53Nameserver(AddressAndPortNameserver): - def __init__(self, address: str, port: int = 53): - super().__init__(address, port) - - def kind(self): - return "Do53" - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - if max_size: - response = dns.query.tcp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - else: - response = dns.query.udp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - raise_on_truncation=True, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ignore_errors=True, - ignore_unexpected=True, - ) - return response - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - if max_size: - response = await dns.asyncquery.tcp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - backend=backend, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - else: - response = await dns.asyncquery.udp( - request, - self.address, - timeout=timeout, - port=self.port, - source=source, - source_port=source_port, - raise_on_truncation=True, - backend=backend, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ignore_errors=True, - ignore_unexpected=True, - ) - return response - - -class DoHNameserver(Nameserver): - def __init__( - self, - url: str, - bootstrap_address: Optional[str] = None, - verify: Union[bool, str] = True, - want_get: bool = False, - ): - super().__init__() - self.url = url - self.bootstrap_address = bootstrap_address - self.verify = verify - self.want_get = want_get - - def kind(self): - return "DoH" - - def is_always_max_size(self) -> bool: - return True - - def __str__(self): - return self.url - - def answer_nameserver(self) -> str: - return self.url - - def answer_port(self) -> int: - port = urlparse(self.url).port - if port is None: - port = 443 - return port - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return dns.query.https( - request, - self.url, - timeout=timeout, - source=source, - source_port=source_port, - bootstrap_address=self.bootstrap_address, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - post=(not self.want_get), - ) - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return await dns.asyncquery.https( - request, - self.url, - timeout=timeout, - source=source, - source_port=source_port, - bootstrap_address=self.bootstrap_address, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - post=(not self.want_get), - ) - - -class DoTNameserver(AddressAndPortNameserver): - def __init__( - self, - address: str, - port: int = 853, - hostname: Optional[str] = None, - verify: Union[bool, str] = True, - ): - super().__init__(address, port) - self.hostname = hostname - self.verify = verify - - def kind(self): - return "DoT" - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return dns.query.tls( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - server_hostname=self.hostname, - verify=self.verify, - ) - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return await dns.asyncquery.tls( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - server_hostname=self.hostname, - verify=self.verify, - ) - - -class DoQNameserver(AddressAndPortNameserver): - def __init__( - self, - address: str, - port: int = 853, - verify: Union[bool, str] = True, - server_hostname: Optional[str] = None, - ): - super().__init__(address, port) - self.verify = verify - self.server_hostname = server_hostname - - def kind(self): - return "DoQ" - - def query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return dns.query.quic( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - server_hostname=self.server_hostname, - ) - - async def async_query( - self, - request: dns.message.QueryMessage, - timeout: float, - source: Optional[str], - source_port: int, - max_size: bool, - backend: dns.asyncbackend.Backend, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - ) -> dns.message.Message: - return await dns.asyncquery.quic( - request, - self.address, - port=self.port, - timeout=timeout, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - verify=self.verify, - server_hostname=self.server_hostname, - ) diff --git a/.venv/lib/python3.12/site-packages/dns/node.py b/.venv/lib/python3.12/site-packages/dns/node.py deleted file mode 100644 index de85a82..0000000 --- a/.venv/lib/python3.12/site-packages/dns/node.py +++ /dev/null @@ -1,359 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS nodes. A node is a set of rdatasets.""" - -import enum -import io -from typing import Any, Dict, Optional - -import dns.immutable -import dns.name -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.renderer -import dns.rrset - -_cname_types = { - dns.rdatatype.CNAME, -} - -# "neutral" types can coexist with a CNAME and thus are not "other data" -_neutral_types = { - dns.rdatatype.NSEC, # RFC 4035 section 2.5 - dns.rdatatype.NSEC3, # This is not likely to happen, but not impossible! - dns.rdatatype.KEY, # RFC 4035 section 2.5, RFC 3007 -} - - -def _matches_type_or_its_signature(rdtypes, rdtype, covers): - return rdtype in rdtypes or (rdtype == dns.rdatatype.RRSIG and covers in rdtypes) - - -@enum.unique -class NodeKind(enum.Enum): - """Rdatasets in nodes""" - - REGULAR = 0 # a.k.a "other data" - NEUTRAL = 1 - CNAME = 2 - - @classmethod - def classify( - cls, rdtype: dns.rdatatype.RdataType, covers: dns.rdatatype.RdataType - ) -> "NodeKind": - if _matches_type_or_its_signature(_cname_types, rdtype, covers): - return NodeKind.CNAME - elif _matches_type_or_its_signature(_neutral_types, rdtype, covers): - return NodeKind.NEUTRAL - else: - return NodeKind.REGULAR - - @classmethod - def classify_rdataset(cls, rdataset: dns.rdataset.Rdataset) -> "NodeKind": - return cls.classify(rdataset.rdtype, rdataset.covers) - - -class Node: - """A Node is a set of rdatasets. - - A node is either a CNAME node or an "other data" node. A CNAME - node contains only CNAME, KEY, NSEC, and NSEC3 rdatasets along with their - covering RRSIG rdatasets. An "other data" node contains any - rdataset other than a CNAME or RRSIG(CNAME) rdataset. When - changes are made to a node, the CNAME or "other data" state is - always consistent with the update, i.e. the most recent change - wins. For example, if you have a node which contains a CNAME - rdataset, and then add an MX rdataset to it, then the CNAME - rdataset will be deleted. Likewise if you have a node containing - an MX rdataset and add a CNAME rdataset, the MX rdataset will be - deleted. - """ - - __slots__ = ["rdatasets"] - - def __init__(self): - # the set of rdatasets, represented as a list. - self.rdatasets = [] - - def to_text(self, name: dns.name.Name, **kw: Dict[str, Any]) -> str: - """Convert a node to text format. - - Each rdataset at the node is printed. Any keyword arguments - to this method are passed on to the rdataset's to_text() method. - - *name*, a ``dns.name.Name``, the owner name of the - rdatasets. - - Returns a ``str``. - - """ - - s = io.StringIO() - for rds in self.rdatasets: - if len(rds) > 0: - s.write(rds.to_text(name, **kw)) # type: ignore[arg-type] - s.write("\n") - return s.getvalue()[:-1] - - def __repr__(self): - return "" - - def __eq__(self, other): - # - # This is inefficient. Good thing we don't need to do it much. - # - for rd in self.rdatasets: - if rd not in other.rdatasets: - return False - for rd in other.rdatasets: - if rd not in self.rdatasets: - return False - return True - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.rdatasets) - - def __iter__(self): - return iter(self.rdatasets) - - def _append_rdataset(self, rdataset): - """Append rdataset to the node with special handling for CNAME and - other data conditions. - - Specifically, if the rdataset being appended has ``NodeKind.CNAME``, - then all rdatasets other than KEY, NSEC, NSEC3, and their covering - RRSIGs are deleted. If the rdataset being appended has - ``NodeKind.REGULAR`` then CNAME and RRSIG(CNAME) are deleted. - """ - # Make having just one rdataset at the node fast. - if len(self.rdatasets) > 0: - kind = NodeKind.classify_rdataset(rdataset) - if kind == NodeKind.CNAME: - self.rdatasets = [ - rds - for rds in self.rdatasets - if NodeKind.classify_rdataset(rds) != NodeKind.REGULAR - ] - elif kind == NodeKind.REGULAR: - self.rdatasets = [ - rds - for rds in self.rdatasets - if NodeKind.classify_rdataset(rds) != NodeKind.CNAME - ] - # Otherwise the rdataset is NodeKind.NEUTRAL and we do not need to - # edit self.rdatasets. - self.rdatasets.append(rdataset) - - def find_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - """Find an rdataset matching the specified properties in the - current node. - - *rdclass*, a ``dns.rdataclass.RdataClass``, the class of the rdataset. - - *rdtype*, a ``dns.rdatatype.RdataType``, the type of the rdataset. - - *covers*, a ``dns.rdatatype.RdataType``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If True, create the rdataset if it is not found. - - Raises ``KeyError`` if an rdataset of the desired type and class does - not exist and *create* is not ``True``. - - Returns a ``dns.rdataset.Rdataset``. - """ - - for rds in self.rdatasets: - if rds.match(rdclass, rdtype, covers): - return rds - if not create: - raise KeyError - rds = dns.rdataset.Rdataset(rdclass, rdtype, covers) - self._append_rdataset(rds) - return rds - - def get_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - """Get an rdataset matching the specified properties in the - current node. - - None is returned if an rdataset of the specified type and - class does not exist and *create* is not ``True``. - - *rdclass*, an ``int``, the class of the rdataset. - - *rdtype*, an ``int``, the type of the rdataset. - - *covers*, an ``int``, the covered type. Usually this value is - dns.rdatatype.NONE, but if the rdtype is dns.rdatatype.SIG or - dns.rdatatype.RRSIG, then the covers value will be the rdata - type the SIG/RRSIG covers. The library treats the SIG and RRSIG - types as if they were a family of - types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This makes RRSIGs much - easier to work with than if RRSIGs covering different rdata - types were aggregated into a single RRSIG rdataset. - - *create*, a ``bool``. If True, create the rdataset if it is not found. - - Returns a ``dns.rdataset.Rdataset`` or ``None``. - """ - - try: - rds = self.find_rdataset(rdclass, rdtype, covers, create) - except KeyError: - rds = None - return rds - - def delete_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ) -> None: - """Delete the rdataset matching the specified properties in the - current node. - - If a matching rdataset does not exist, it is not an error. - - *rdclass*, an ``int``, the class of the rdataset. - - *rdtype*, an ``int``, the type of the rdataset. - - *covers*, an ``int``, the covered type. - """ - - rds = self.get_rdataset(rdclass, rdtype, covers) - if rds is not None: - self.rdatasets.remove(rds) - - def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: - """Replace an rdataset. - - It is not an error if there is no rdataset matching *replacement*. - - Ownership of the *replacement* object is transferred to the node; - in other words, this method does not store a copy of *replacement* - at the node, it stores *replacement* itself. - - *replacement*, a ``dns.rdataset.Rdataset``. - - Raises ``ValueError`` if *replacement* is not a - ``dns.rdataset.Rdataset``. - """ - - if not isinstance(replacement, dns.rdataset.Rdataset): - raise ValueError("replacement is not an rdataset") - if isinstance(replacement, dns.rrset.RRset): - # RRsets are not good replacements as the match() method - # is not compatible. - replacement = replacement.to_rdataset() - self.delete_rdataset( - replacement.rdclass, replacement.rdtype, replacement.covers - ) - self._append_rdataset(replacement) - - def classify(self) -> NodeKind: - """Classify a node. - - A node which contains a CNAME or RRSIG(CNAME) is a - ``NodeKind.CNAME`` node. - - A node which contains only "neutral" types, i.e. types allowed to - co-exist with a CNAME, is a ``NodeKind.NEUTRAL`` node. The neutral - types are NSEC, NSEC3, KEY, and their associated RRSIGS. An empty node - is also considered neutral. - - A node which contains some rdataset which is not a CNAME, RRSIG(CNAME), - or a neutral type is a a ``NodeKind.REGULAR`` node. Regular nodes are - also commonly referred to as "other data". - """ - for rdataset in self.rdatasets: - kind = NodeKind.classify(rdataset.rdtype, rdataset.covers) - if kind != NodeKind.NEUTRAL: - return kind - return NodeKind.NEUTRAL - - def is_immutable(self) -> bool: - return False - - -@dns.immutable.immutable -class ImmutableNode(Node): - def __init__(self, node): - super().__init__() - self.rdatasets = tuple( - [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] - ) - - def find_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - if create: - raise TypeError("immutable") - return super().find_rdataset(rdclass, rdtype, covers, False) - - def get_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - if create: - raise TypeError("immutable") - return super().get_rdataset(rdclass, rdtype, covers, False) - - def delete_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ) -> None: - raise TypeError("immutable") - - def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: - raise TypeError("immutable") - - def is_immutable(self) -> bool: - return True diff --git a/.venv/lib/python3.12/site-packages/dns/opcode.py b/.venv/lib/python3.12/site-packages/dns/opcode.py deleted file mode 100644 index 78b43d2..0000000 --- a/.venv/lib/python3.12/site-packages/dns/opcode.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Opcodes.""" - -import dns.enum -import dns.exception - - -class Opcode(dns.enum.IntEnum): - #: Query - QUERY = 0 - #: Inverse Query (historical) - IQUERY = 1 - #: Server Status (unspecified and unimplemented anywhere) - STATUS = 2 - #: Notify - NOTIFY = 4 - #: Dynamic Update - UPDATE = 5 - - @classmethod - def _maximum(cls): - return 15 - - @classmethod - def _unknown_exception_class(cls): - return UnknownOpcode - - -class UnknownOpcode(dns.exception.DNSException): - """An DNS opcode is unknown.""" - - -def from_text(text: str) -> Opcode: - """Convert text into an opcode. - - *text*, a ``str``, the textual opcode - - Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. - - Returns an ``int``. - """ - - return Opcode.from_text(text) - - -def from_flags(flags: int) -> Opcode: - """Extract an opcode from DNS message flags. - - *flags*, an ``int``, the DNS flags. - - Returns an ``int``. - """ - - return Opcode((flags & 0x7800) >> 11) - - -def to_flags(value: Opcode) -> int: - """Convert an opcode to a value suitable for ORing into DNS message - flags. - - *value*, an ``int``, the DNS opcode value. - - Returns an ``int``. - """ - - return (value << 11) & 0x7800 - - -def to_text(value: Opcode) -> str: - """Convert an opcode to text. - - *value*, an ``int`` the opcode value, - - Raises ``dns.opcode.UnknownOpcode`` if the opcode is unknown. - - Returns a ``str``. - """ - - return Opcode.to_text(value) - - -def is_update(flags: int) -> bool: - """Is the opcode in flags UPDATE? - - *flags*, an ``int``, the DNS message flags. - - Returns a ``bool``. - """ - - return from_flags(flags) == Opcode.UPDATE - - -### BEGIN generated Opcode constants - -QUERY = Opcode.QUERY -IQUERY = Opcode.IQUERY -STATUS = Opcode.STATUS -NOTIFY = Opcode.NOTIFY -UPDATE = Opcode.UPDATE - -### END generated Opcode constants diff --git a/.venv/lib/python3.12/site-packages/dns/py.typed b/.venv/lib/python3.12/site-packages/dns/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/dns/query.py b/.venv/lib/python3.12/site-packages/dns/query.py deleted file mode 100644 index f0ee916..0000000 --- a/.venv/lib/python3.12/site-packages/dns/query.py +++ /dev/null @@ -1,1578 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Talk to a DNS server.""" - -import base64 -import contextlib -import enum -import errno -import os -import os.path -import selectors -import socket -import struct -import time -from typing import Any, Dict, Optional, Tuple, Union - -import dns._features -import dns.exception -import dns.inet -import dns.message -import dns.name -import dns.quic -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.serial -import dns.transaction -import dns.tsig -import dns.xfr - - -def _remaining(expiration): - if expiration is None: - return None - timeout = expiration - time.time() - if timeout <= 0.0: - raise dns.exception.Timeout - return timeout - - -def _expiration_for_this_attempt(timeout, expiration): - if expiration is None: - return None - return min(time.time() + timeout, expiration) - - -_have_httpx = dns._features.have("doh") -if _have_httpx: - import httpcore._backends.sync - import httpx - - _CoreNetworkBackend = httpcore.NetworkBackend - _CoreSyncStream = httpcore._backends.sync.SyncStream - - class _NetworkBackend(_CoreNetworkBackend): - def __init__(self, resolver, local_port, bootstrap_address, family): - super().__init__() - self._local_port = local_port - self._resolver = resolver - self._bootstrap_address = bootstrap_address - self._family = family - - def connect_tcp( - self, host, port, timeout, local_address, socket_options=None - ): # pylint: disable=signature-differs - addresses = [] - _, expiration = _compute_times(timeout) - if dns.inet.is_address(host): - addresses.append(host) - elif self._bootstrap_address is not None: - addresses.append(self._bootstrap_address) - else: - timeout = _remaining(expiration) - family = self._family - if local_address: - family = dns.inet.af_for_address(local_address) - answers = self._resolver.resolve_name( - host, family=family, lifetime=timeout - ) - addresses = answers.addresses() - for address in addresses: - af = dns.inet.af_for_address(address) - if local_address is not None or self._local_port != 0: - source = dns.inet.low_level_address_tuple( - (local_address, self._local_port), af - ) - else: - source = None - sock = _make_socket(af, socket.SOCK_STREAM, source) - attempt_expiration = _expiration_for_this_attempt(2.0, expiration) - try: - _connect( - sock, - dns.inet.low_level_address_tuple((address, port), af), - attempt_expiration, - ) - return _CoreSyncStream(sock) - except Exception: - pass - raise httpcore.ConnectError - - def connect_unix_socket( - self, path, timeout, socket_options=None - ): # pylint: disable=signature-differs - raise NotImplementedError - - class _HTTPTransport(httpx.HTTPTransport): - def __init__( - self, - *args, - local_port=0, - bootstrap_address=None, - resolver=None, - family=socket.AF_UNSPEC, - **kwargs, - ): - if resolver is None: - # pylint: disable=import-outside-toplevel,redefined-outer-name - import dns.resolver - - resolver = dns.resolver.Resolver() - super().__init__(*args, **kwargs) - self._pool._network_backend = _NetworkBackend( - resolver, local_port, bootstrap_address, family - ) - -else: - - class _HTTPTransport: # type: ignore - def connect_tcp(self, host, port, timeout, local_address): - raise NotImplementedError - - -have_doh = _have_httpx - -try: - import ssl -except ImportError: # pragma: no cover - - class ssl: # type: ignore - CERT_NONE = 0 - - class WantReadException(Exception): - pass - - class WantWriteException(Exception): - pass - - class SSLContext: - pass - - class SSLSocket: - pass - - @classmethod - def create_default_context(cls, *args, **kwargs): - raise Exception("no ssl support") # pylint: disable=broad-exception-raised - - -# Function used to create a socket. Can be overridden if needed in special -# situations. -socket_factory = socket.socket - - -class UnexpectedSource(dns.exception.DNSException): - """A DNS query response came from an unexpected address or port.""" - - -class BadResponse(dns.exception.FormError): - """A DNS query response does not respond to the question asked.""" - - -class NoDOH(dns.exception.DNSException): - """DNS over HTTPS (DOH) was requested but the httpx module is not - available.""" - - -class NoDOQ(dns.exception.DNSException): - """DNS over QUIC (DOQ) was requested but the aioquic module is not - available.""" - - -# for backwards compatibility -TransferError = dns.xfr.TransferError - - -def _compute_times(timeout): - now = time.time() - if timeout is None: - return (now, None) - else: - return (now, now + timeout) - - -def _wait_for(fd, readable, writable, _, expiration): - # Use the selected selector class to wait for any of the specified - # events. An "expiration" absolute time is converted into a relative - # timeout. - # - # The unused parameter is 'error', which is always set when - # selecting for read or write, and we have no error-only selects. - - if readable and isinstance(fd, ssl.SSLSocket) and fd.pending() > 0: - return True - sel = _selector_class() - events = 0 - if readable: - events |= selectors.EVENT_READ - if writable: - events |= selectors.EVENT_WRITE - if events: - sel.register(fd, events) - if expiration is None: - timeout = None - else: - timeout = expiration - time.time() - if timeout <= 0.0: - raise dns.exception.Timeout - if not sel.select(timeout): - raise dns.exception.Timeout - - -def _set_selector_class(selector_class): - # Internal API. Do not use. - - global _selector_class - - _selector_class = selector_class - - -if hasattr(selectors, "PollSelector"): - # Prefer poll() on platforms that support it because it has no - # limits on the maximum value of a file descriptor (plus it will - # be more efficient for high values). - # - # We ignore typing here as we can't say _selector_class is Any - # on python < 3.8 due to a bug. - _selector_class = selectors.PollSelector # type: ignore -else: - _selector_class = selectors.SelectSelector # type: ignore - - -def _wait_for_readable(s, expiration): - _wait_for(s, True, False, True, expiration) - - -def _wait_for_writable(s, expiration): - _wait_for(s, False, True, True, expiration) - - -def _addresses_equal(af, a1, a2): - # Convert the first value of the tuple, which is a textual format - # address into binary form, so that we are not confused by different - # textual representations of the same address - try: - n1 = dns.inet.inet_pton(af, a1[0]) - n2 = dns.inet.inet_pton(af, a2[0]) - except dns.exception.SyntaxError: - return False - return n1 == n2 and a1[1:] == a2[1:] - - -def _matches_destination(af, from_address, destination, ignore_unexpected): - # Check that from_address is appropriate for a response to a query - # sent to destination. - if not destination: - return True - if _addresses_equal(af, from_address, destination) or ( - dns.inet.is_multicast(destination[0]) and from_address[1:] == destination[1:] - ): - return True - elif ignore_unexpected: - return False - raise UnexpectedSource( - f"got a response from {from_address} instead of " f"{destination}" - ) - - -def _destination_and_source( - where, port, source, source_port, where_must_be_address=True -): - # Apply defaults and compute destination and source tuples - # suitable for use in connect(), sendto(), or bind(). - af = None - destination = None - try: - af = dns.inet.af_for_address(where) - destination = where - except Exception: - if where_must_be_address: - raise - # URLs are ok so eat the exception - if source: - saf = dns.inet.af_for_address(source) - if af: - # We know the destination af, so source had better agree! - if saf != af: - raise ValueError( - "different address families for source and destination" - ) - else: - # We didn't know the destination af, but we know the source, - # so that's our af. - af = saf - if source_port and not source: - # Caller has specified a source_port but not an address, so we - # need to return a source, and we need to use the appropriate - # wildcard address as the address. - try: - source = dns.inet.any_for_af(af) - except Exception: - # we catch this and raise ValueError for backwards compatibility - raise ValueError("source_port specified but address family is unknown") - # Convert high-level (address, port) tuples into low-level address - # tuples. - if destination: - destination = dns.inet.low_level_address_tuple((destination, port), af) - if source: - source = dns.inet.low_level_address_tuple((source, source_port), af) - return (af, destination, source) - - -def _make_socket(af, type, source, ssl_context=None, server_hostname=None): - s = socket_factory(af, type) - try: - s.setblocking(False) - if source is not None: - s.bind(source) - if ssl_context: - # LGTM gets a false positive here, as our default context is OK - return ssl_context.wrap_socket( - s, - do_handshake_on_connect=False, # lgtm[py/insecure-protocol] - server_hostname=server_hostname, - ) - else: - return s - except Exception: - s.close() - raise - - -def https( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 443, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - session: Optional[Any] = None, - path: str = "/dns-query", - post: bool = True, - bootstrap_address: Optional[str] = None, - verify: Union[bool, str] = True, - resolver: Optional["dns.resolver.Resolver"] = None, - family: Optional[int] = socket.AF_UNSPEC, -) -> dns.message.Message: - """Return the response obtained after sending a query via DNS-over-HTTPS. - - *q*, a ``dns.message.Message``, the query to send. - - *where*, a ``str``, the nameserver IP address or the full URL. If an IP address is - given, the URL will be constructed using the following schema: - https://:/. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query - times out. If ``None``, the default, wait forever. - - *port*, a ``int``, the port to send the query to. The default is 443. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source - address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. The default is - 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - received message. - - *session*, an ``httpx.Client``. If provided, the client session to use to send the - queries. - - *path*, a ``str``. If *where* is an IP address, then *path* will be used to - construct the URL to send the DNS query to. - - *post*, a ``bool``. If ``True``, the default, POST method will be used. - - *bootstrap_address*, a ``str``, the IP address to use to bypass resolution. - - *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification - of the server is done using the default CA bundle; if ``False``, then no - verification is done; if a `str` then it specifies the path to a certificate file or - directory which will be used for verification. - - *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for - resolution of hostnames in URLs. If not specified, a new resolver with a default - configuration will be used; note this is *not* the default resolver as that resolver - might have been configured to use DoH causing a chicken-and-egg problem. This - parameter only has an effect if the HTTP library is httpx. - - *family*, an ``int``, the address family. If socket.AF_UNSPEC (the default), both A - and AAAA records will be retrieved. - - Returns a ``dns.message.Message``. - """ - - if not have_doh: - raise NoDOH # pragma: no cover - if session and not isinstance(session, httpx.Client): - raise ValueError("session parameter must be an httpx.Client") - - wire = q.to_wire() - (af, _, the_source) = _destination_and_source( - where, port, source, source_port, False - ) - transport = None - headers = {"accept": "application/dns-message"} - if af is not None and dns.inet.is_address(where): - if af == socket.AF_INET: - url = "https://{}:{}{}".format(where, port, path) - elif af == socket.AF_INET6: - url = "https://[{}]:{}{}".format(where, port, path) - else: - url = where - - # set source port and source address - - if the_source is None: - local_address = None - local_port = 0 - else: - local_address = the_source[0] - local_port = the_source[1] - transport = _HTTPTransport( - local_address=local_address, - http1=True, - http2=True, - verify=verify, - local_port=local_port, - bootstrap_address=bootstrap_address, - resolver=resolver, - family=family, - ) - - if session: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(session) - else: - cm = httpx.Client(http1=True, http2=True, verify=verify, transport=transport) - with cm as session: - # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH - # GET and POST examples - if post: - headers.update( - { - "content-type": "application/dns-message", - "content-length": str(len(wire)), - } - ) - response = session.post(url, headers=headers, content=wire, timeout=timeout) - else: - wire = base64.urlsafe_b64encode(wire).rstrip(b"=") - twire = wire.decode() # httpx does a repr() if we give it bytes - response = session.get( - url, headers=headers, timeout=timeout, params={"dns": twire} - ) - - # see https://tools.ietf.org/html/rfc8484#section-4.2.1 for info about DoH - # status codes - if response.status_code < 200 or response.status_code > 299: - raise ValueError( - "{} responded with status code {}" - "\nResponse body: {}".format(where, response.status_code, response.content) - ) - r = dns.message.from_wire( - response.content, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = response.elapsed.total_seconds() - if not q.is_response(r): - raise BadResponse - return r - - -def _udp_recv(sock, max_size, expiration): - """Reads a datagram from the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - while True: - try: - return sock.recvfrom(max_size) - except BlockingIOError: - _wait_for_readable(sock, expiration) - - -def _udp_send(sock, data, destination, expiration): - """Sends the specified datagram to destination over the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - while True: - try: - if destination: - return sock.sendto(data, destination) - else: - return sock.send(data) - except BlockingIOError: # pragma: no cover - _wait_for_writable(sock, expiration) - - -def send_udp( - sock: Any, - what: Union[dns.message.Message, bytes], - destination: Any, - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified UDP socket. - - *sock*, a ``socket``. - - *what*, a ``bytes`` or ``dns.message.Message``, the message to send. - - *destination*, a destination tuple appropriate for the address family - of the socket, specifying where to send the query. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - Returns an ``(int, float)`` tuple of bytes sent and the sent time. - """ - - if isinstance(what, dns.message.Message): - what = what.to_wire() - sent_time = time.time() - n = _udp_send(sock, what, destination, expiration) - return (n, sent_time) - - -def receive_udp( - sock: Any, - destination: Optional[Any] = None, - expiration: Optional[float] = None, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - ignore_errors: bool = False, - query: Optional[dns.message.Message] = None, -) -> Any: - """Read a DNS message from a UDP socket. - - *sock*, a ``socket``. - - *destination*, a destination tuple appropriate for the address family - of the socket, specifying where the message is expected to arrive from. - When receiving a response, this would be where the associated query was - sent. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from - unexpected sources. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *keyring*, a ``dict``, the keyring to use for TSIG. - - *request_mac*, a ``bytes`` or ``None``, the MAC of the request (for TSIG). - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if - the TC bit is set. - - Raises if the message is malformed, if network errors occur, of if - there is a timeout. - - If *destination* is not ``None``, returns a ``(dns.message.Message, float)`` - tuple of the received message and the received time. - - If *destination* is ``None``, returns a - ``(dns.message.Message, float, tuple)`` - tuple of the received message, the received time, and the address where - the message arrived from. - - *ignore_errors*, a ``bool``. If various format errors or response - mismatches occur, ignore them and keep listening for a valid response. - The default is ``False``. - - *query*, a ``dns.message.Message`` or ``None``. If not ``None`` and - *ignore_errors* is ``True``, check that the received message is a response - to this query, and if not keep listening for a valid response. - """ - - wire = b"" - while True: - (wire, from_address) = _udp_recv(sock, 65535, expiration) - if not _matches_destination( - sock.family, from_address, destination, ignore_unexpected - ): - continue - received_time = time.time() - try: - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - raise_on_truncation=raise_on_truncation, - ) - except dns.message.Truncated as e: - # If we got Truncated and not FORMERR, we at least got the header with TC - # set, and very likely the question section, so we'll re-raise if the - # message seems to be a response as we need to know when truncation happens. - # We need to check that it seems to be a response as we don't want a random - # injected message with TC set to cause us to bail out. - if ( - ignore_errors - and query is not None - and not query.is_response(e.message()) - ): - continue - else: - raise - except Exception: - if ignore_errors: - continue - else: - raise - if ignore_errors and query is not None and not query.is_response(r): - continue - if destination: - return (r, received_time) - else: - return (r, received_time, from_address) - - -def udp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - raise_on_truncation: bool = False, - sock: Optional[Any] = None, - ignore_errors: bool = False, -) -> dns.message.Message: - """Return the response obtained after sending a query via UDP. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the - query times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from - unexpected sources. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *raise_on_truncation*, a ``bool``. If ``True``, raise an exception if - the TC bit is set. - - *sock*, a ``socket.socket``, or ``None``, the socket to use for the - query. If ``None``, the default, a socket is created. Note that - if a socket is provided, it must be a nonblocking datagram socket, - and the *source* and *source_port* are ignored. - - *ignore_errors*, a ``bool``. If various format errors or response - mismatches occur, ignore them and keep listening for a valid response. - The default is ``False``. - - Returns a ``dns.message.Message``. - """ - - wire = q.to_wire() - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - (begin_time, expiration) = _compute_times(timeout) - if sock: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) - else: - cm = _make_socket(af, socket.SOCK_DGRAM, source) - with cm as s: - send_udp(s, wire, destination, expiration) - (r, received_time) = receive_udp( - s, - destination, - expiration, - ignore_unexpected, - one_rr_per_rrset, - q.keyring, - q.mac, - ignore_trailing, - raise_on_truncation, - ignore_errors, - q, - ) - r.time = received_time - begin_time - # We don't need to check q.is_response() if we are in ignore_errors mode - # as receive_udp() will have checked it. - if not (ignore_errors or q.is_response(r)): - raise BadResponse - return r - assert ( - False # help mypy figure out we can't get here lgtm[py/unreachable-statement] - ) - - -def udp_with_fallback( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - ignore_unexpected: bool = False, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - udp_sock: Optional[Any] = None, - tcp_sock: Optional[Any] = None, - ignore_errors: bool = False, -) -> Tuple[dns.message.Message, bool]: - """Return the response to the query, trying UDP first and falling back - to TCP if UDP results in a truncated response. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query - times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source - address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. The default is - 0. - - *ignore_unexpected*, a ``bool``. If ``True``, ignore responses from unexpected - sources. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - received message. - - *udp_sock*, a ``socket.socket``, or ``None``, the socket to use for the UDP query. - If ``None``, the default, a socket is created. Note that if a socket is provided, - it must be a nonblocking datagram socket, and the *source* and *source_port* are - ignored for the UDP query. - - *tcp_sock*, a ``socket.socket``, or ``None``, the connected socket to use for the - TCP query. If ``None``, the default, a socket is created. Note that if a socket is - provided, it must be a nonblocking connected stream socket, and *where*, *source* - and *source_port* are ignored for the TCP query. - - *ignore_errors*, a ``bool``. If various format errors or response mismatches occur - while listening for UDP, ignore them and keep listening for a valid response. The - default is ``False``. - - Returns a (``dns.message.Message``, tcp) tuple where tcp is ``True`` if and only if - TCP was used. - """ - try: - response = udp( - q, - where, - timeout, - port, - source, - source_port, - ignore_unexpected, - one_rr_per_rrset, - ignore_trailing, - True, - udp_sock, - ignore_errors, - ) - return (response, False) - except dns.message.Truncated: - response = tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - tcp_sock, - ) - return (response, True) - - -def _net_read(sock, count, expiration): - """Read the specified number of bytes from sock. Keep trying until we - either get the desired amount, or we hit EOF. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - s = b"" - while count > 0: - try: - n = sock.recv(count) - if n == b"": - raise EOFError - count -= len(n) - s += n - except (BlockingIOError, ssl.SSLWantReadError): - _wait_for_readable(sock, expiration) - except ssl.SSLWantWriteError: # pragma: no cover - _wait_for_writable(sock, expiration) - return s - - -def _net_write(sock, data, expiration): - """Write the specified data to the socket. - A Timeout exception will be raised if the operation is not completed - by the expiration time. - """ - current = 0 - l = len(data) - while current < l: - try: - current += sock.send(data[current:]) - except (BlockingIOError, ssl.SSLWantWriteError): - _wait_for_writable(sock, expiration) - except ssl.SSLWantReadError: # pragma: no cover - _wait_for_readable(sock, expiration) - - -def send_tcp( - sock: Any, - what: Union[dns.message.Message, bytes], - expiration: Optional[float] = None, -) -> Tuple[int, float]: - """Send a DNS message to the specified TCP socket. - - *sock*, a ``socket``. - - *what*, a ``bytes`` or ``dns.message.Message``, the message to send. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - Returns an ``(int, float)`` tuple of bytes sent and the sent time. - """ - - if isinstance(what, dns.message.Message): - tcpmsg = what.to_wire(prepend_length=True) - else: - # copying the wire into tcpmsg is inefficient, but lets us - # avoid writev() or doing a short write that would get pushed - # onto the net - tcpmsg = len(what).to_bytes(2, "big") + what - sent_time = time.time() - _net_write(sock, tcpmsg, expiration) - return (len(tcpmsg), sent_time) - - -def receive_tcp( - sock: Any, - expiration: Optional[float] = None, - one_rr_per_rrset: bool = False, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - request_mac: Optional[bytes] = b"", - ignore_trailing: bool = False, -) -> Tuple[dns.message.Message, float]: - """Read a DNS message from a TCP socket. - - *sock*, a ``socket``. - - *expiration*, a ``float`` or ``None``, the absolute time at which - a timeout exception should be raised. If ``None``, no timeout will - occur. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *keyring*, a ``dict``, the keyring to use for TSIG. - - *request_mac*, a ``bytes`` or ``None``, the MAC of the request (for TSIG). - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - Raises if the message is malformed, if network errors occur, of if - there is a timeout. - - Returns a ``(dns.message.Message, float)`` tuple of the received message - and the received time. - """ - - ldata = _net_read(sock, 2, expiration) - (l,) = struct.unpack("!H", ldata) - wire = _net_read(sock, l, expiration) - received_time = time.time() - r = dns.message.from_wire( - wire, - keyring=keyring, - request_mac=request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - return (r, received_time) - - -def _connect(s, address, expiration): - err = s.connect_ex(address) - if err == 0: - return - if err in (errno.EINPROGRESS, errno.EWOULDBLOCK, errno.EALREADY): - _wait_for_writable(s, expiration) - err = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) - if err != 0: - raise OSError(err, os.strerror(err)) - - -def tcp( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 53, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[Any] = None, -) -> dns.message.Message: - """Return the response obtained after sending a query via TCP. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the - query times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *sock*, a ``socket.socket``, or ``None``, the connected socket to use for the - query. If ``None``, the default, a socket is created. Note that - if a socket is provided, it must be a nonblocking connected stream - socket, and *where*, *port*, *source* and *source_port* are ignored. - - Returns a ``dns.message.Message``. - """ - - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - if sock: - cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) - else: - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - cm = _make_socket(af, socket.SOCK_STREAM, source) - with cm as s: - if not sock: - _connect(s, destination, expiration) - send_tcp(s, wire, expiration) - (r, received_time) = receive_tcp( - s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing - ) - r.time = received_time - begin_time - if not q.is_response(r): - raise BadResponse - return r - assert ( - False # help mypy figure out we can't get here lgtm[py/unreachable-statement] - ) - - -def _tls_handshake(s, expiration): - while True: - try: - s.do_handshake() - return - except ssl.SSLWantReadError: - _wait_for_readable(s, expiration) - except ssl.SSLWantWriteError: # pragma: no cover - _wait_for_writable(s, expiration) - - -def _make_dot_ssl_context( - server_hostname: Optional[str], verify: Union[bool, str] -) -> ssl.SSLContext: - cafile: Optional[str] = None - capath: Optional[str] = None - if isinstance(verify, str): - if os.path.isfile(verify): - cafile = verify - elif os.path.isdir(verify): - capath = verify - else: - raise ValueError("invalid verify string") - ssl_context = ssl.create_default_context(cafile=cafile, capath=capath) - ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 - if server_hostname is None: - ssl_context.check_hostname = False - ssl_context.set_alpn_protocols(["dot"]) - if verify is False: - ssl_context.verify_mode = ssl.CERT_NONE - return ssl_context - - -def tls( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - sock: Optional[ssl.SSLSocket] = None, - ssl_context: Optional[ssl.SSLContext] = None, - server_hostname: Optional[str] = None, - verify: Union[bool, str] = True, -) -> dns.message.Message: - """Return the response obtained after sending a query via TLS. - - *q*, a ``dns.message.Message``, the query to send - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the - query times out. If ``None``, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 853. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own - RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing - junk at end of the received message. - - *sock*, an ``ssl.SSLSocket``, or ``None``, the socket to use for - the query. If ``None``, the default, a socket is created. Note - that if a socket is provided, it must be a nonblocking connected - SSL stream socket, and *where*, *port*, *source*, *source_port*, - and *ssl_context* are ignored. - - *ssl_context*, an ``ssl.SSLContext``, the context to use when establishing - a TLS connection. If ``None``, the default, creates one with the default - configuration. - - *server_hostname*, a ``str`` containing the server's hostname. The - default is ``None``, which means that no hostname is known, and if an - SSL context is created, hostname checking will be disabled. - - *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification - of the server is done using the default CA bundle; if ``False``, then no - verification is done; if a `str` then it specifies the path to a certificate file or - directory which will be used for verification. - - Returns a ``dns.message.Message``. - - """ - - if sock: - # - # If a socket was provided, there's no special TLS handling needed. - # - return tcp( - q, - where, - timeout, - port, - source, - source_port, - one_rr_per_rrset, - ignore_trailing, - sock, - ) - - wire = q.to_wire() - (begin_time, expiration) = _compute_times(timeout) - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - if ssl_context is None and not sock: - ssl_context = _make_dot_ssl_context(server_hostname, verify) - - with _make_socket( - af, - socket.SOCK_STREAM, - source, - ssl_context=ssl_context, - server_hostname=server_hostname, - ) as s: - _connect(s, destination, expiration) - _tls_handshake(s, expiration) - send_tcp(s, wire, expiration) - (r, received_time) = receive_tcp( - s, expiration, one_rr_per_rrset, q.keyring, q.mac, ignore_trailing - ) - r.time = received_time - begin_time - if not q.is_response(r): - raise BadResponse - return r - assert ( - False # help mypy figure out we can't get here lgtm[py/unreachable-statement] - ) - - -def quic( - q: dns.message.Message, - where: str, - timeout: Optional[float] = None, - port: int = 853, - source: Optional[str] = None, - source_port: int = 0, - one_rr_per_rrset: bool = False, - ignore_trailing: bool = False, - connection: Optional[dns.quic.SyncQuicConnection] = None, - verify: Union[bool, str] = True, - server_hostname: Optional[str] = None, -) -> dns.message.Message: - """Return the response obtained after sending a query via DNS-over-QUIC. - - *q*, a ``dns.message.Message``, the query to send. - - *where*, a ``str``, the nameserver IP address. - - *timeout*, a ``float`` or ``None``, the number of seconds to wait before the query - times out. If ``None``, the default, wait forever. - - *port*, a ``int``, the port to send the query to. The default is 853. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying the source - address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. The default is - 0. - - *one_rr_per_rrset*, a ``bool``. If ``True``, put each RR into its own RRset. - - *ignore_trailing*, a ``bool``. If ``True``, ignore trailing junk at end of the - received message. - - *connection*, a ``dns.quic.SyncQuicConnection``. If provided, the - connection to use to send the query. - - *verify*, a ``bool`` or ``str``. If a ``True``, then TLS certificate verification - of the server is done using the default CA bundle; if ``False``, then no - verification is done; if a `str` then it specifies the path to a certificate file or - directory which will be used for verification. - - *server_hostname*, a ``str`` containing the server's hostname. The - default is ``None``, which means that no hostname is known, and if an - SSL context is created, hostname checking will be disabled. - - Returns a ``dns.message.Message``. - """ - - if not dns.quic.have_quic: - raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover - - q.id = 0 - wire = q.to_wire() - the_connection: dns.quic.SyncQuicConnection - the_manager: dns.quic.SyncQuicManager - if connection: - manager: contextlib.AbstractContextManager = contextlib.nullcontext(None) - the_connection = connection - else: - manager = dns.quic.SyncQuicManager( - verify_mode=verify, server_name=server_hostname - ) - the_manager = manager # for type checking happiness - - with manager: - if not connection: - the_connection = the_manager.connect(where, port, source, source_port) - (start, expiration) = _compute_times(timeout) - with the_connection.make_stream(timeout) as stream: - stream.send(wire, True) - wire = stream.receive(_remaining(expiration)) - finish = time.time() - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.request_mac, - one_rr_per_rrset=one_rr_per_rrset, - ignore_trailing=ignore_trailing, - ) - r.time = max(finish - start, 0.0) - if not q.is_response(r): - raise BadResponse - return r - - -def xfr( - where: str, - zone: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.AXFR, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - timeout: Optional[float] = None, - port: int = 53, - keyring: Optional[Dict[dns.name.Name, dns.tsig.Key]] = None, - keyname: Optional[Union[dns.name.Name, str]] = None, - relativize: bool = True, - lifetime: Optional[float] = None, - source: Optional[str] = None, - source_port: int = 0, - serial: int = 0, - use_udp: bool = False, - keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, -) -> Any: - """Return a generator for the responses to a zone transfer. - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *zone*, a ``dns.name.Name`` or ``str``, the name of the zone to transfer. - - *rdtype*, an ``int`` or ``str``, the type of zone transfer. The - default is ``dns.rdatatype.AXFR``. ``dns.rdatatype.IXFR`` can be - used to do an incremental transfer instead. - - *rdclass*, an ``int`` or ``str``, the class of the zone transfer. - The default is ``dns.rdataclass.IN``. - - *timeout*, a ``float``, the number of seconds to wait for each - response message. If None, the default, wait forever. - - *port*, an ``int``, the port send the message to. The default is 53. - - *keyring*, a ``dict``, the keyring to use for TSIG. - - *keyname*, a ``dns.name.Name`` or ``str``, the name of the TSIG - key to use. - - *relativize*, a ``bool``. If ``True``, all names in the zone will be - relativized to the zone origin. It is essential that the - relativize setting matches the one specified to - ``dns.zone.from_xfr()`` if using this generator to make a zone. - - *lifetime*, a ``float``, the total number of seconds to spend - doing the transfer. If ``None``, the default, then there is no - limit on the time the transfer may take. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *serial*, an ``int``, the SOA serial number to use as the base for - an IXFR diff sequence (only meaningful if *rdtype* is - ``dns.rdatatype.IXFR``). - - *use_udp*, a ``bool``. If ``True``, use UDP (only meaningful for IXFR). - - *keyalgorithm*, a ``dns.name.Name`` or ``str``, the TSIG algorithm to use. - - Raises on errors, and so does the generator. - - Returns a generator of ``dns.message.Message`` objects. - """ - - if isinstance(zone, str): - zone = dns.name.from_text(zone) - rdtype = dns.rdatatype.RdataType.make(rdtype) - q = dns.message.make_query(zone, rdtype, rdclass) - if rdtype == dns.rdatatype.IXFR: - rrset = dns.rrset.from_text(zone, 0, "IN", "SOA", ". . %u 0 0 0 0" % serial) - q.authority.append(rrset) - if keyring is not None: - q.use_tsig(keyring, keyname, algorithm=keyalgorithm) - wire = q.to_wire() - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - if use_udp and rdtype != dns.rdatatype.IXFR: - raise ValueError("cannot do a UDP AXFR") - sock_type = socket.SOCK_DGRAM if use_udp else socket.SOCK_STREAM - with _make_socket(af, sock_type, source) as s: - (_, expiration) = _compute_times(lifetime) - _connect(s, destination, expiration) - l = len(wire) - if use_udp: - _udp_send(s, wire, None, expiration) - else: - tcpmsg = struct.pack("!H", l) + wire - _net_write(s, tcpmsg, expiration) - done = False - delete_mode = True - expecting_SOA = False - soa_rrset = None - if relativize: - origin = zone - oname = dns.name.empty - else: - origin = None - oname = zone - tsig_ctx = None - while not done: - (_, mexpiration) = _compute_times(timeout) - if mexpiration is None or ( - expiration is not None and mexpiration > expiration - ): - mexpiration = expiration - if use_udp: - (wire, _) = _udp_recv(s, 65535, mexpiration) - else: - ldata = _net_read(s, 2, mexpiration) - (l,) = struct.unpack("!H", ldata) - wire = _net_read(s, l, mexpiration) - is_ixfr = rdtype == dns.rdatatype.IXFR - r = dns.message.from_wire( - wire, - keyring=q.keyring, - request_mac=q.mac, - xfr=True, - origin=origin, - tsig_ctx=tsig_ctx, - multi=True, - one_rr_per_rrset=is_ixfr, - ) - rcode = r.rcode() - if rcode != dns.rcode.NOERROR: - raise TransferError(rcode) - tsig_ctx = r.tsig_ctx - answer_index = 0 - if soa_rrset is None: - if not r.answer or r.answer[0].name != oname: - raise dns.exception.FormError("No answer or RRset not for qname") - rrset = r.answer[0] - if rrset.rdtype != dns.rdatatype.SOA: - raise dns.exception.FormError("first RRset is not an SOA") - answer_index = 1 - soa_rrset = rrset.copy() - if rdtype == dns.rdatatype.IXFR: - if dns.serial.Serial(soa_rrset[0].serial) <= serial: - # - # We're already up-to-date. - # - done = True - else: - expecting_SOA = True - # - # Process SOAs in the answer section (other than the initial - # SOA in the first message). - # - for rrset in r.answer[answer_index:]: - if done: - raise dns.exception.FormError("answers after final SOA") - if rrset.rdtype == dns.rdatatype.SOA and rrset.name == oname: - if expecting_SOA: - if rrset[0].serial != serial: - raise dns.exception.FormError("IXFR base serial mismatch") - expecting_SOA = False - elif rdtype == dns.rdatatype.IXFR: - delete_mode = not delete_mode - # - # If this SOA RRset is equal to the first we saw then we're - # finished. If this is an IXFR we also check that we're - # seeing the record in the expected part of the response. - # - if rrset == soa_rrset and ( - rdtype == dns.rdatatype.AXFR - or (rdtype == dns.rdatatype.IXFR and delete_mode) - ): - done = True - elif expecting_SOA: - # - # We made an IXFR request and are expecting another - # SOA RR, but saw something else, so this must be an - # AXFR response. - # - rdtype = dns.rdatatype.AXFR - expecting_SOA = False - if done and q.keyring and not r.had_tsig: - raise dns.exception.FormError("missing TSIG") - yield r - - -class UDPMode(enum.IntEnum): - """How should UDP be used in an IXFR from :py:func:`inbound_xfr()`? - - NEVER means "never use UDP; always use TCP" - TRY_FIRST means "try to use UDP but fall back to TCP if needed" - ONLY means "raise ``dns.xfr.UseTCP`` if trying UDP does not succeed" - """ - - NEVER = 0 - TRY_FIRST = 1 - ONLY = 2 - - -def inbound_xfr( - where: str, - txn_manager: dns.transaction.TransactionManager, - query: Optional[dns.message.Message] = None, - port: int = 53, - timeout: Optional[float] = None, - lifetime: Optional[float] = None, - source: Optional[str] = None, - source_port: int = 0, - udp_mode: UDPMode = UDPMode.NEVER, -) -> None: - """Conduct an inbound transfer and apply it via a transaction from the - txn_manager. - - *where*, a ``str`` containing an IPv4 or IPv6 address, where - to send the message. - - *txn_manager*, a ``dns.transaction.TransactionManager``, the txn_manager - for this transfer (typically a ``dns.zone.Zone``). - - *query*, the query to send. If not supplied, a default query is - constructed using information from the *txn_manager*. - - *port*, an ``int``, the port send the message to. The default is 53. - - *timeout*, a ``float``, the number of seconds to wait for each - response message. If None, the default, wait forever. - - *lifetime*, a ``float``, the total number of seconds to spend - doing the transfer. If ``None``, the default, then there is no - limit on the time the transfer may take. - - *source*, a ``str`` containing an IPv4 or IPv6 address, specifying - the source address. The default is the wildcard address. - - *source_port*, an ``int``, the port from which to send the message. - The default is 0. - - *udp_mode*, a ``dns.query.UDPMode``, determines how UDP is used - for IXFRs. The default is ``dns.UDPMode.NEVER``, i.e. only use - TCP. Other possibilities are ``dns.UDPMode.TRY_FIRST``, which - means "try UDP but fallback to TCP if needed", and - ``dns.UDPMode.ONLY``, which means "try UDP and raise - ``dns.xfr.UseTCP`` if it does not succeed. - - Raises on errors. - """ - if query is None: - (query, serial) = dns.xfr.make_query(txn_manager) - else: - serial = dns.xfr.extract_serial_from_query(query) - rdtype = query.question[0].rdtype - is_ixfr = rdtype == dns.rdatatype.IXFR - origin = txn_manager.from_wire_origin() - wire = query.to_wire() - (af, destination, source) = _destination_and_source( - where, port, source, source_port - ) - (_, expiration) = _compute_times(lifetime) - retry = True - while retry: - retry = False - if is_ixfr and udp_mode != UDPMode.NEVER: - sock_type = socket.SOCK_DGRAM - is_udp = True - else: - sock_type = socket.SOCK_STREAM - is_udp = False - with _make_socket(af, sock_type, source) as s: - _connect(s, destination, expiration) - if is_udp: - _udp_send(s, wire, None, expiration) - else: - tcpmsg = struct.pack("!H", len(wire)) + wire - _net_write(s, tcpmsg, expiration) - with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: - done = False - tsig_ctx = None - while not done: - (_, mexpiration) = _compute_times(timeout) - if mexpiration is None or ( - expiration is not None and mexpiration > expiration - ): - mexpiration = expiration - if is_udp: - (rwire, _) = _udp_recv(s, 65535, mexpiration) - else: - ldata = _net_read(s, 2, mexpiration) - (l,) = struct.unpack("!H", ldata) - rwire = _net_read(s, l, mexpiration) - r = dns.message.from_wire( - rwire, - keyring=query.keyring, - request_mac=query.mac, - xfr=True, - origin=origin, - tsig_ctx=tsig_ctx, - multi=(not is_udp), - one_rr_per_rrset=is_ixfr, - ) - try: - done = inbound.process_message(r) - except dns.xfr.UseTCP: - assert is_udp # should not happen if we used TCP! - if udp_mode == UDPMode.ONLY: - raise - done = True - retry = True - udp_mode = UDPMode.NEVER - continue - tsig_ctx = r.tsig_ctx - if not retry and query.keyring and not r.had_tsig: - raise dns.exception.FormError("missing TSIG") diff --git a/.venv/lib/python3.12/site-packages/dns/quic/__init__.py b/.venv/lib/python3.12/site-packages/dns/quic/__init__.py deleted file mode 100644 index 20aff34..0000000 --- a/.venv/lib/python3.12/site-packages/dns/quic/__init__.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns._features -import dns.asyncbackend - -if dns._features.have("doq"): - import aioquic.quic.configuration # type: ignore - - from dns._asyncbackend import NullContext - from dns.quic._asyncio import ( - AsyncioQuicConnection, - AsyncioQuicManager, - AsyncioQuicStream, - ) - from dns.quic._common import AsyncQuicConnection, AsyncQuicManager - from dns.quic._sync import SyncQuicConnection, SyncQuicManager, SyncQuicStream - - have_quic = True - - def null_factory( - *args, # pylint: disable=unused-argument - **kwargs, # pylint: disable=unused-argument - ): - return NullContext(None) - - def _asyncio_manager_factory( - context, *args, **kwargs # pylint: disable=unused-argument - ): - return AsyncioQuicManager(*args, **kwargs) - - # We have a context factory and a manager factory as for trio we need to have - # a nursery. - - _async_factories = {"asyncio": (null_factory, _asyncio_manager_factory)} - - if dns._features.have("trio"): - import trio - - from dns.quic._trio import ( # pylint: disable=ungrouped-imports - TrioQuicConnection, - TrioQuicManager, - TrioQuicStream, - ) - - def _trio_context_factory(): - return trio.open_nursery() - - def _trio_manager_factory(context, *args, **kwargs): - return TrioQuicManager(context, *args, **kwargs) - - _async_factories["trio"] = (_trio_context_factory, _trio_manager_factory) - - def factories_for_backend(backend=None): - if backend is None: - backend = dns.asyncbackend.get_default_backend() - return _async_factories[backend.name()] - -else: # pragma: no cover - have_quic = False - - from typing import Any - - class AsyncQuicStream: # type: ignore - pass - - class AsyncQuicConnection: # type: ignore - async def make_stream(self) -> Any: - raise NotImplementedError - - class SyncQuicStream: # type: ignore - pass - - class SyncQuicConnection: # type: ignore - def make_stream(self) -> Any: - raise NotImplementedError diff --git a/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 0ba94868e9e112b9c22ac002a3bbd48ecae33867..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3086 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 J;7$|Nj$Qxi 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 P%k(Y0{$EgGu>XGm1;s)O diff --git a/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/_sync.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/_sync.cpython-312.pyc deleted file mode 100644 index c0d1694a9def7144f7e09ede727378503562190b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15787 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 10: - # yield - count = 0 - await asyncio.sleep(0) - - async def write(self, stream, data, is_end=False): - self._connection.send_stream_data(stream, data, is_end) - async with self._wake_timer: - self._wake_timer.notify_all() - - def run(self): - if self._closed: - return - self._receiver_task = asyncio.Task(self._receiver()) - self._sender_task = asyncio.Task(self._sender()) - - async def make_stream(self, timeout=None): - try: - await asyncio.wait_for(self._handshake_complete.wait(), timeout) - except TimeoutError: - raise dns.exception.Timeout - if self._done: - raise UnexpectedEOF - stream_id = self._connection.get_next_available_stream_id(False) - stream = AsyncioQuicStream(self, stream_id) - self._streams[stream_id] = stream - return stream - - async def close(self): - if not self._closed: - self._manager.closed(self._peer[0], self._peer[1]) - self._closed = True - self._connection.close() - # sender might be blocked on this, so set it - self._socket_created.set() - async with self._wake_timer: - self._wake_timer.notify_all() - try: - await self._receiver_task - except asyncio.CancelledError: - pass - try: - await self._sender_task - except asyncio.CancelledError: - pass - await self._socket.close() - - -class AsyncioQuicManager(AsyncQuicManager): - def __init__(self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None): - super().__init__(conf, verify_mode, AsyncioQuicConnection, server_name) - - def connect( - self, address, port=853, source=None, source_port=0, want_session_ticket=True - ): - (connection, start) = self._connect( - address, port, source, source_port, want_session_ticket - ) - if start: - connection.run() - return connection - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - # Copy the iterator into a list as exiting things will mutate the connections - # table. - connections = list(self._connections.values()) - for connection in connections: - await connection.close() - return False diff --git a/.venv/lib/python3.12/site-packages/dns/quic/_common.py b/.venv/lib/python3.12/site-packages/dns/quic/_common.py deleted file mode 100644 index 0eacc69..0000000 --- a/.venv/lib/python3.12/site-packages/dns/quic/_common.py +++ /dev/null @@ -1,224 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import copy -import functools -import socket -import struct -import time -from typing import Any, Optional - -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore - -import dns.inet - -QUIC_MAX_DATAGRAM = 2048 -MAX_SESSION_TICKETS = 8 -# If we hit the max sessions limit we will delete this many of the oldest connections. -# The value must be a integer > 0 and <= MAX_SESSION_TICKETS. -SESSIONS_TO_DELETE = MAX_SESSION_TICKETS // 4 - - -class UnexpectedEOF(Exception): - pass - - -class Buffer: - def __init__(self): - self._buffer = b"" - self._seen_end = False - - def put(self, data, is_end): - if self._seen_end: - return - self._buffer += data - if is_end: - self._seen_end = True - - def have(self, amount): - if len(self._buffer) >= amount: - return True - if self._seen_end: - raise UnexpectedEOF - return False - - def seen_end(self): - return self._seen_end - - def get(self, amount): - assert self.have(amount) - data = self._buffer[:amount] - self._buffer = self._buffer[amount:] - return data - - -class BaseQuicStream: - def __init__(self, connection, stream_id): - self._connection = connection - self._stream_id = stream_id - self._buffer = Buffer() - self._expecting = 0 - - def id(self): - return self._stream_id - - def _expiration_from_timeout(self, timeout): - if timeout is not None: - expiration = time.time() + timeout - else: - expiration = None - return expiration - - def _timeout_from_expiration(self, expiration): - if expiration is not None: - timeout = max(expiration - time.time(), 0.0) - else: - timeout = None - return timeout - - # Subclass must implement receive() as sync / async and which returns a message - # or raises UnexpectedEOF. - - def _encapsulate(self, datagram): - l = len(datagram) - return struct.pack("!H", l) + datagram - - def _common_add_input(self, data, is_end): - self._buffer.put(data, is_end) - try: - return self._expecting > 0 and self._buffer.have(self._expecting) - except UnexpectedEOF: - return True - - def _close(self): - self._connection.close_stream(self._stream_id) - self._buffer.put(b"", True) # send EOF in case we haven't seen it. - - -class BaseQuicConnection: - def __init__( - self, connection, address, port, source=None, source_port=0, manager=None - ): - self._done = False - self._connection = connection - self._address = address - self._port = port - self._closed = False - self._manager = manager - self._streams = {} - self._af = dns.inet.af_for_address(address) - self._peer = dns.inet.low_level_address_tuple((address, port)) - if source is None and source_port != 0: - if self._af == socket.AF_INET: - source = "0.0.0.0" - elif self._af == socket.AF_INET6: - source = "::" - else: - raise NotImplementedError - if source: - self._source = (source, source_port) - else: - self._source = None - - def close_stream(self, stream_id): - del self._streams[stream_id] - - def _get_timer_values(self, closed_is_special=True): - now = time.time() - expiration = self._connection.get_timer() - if expiration is None: - expiration = now + 3600 # arbitrary "big" value - interval = max(expiration - now, 0) - if self._closed and closed_is_special: - # lower sleep interval to avoid a race in the closing process - # which can lead to higher latency closing due to sleeping when - # we have events. - interval = min(interval, 0.05) - return (expiration, interval) - - def _handle_timer(self, expiration): - now = time.time() - if expiration <= now: - self._connection.handle_timer(now) - - -class AsyncQuicConnection(BaseQuicConnection): - async def make_stream(self, timeout: Optional[float] = None) -> Any: - pass - - -class BaseQuicManager: - def __init__(self, conf, verify_mode, connection_factory, server_name=None): - self._connections = {} - self._connection_factory = connection_factory - self._session_tickets = {} - if conf is None: - verify_path = None - if isinstance(verify_mode, str): - verify_path = verify_mode - verify_mode = True - conf = aioquic.quic.configuration.QuicConfiguration( - alpn_protocols=["doq", "doq-i03"], - verify_mode=verify_mode, - server_name=server_name, - ) - if verify_path is not None: - conf.load_verify_locations(verify_path) - self._conf = conf - - def _connect( - self, address, port=853, source=None, source_port=0, want_session_ticket=True - ): - connection = self._connections.get((address, port)) - if connection is not None: - return (connection, False) - conf = self._conf - if want_session_ticket: - try: - session_ticket = self._session_tickets.pop((address, port)) - # We found a session ticket, so make a configuration that uses it. - conf = copy.copy(conf) - conf.session_ticket = session_ticket - except KeyError: - # No session ticket. - pass - # Whether or not we found a session ticket, we want a handler to save - # one. - session_ticket_handler = functools.partial( - self.save_session_ticket, address, port - ) - else: - session_ticket_handler = None - qconn = aioquic.quic.connection.QuicConnection( - configuration=conf, - session_ticket_handler=session_ticket_handler, - ) - lladdress = dns.inet.low_level_address_tuple((address, port)) - qconn.connect(lladdress, time.time()) - connection = self._connection_factory( - qconn, address, port, source, source_port, self - ) - self._connections[(address, port)] = connection - return (connection, True) - - def closed(self, address, port): - try: - del self._connections[(address, port)] - except KeyError: - pass - - def save_session_ticket(self, address, port, ticket): - # We rely on dictionaries keys() being in insertion order here. We - # can't just popitem() as that would be LIFO which is the opposite of - # what we want. - l = len(self._session_tickets) - if l >= MAX_SESSION_TICKETS: - keys_to_delete = list(self._session_tickets.keys())[0:SESSIONS_TO_DELETE] - for key in keys_to_delete: - del self._session_tickets[key] - self._session_tickets[(address, port)] = ticket - - -class AsyncQuicManager(BaseQuicManager): - def connect(self, address, port=853, source=None, source_port=0): - raise NotImplementedError diff --git a/.venv/lib/python3.12/site-packages/dns/quic/_sync.py b/.venv/lib/python3.12/site-packages/dns/quic/_sync.py deleted file mode 100644 index 120cb5f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/quic/_sync.py +++ /dev/null @@ -1,238 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import selectors -import socket -import ssl -import struct -import threading -import time - -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore -import aioquic.quic.events # type: ignore - -import dns.exception -import dns.inet -from dns.quic._common import ( - QUIC_MAX_DATAGRAM, - BaseQuicConnection, - BaseQuicManager, - BaseQuicStream, - UnexpectedEOF, -) - -# Avoid circularity with dns.query -if hasattr(selectors, "PollSelector"): - _selector_class = selectors.PollSelector # type: ignore -else: - _selector_class = selectors.SelectSelector # type: ignore - - -class SyncQuicStream(BaseQuicStream): - def __init__(self, connection, stream_id): - super().__init__(connection, stream_id) - self._wake_up = threading.Condition() - self._lock = threading.Lock() - - def wait_for(self, amount, expiration): - while True: - timeout = self._timeout_from_expiration(expiration) - with self._lock: - if self._buffer.have(amount): - return - self._expecting = amount - with self._wake_up: - if not self._wake_up.wait(timeout): - raise dns.exception.Timeout - self._expecting = 0 - - def receive(self, timeout=None): - expiration = self._expiration_from_timeout(timeout) - self.wait_for(2, expiration) - with self._lock: - (size,) = struct.unpack("!H", self._buffer.get(2)) - self.wait_for(size, expiration) - with self._lock: - return self._buffer.get(size) - - def send(self, datagram, is_end=False): - data = self._encapsulate(datagram) - self._connection.write(self._stream_id, data, is_end) - - def _add_input(self, data, is_end): - if self._common_add_input(data, is_end): - with self._wake_up: - self._wake_up.notify() - - def close(self): - with self._lock: - self._close() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - with self._wake_up: - self._wake_up.notify() - return False - - -class SyncQuicConnection(BaseQuicConnection): - def __init__(self, connection, address, port, source, source_port, manager): - super().__init__(connection, address, port, source, source_port, manager) - self._socket = socket.socket(self._af, socket.SOCK_DGRAM, 0) - if self._source is not None: - try: - self._socket.bind( - dns.inet.low_level_address_tuple(self._source, self._af) - ) - except Exception: - self._socket.close() - raise - self._socket.connect(self._peer) - (self._send_wakeup, self._receive_wakeup) = socket.socketpair() - self._receive_wakeup.setblocking(False) - self._socket.setblocking(False) - self._handshake_complete = threading.Event() - self._worker_thread = None - self._lock = threading.Lock() - - def _read(self): - count = 0 - while count < 10: - count += 1 - try: - datagram = self._socket.recv(QUIC_MAX_DATAGRAM) - except BlockingIOError: - return - with self._lock: - self._connection.receive_datagram(datagram, self._peer, time.time()) - - def _drain_wakeup(self): - while True: - try: - self._receive_wakeup.recv(32) - except BlockingIOError: - return - - def _worker(self): - try: - sel = _selector_class() - sel.register(self._socket, selectors.EVENT_READ, self._read) - sel.register(self._receive_wakeup, selectors.EVENT_READ, self._drain_wakeup) - while not self._done: - (expiration, interval) = self._get_timer_values(False) - items = sel.select(interval) - for key, _ in items: - key.data() - with self._lock: - self._handle_timer(expiration) - self._handle_events() - with self._lock: - datagrams = self._connection.datagrams_to_send(time.time()) - for datagram, _ in datagrams: - try: - self._socket.send(datagram) - except BlockingIOError: - # we let QUIC handle any lossage - pass - finally: - with self._lock: - self._done = True - # Ensure anyone waiting for this gets woken up. - self._handshake_complete.set() - - def _handle_events(self): - while True: - with self._lock: - event = self._connection.next_event() - if event is None: - return - if isinstance(event, aioquic.quic.events.StreamDataReceived): - with self._lock: - stream = self._streams.get(event.stream_id) - if stream: - stream._add_input(event.data, event.end_stream) - elif isinstance(event, aioquic.quic.events.HandshakeCompleted): - self._handshake_complete.set() - elif isinstance(event, aioquic.quic.events.ConnectionTerminated): - with self._lock: - self._done = True - elif isinstance(event, aioquic.quic.events.StreamReset): - with self._lock: - stream = self._streams.get(event.stream_id) - if stream: - stream._add_input(b"", True) - - def write(self, stream, data, is_end=False): - with self._lock: - self._connection.send_stream_data(stream, data, is_end) - self._send_wakeup.send(b"\x01") - - def run(self): - if self._closed: - return - self._worker_thread = threading.Thread(target=self._worker) - self._worker_thread.start() - - def make_stream(self, timeout=None): - if not self._handshake_complete.wait(timeout): - raise dns.exception.Timeout - with self._lock: - if self._done: - raise UnexpectedEOF - stream_id = self._connection.get_next_available_stream_id(False) - stream = SyncQuicStream(self, stream_id) - self._streams[stream_id] = stream - return stream - - def close_stream(self, stream_id): - with self._lock: - super().close_stream(stream_id) - - def close(self): - with self._lock: - if self._closed: - return - self._manager.closed(self._peer[0], self._peer[1]) - self._closed = True - self._connection.close() - self._send_wakeup.send(b"\x01") - self._worker_thread.join() - - -class SyncQuicManager(BaseQuicManager): - def __init__(self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None): - super().__init__(conf, verify_mode, SyncQuicConnection, server_name) - self._lock = threading.Lock() - - def connect( - self, address, port=853, source=None, source_port=0, want_session_ticket=True - ): - with self._lock: - (connection, start) = self._connect( - address, port, source, source_port, want_session_ticket - ) - if start: - connection.run() - return connection - - def closed(self, address, port): - with self._lock: - super().closed(address, port) - - def save_session_ticket(self, address, port, ticket): - with self._lock: - super().save_session_ticket(address, port, ticket) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - # Copy the iterator into a list as exiting things will mutate the connections - # table. - connections = list(self._connections.values()) - for connection in connections: - connection.close() - return False diff --git a/.venv/lib/python3.12/site-packages/dns/quic/_trio.py b/.venv/lib/python3.12/site-packages/dns/quic/_trio.py deleted file mode 100644 index 35e36b9..0000000 --- a/.venv/lib/python3.12/site-packages/dns/quic/_trio.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import socket -import ssl -import struct -import time - -import aioquic.quic.configuration # type: ignore -import aioquic.quic.connection # type: ignore -import aioquic.quic.events # type: ignore -import trio - -import dns.exception -import dns.inet -from dns._asyncbackend import NullContext -from dns.quic._common import ( - QUIC_MAX_DATAGRAM, - AsyncQuicConnection, - AsyncQuicManager, - BaseQuicStream, - UnexpectedEOF, -) - - -class TrioQuicStream(BaseQuicStream): - def __init__(self, connection, stream_id): - super().__init__(connection, stream_id) - self._wake_up = trio.Condition() - - async def wait_for(self, amount): - while True: - if self._buffer.have(amount): - return - self._expecting = amount - async with self._wake_up: - await self._wake_up.wait() - self._expecting = 0 - - async def receive(self, timeout=None): - if timeout is None: - context = NullContext(None) - else: - context = trio.move_on_after(timeout) - with context: - await self.wait_for(2) - (size,) = struct.unpack("!H", self._buffer.get(2)) - await self.wait_for(size) - return self._buffer.get(size) - raise dns.exception.Timeout - - async def send(self, datagram, is_end=False): - data = self._encapsulate(datagram) - await self._connection.write(self._stream_id, data, is_end) - - async def _add_input(self, data, is_end): - if self._common_add_input(data, is_end): - async with self._wake_up: - self._wake_up.notify() - - async def close(self): - self._close() - - # Streams are async context managers - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.close() - async with self._wake_up: - self._wake_up.notify() - return False - - -class TrioQuicConnection(AsyncQuicConnection): - def __init__(self, connection, address, port, source, source_port, manager=None): - super().__init__(connection, address, port, source, source_port, manager) - self._socket = trio.socket.socket(self._af, socket.SOCK_DGRAM, 0) - self._handshake_complete = trio.Event() - self._run_done = trio.Event() - self._worker_scope = None - self._send_pending = False - - async def _worker(self): - try: - if self._source: - await self._socket.bind( - dns.inet.low_level_address_tuple(self._source, self._af) - ) - await self._socket.connect(self._peer) - while not self._done: - (expiration, interval) = self._get_timer_values(False) - if self._send_pending: - # Do not block forever if sends are pending. Even though we - # have a wake-up mechanism if we've already started the blocking - # read, the possibility of context switching in send means that - # more writes can happen while we have no wake up context, so - # we need self._send_pending to avoid (effectively) a "lost wakeup" - # race. - interval = 0.0 - with trio.CancelScope( - deadline=trio.current_time() + interval - ) as self._worker_scope: - datagram = await self._socket.recv(QUIC_MAX_DATAGRAM) - self._connection.receive_datagram(datagram, self._peer, time.time()) - self._worker_scope = None - self._handle_timer(expiration) - await self._handle_events() - # We clear this now, before sending anything, as sending can cause - # context switches that do more sends. We want to know if that - # happens so we don't block a long time on the recv() above. - self._send_pending = False - datagrams = self._connection.datagrams_to_send(time.time()) - for datagram, _ in datagrams: - await self._socket.send(datagram) - finally: - self._done = True - self._handshake_complete.set() - - async def _handle_events(self): - count = 0 - while True: - event = self._connection.next_event() - if event is None: - return - if isinstance(event, aioquic.quic.events.StreamDataReceived): - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input(event.data, event.end_stream) - elif isinstance(event, aioquic.quic.events.HandshakeCompleted): - self._handshake_complete.set() - elif isinstance(event, aioquic.quic.events.ConnectionTerminated): - self._done = True - self._socket.close() - elif isinstance(event, aioquic.quic.events.StreamReset): - stream = self._streams.get(event.stream_id) - if stream: - await stream._add_input(b"", True) - count += 1 - if count > 10: - # yield - count = 0 - await trio.sleep(0) - - async def write(self, stream, data, is_end=False): - self._connection.send_stream_data(stream, data, is_end) - self._send_pending = True - if self._worker_scope is not None: - self._worker_scope.cancel() - - async def run(self): - if self._closed: - return - async with trio.open_nursery() as nursery: - nursery.start_soon(self._worker) - self._run_done.set() - - async def make_stream(self, timeout=None): - if timeout is None: - context = NullContext(None) - else: - context = trio.move_on_after(timeout) - with context: - await self._handshake_complete.wait() - if self._done: - raise UnexpectedEOF - stream_id = self._connection.get_next_available_stream_id(False) - stream = TrioQuicStream(self, stream_id) - self._streams[stream_id] = stream - return stream - raise dns.exception.Timeout - - async def close(self): - if not self._closed: - self._manager.closed(self._peer[0], self._peer[1]) - self._closed = True - self._connection.close() - self._send_pending = True - if self._worker_scope is not None: - self._worker_scope.cancel() - await self._run_done.wait() - - -class TrioQuicManager(AsyncQuicManager): - def __init__( - self, nursery, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None - ): - super().__init__(conf, verify_mode, TrioQuicConnection, server_name) - self._nursery = nursery - - def connect( - self, address, port=853, source=None, source_port=0, want_session_ticket=True - ): - (connection, start) = self._connect( - address, port, source, source_port, want_session_ticket - ) - if start: - self._nursery.start_soon(connection.run) - return connection - - async def __aenter__(self): - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - # Copy the iterator into a list as exiting things will mutate the connections - # table. - connections = list(self._connections.values()) - for connection in connections: - await connection.close() - return False diff --git a/.venv/lib/python3.12/site-packages/dns/rcode.py b/.venv/lib/python3.12/site-packages/dns/rcode.py deleted file mode 100644 index 8e6386f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rcode.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Result Codes.""" - -from typing import Tuple - -import dns.enum -import dns.exception - - -class Rcode(dns.enum.IntEnum): - #: No error - NOERROR = 0 - #: Format error - FORMERR = 1 - #: Server failure - SERVFAIL = 2 - #: Name does not exist ("Name Error" in RFC 1025 terminology). - NXDOMAIN = 3 - #: Not implemented - NOTIMP = 4 - #: Refused - REFUSED = 5 - #: Name exists. - YXDOMAIN = 6 - #: RRset exists. - YXRRSET = 7 - #: RRset does not exist. - NXRRSET = 8 - #: Not authoritative. - NOTAUTH = 9 - #: Name not in zone. - NOTZONE = 10 - #: DSO-TYPE Not Implemented - DSOTYPENI = 11 - #: Bad EDNS version. - BADVERS = 16 - #: TSIG Signature Failure - BADSIG = 16 - #: Key not recognized. - BADKEY = 17 - #: Signature out of time window. - BADTIME = 18 - #: Bad TKEY Mode. - BADMODE = 19 - #: Duplicate key name. - BADNAME = 20 - #: Algorithm not supported. - BADALG = 21 - #: Bad Truncation - BADTRUNC = 22 - #: Bad/missing Server Cookie - BADCOOKIE = 23 - - @classmethod - def _maximum(cls): - return 4095 - - @classmethod - def _unknown_exception_class(cls): - return UnknownRcode - - -class UnknownRcode(dns.exception.DNSException): - """A DNS rcode is unknown.""" - - -def from_text(text: str) -> Rcode: - """Convert text into an rcode. - - *text*, a ``str``, the textual rcode or an integer in textual form. - - Raises ``dns.rcode.UnknownRcode`` if the rcode mnemonic is unknown. - - Returns a ``dns.rcode.Rcode``. - """ - - return Rcode.from_text(text) - - -def from_flags(flags: int, ednsflags: int) -> Rcode: - """Return the rcode value encoded by flags and ednsflags. - - *flags*, an ``int``, the DNS flags field. - - *ednsflags*, an ``int``, the EDNS flags field. - - Raises ``ValueError`` if rcode is < 0 or > 4095 - - Returns a ``dns.rcode.Rcode``. - """ - - value = (flags & 0x000F) | ((ednsflags >> 20) & 0xFF0) - return Rcode.make(value) - - -def to_flags(value: Rcode) -> Tuple[int, int]: - """Return a (flags, ednsflags) tuple which encodes the rcode. - - *value*, a ``dns.rcode.Rcode``, the rcode. - - Raises ``ValueError`` if rcode is < 0 or > 4095. - - Returns an ``(int, int)`` tuple. - """ - - if value < 0 or value > 4095: - raise ValueError("rcode must be >= 0 and <= 4095") - v = value & 0xF - ev = (value & 0xFF0) << 20 - return (v, ev) - - -def to_text(value: Rcode, tsig: bool = False) -> str: - """Convert rcode into text. - - *value*, a ``dns.rcode.Rcode``, the rcode. - - Raises ``ValueError`` if rcode is < 0 or > 4095. - - Returns a ``str``. - """ - - if tsig and value == Rcode.BADVERS: - return "BADSIG" - return Rcode.to_text(value) - - -### BEGIN generated Rcode constants - -NOERROR = Rcode.NOERROR -FORMERR = Rcode.FORMERR -SERVFAIL = Rcode.SERVFAIL -NXDOMAIN = Rcode.NXDOMAIN -NOTIMP = Rcode.NOTIMP -REFUSED = Rcode.REFUSED -YXDOMAIN = Rcode.YXDOMAIN -YXRRSET = Rcode.YXRRSET -NXRRSET = Rcode.NXRRSET -NOTAUTH = Rcode.NOTAUTH -NOTZONE = Rcode.NOTZONE -DSOTYPENI = Rcode.DSOTYPENI -BADVERS = Rcode.BADVERS -BADSIG = Rcode.BADSIG -BADKEY = Rcode.BADKEY -BADTIME = Rcode.BADTIME -BADMODE = Rcode.BADMODE -BADNAME = Rcode.BADNAME -BADALG = Rcode.BADALG -BADTRUNC = Rcode.BADTRUNC -BADCOOKIE = Rcode.BADCOOKIE - -### END generated Rcode constants diff --git a/.venv/lib/python3.12/site-packages/dns/rdata.py b/.venv/lib/python3.12/site-packages/dns/rdata.py deleted file mode 100644 index 024fd8f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdata.py +++ /dev/null @@ -1,884 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdata.""" - -import base64 -import binascii -import inspect -import io -import itertools -import random -from importlib import import_module -from typing import Any, Dict, Optional, Tuple, Union - -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.ipv6 -import dns.name -import dns.rdataclass -import dns.rdatatype -import dns.tokenizer -import dns.ttl -import dns.wire - -_chunksize = 32 - -# We currently allow comparisons for rdata with relative names for backwards -# compatibility, but in the future we will not, as these kinds of comparisons -# can lead to subtle bugs if code is not carefully written. -# -# This switch allows the future behavior to be turned on so code can be -# tested with it. -_allow_relative_comparisons = True - - -class NoRelativeRdataOrdering(dns.exception.DNSException): - """An attempt was made to do an ordered comparison of one or more - rdata with relative names. The only reliable way of sorting rdata - is to use non-relativized rdata. - - """ - - -def _wordbreak(data, chunksize=_chunksize, separator=b" "): - """Break a binary string into chunks of chunksize characters separated by - a space. - """ - - if not chunksize: - return data.decode() - return separator.join( - [data[i : i + chunksize] for i in range(0, len(data), chunksize)] - ).decode() - - -# pylint: disable=unused-argument - - -def _hexify(data, chunksize=_chunksize, separator=b" ", **kw): - """Convert a binary string into its hex encoding, broken up into chunks - of chunksize characters separated by a separator. - """ - - return _wordbreak(binascii.hexlify(data), chunksize, separator) - - -def _base64ify(data, chunksize=_chunksize, separator=b" ", **kw): - """Convert a binary string into its base64 encoding, broken up into chunks - of chunksize characters separated by a separator. - """ - - return _wordbreak(base64.b64encode(data), chunksize, separator) - - -# pylint: enable=unused-argument - -__escaped = b'"\\' - - -def _escapify(qstring): - """Escape the characters in a quoted string which need it.""" - - if isinstance(qstring, str): - qstring = qstring.encode() - if not isinstance(qstring, bytearray): - qstring = bytearray(qstring) - - text = "" - for c in qstring: - if c in __escaped: - text += "\\" + chr(c) - elif c >= 0x20 and c < 0x7F: - text += chr(c) - else: - text += "\\%03d" % c - return text - - -def _truncate_bitmap(what): - """Determine the index of greatest byte that isn't all zeros, and - return the bitmap that contains all the bytes less than that index. - """ - - for i in range(len(what) - 1, -1, -1): - if what[i] != 0: - return what[0 : i + 1] - return what[0:1] - - -# So we don't have to edit all the rdata classes... -_constify = dns.immutable.constify - - -@dns.immutable.immutable -class Rdata: - """Base class for all DNS rdata types.""" - - __slots__ = ["rdclass", "rdtype", "rdcomment"] - - def __init__(self, rdclass, rdtype): - """Initialize an rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - """ - - self.rdclass = self._as_rdataclass(rdclass) - self.rdtype = self._as_rdatatype(rdtype) - self.rdcomment = None - - def _get_all_slots(self): - return itertools.chain.from_iterable( - getattr(cls, "__slots__", []) for cls in self.__class__.__mro__ - ) - - def __getstate__(self): - # We used to try to do a tuple of all slots here, but it - # doesn't work as self._all_slots isn't available at - # __setstate__() time. Before that we tried to store a tuple - # of __slots__, but that didn't work as it didn't store the - # slots defined by ancestors. This older way didn't fail - # outright, but ended up with partially broken objects, e.g. - # if you unpickled an A RR it wouldn't have rdclass and rdtype - # attributes, and would compare badly. - state = {} - for slot in self._get_all_slots(): - state[slot] = getattr(self, slot) - return state - - def __setstate__(self, state): - for slot, val in state.items(): - object.__setattr__(self, slot, val) - if not hasattr(self, "rdcomment"): - # Pickled rdata from 2.0.x might not have a rdcomment, so add - # it if needed. - object.__setattr__(self, "rdcomment", None) - - def covers(self) -> dns.rdatatype.RdataType: - """Return the type a Rdata covers. - - DNS SIG/RRSIG rdatas apply to a specific type; this type is - returned by the covers() function. If the rdata type is not - SIG or RRSIG, dns.rdatatype.NONE is returned. This is useful when - creating rdatasets, allowing the rdataset to contain only RRSIGs - of a particular type, e.g. RRSIG(NS). - - Returns a ``dns.rdatatype.RdataType``. - """ - - return dns.rdatatype.NONE - - def extended_rdatatype(self) -> int: - """Return a 32-bit type value, the least significant 16 bits of - which are the ordinary DNS type, and the upper 16 bits of which are - the "covered" type, if any. - - Returns an ``int``. - """ - - return self.covers() << 16 | self.rdtype - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - """Convert an rdata to text format. - - Returns a ``str``. - """ - - raise NotImplementedError # pragma: no cover - - def _to_wire( - self, - file: Optional[Any], - compress: Optional[dns.name.CompressType] = None, - origin: Optional[dns.name.Name] = None, - canonicalize: bool = False, - ) -> bytes: - raise NotImplementedError # pragma: no cover - - def to_wire( - self, - file: Optional[Any] = None, - compress: Optional[dns.name.CompressType] = None, - origin: Optional[dns.name.Name] = None, - canonicalize: bool = False, - ) -> bytes: - """Convert an rdata to wire format. - - Returns a ``bytes`` or ``None``. - """ - - if file: - return self._to_wire(file, compress, origin, canonicalize) - else: - f = io.BytesIO() - self._to_wire(f, compress, origin, canonicalize) - return f.getvalue() - - def to_generic( - self, origin: Optional[dns.name.Name] = None - ) -> "dns.rdata.GenericRdata": - """Creates a dns.rdata.GenericRdata equivalent of this rdata. - - Returns a ``dns.rdata.GenericRdata``. - """ - return dns.rdata.GenericRdata( - self.rdclass, self.rdtype, self.to_wire(origin=origin) - ) - - def to_digestable(self, origin: Optional[dns.name.Name] = None) -> bytes: - """Convert rdata to a format suitable for digesting in hashes. This - is also the DNSSEC canonical form. - - Returns a ``bytes``. - """ - - return self.to_wire(origin=origin, canonicalize=True) - - def __repr__(self): - covers = self.covers() - if covers == dns.rdatatype.NONE: - ctext = "" - else: - ctext = "(" + dns.rdatatype.to_text(covers) + ")" - return ( - "" - ) - - def __str__(self): - return self.to_text() - - def _cmp(self, other): - """Compare an rdata with another rdata of the same rdtype and - rdclass. - - For rdata with only absolute names: - Return < 0 if self < other in the DNSSEC ordering, 0 if self - == other, and > 0 if self > other. - For rdata with at least one relative names: - The rdata sorts before any rdata with only absolute names. - When compared with another relative rdata, all names are - made absolute as if they were relative to the root, as the - proper origin is not available. While this creates a stable - ordering, it is NOT guaranteed to be the DNSSEC ordering. - In the future, all ordering comparisons for rdata with - relative names will be disallowed. - """ - try: - our = self.to_digestable() - our_relative = False - except dns.name.NeedAbsoluteNameOrOrigin: - if _allow_relative_comparisons: - our = self.to_digestable(dns.name.root) - our_relative = True - try: - their = other.to_digestable() - their_relative = False - except dns.name.NeedAbsoluteNameOrOrigin: - if _allow_relative_comparisons: - their = other.to_digestable(dns.name.root) - their_relative = True - if _allow_relative_comparisons: - if our_relative != their_relative: - # For the purpose of comparison, all rdata with at least one - # relative name is less than an rdata with only absolute names. - if our_relative: - return -1 - else: - return 1 - elif our_relative or their_relative: - raise NoRelativeRdataOrdering - if our == their: - return 0 - elif our > their: - return 1 - else: - return -1 - - def __eq__(self, other): - if not isinstance(other, Rdata): - return False - if self.rdclass != other.rdclass or self.rdtype != other.rdtype: - return False - our_relative = False - their_relative = False - try: - our = self.to_digestable() - except dns.name.NeedAbsoluteNameOrOrigin: - our = self.to_digestable(dns.name.root) - our_relative = True - try: - their = other.to_digestable() - except dns.name.NeedAbsoluteNameOrOrigin: - their = other.to_digestable(dns.name.root) - their_relative = True - if our_relative != their_relative: - return False - return our == their - - def __ne__(self, other): - if not isinstance(other, Rdata): - return True - if self.rdclass != other.rdclass or self.rdtype != other.rdtype: - return True - return not self.__eq__(other) - - def __lt__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) < 0 - - def __le__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) <= 0 - - def __ge__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) >= 0 - - def __gt__(self, other): - if ( - not isinstance(other, Rdata) - or self.rdclass != other.rdclass - or self.rdtype != other.rdtype - ): - return NotImplemented - return self._cmp(other) > 0 - - def __hash__(self): - return hash(self.to_digestable(dns.name.root)) - - @classmethod - def from_text( - cls, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - tok: dns.tokenizer.Tokenizer, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, - ) -> "Rdata": - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser( - cls, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - parser: dns.wire.Parser, - origin: Optional[dns.name.Name] = None, - ) -> "Rdata": - raise NotImplementedError # pragma: no cover - - def replace(self, **kwargs: Any) -> "Rdata": - """ - Create a new Rdata instance based on the instance replace was - invoked on. It is possible to pass different parameters to - override the corresponding properties of the base Rdata. - - Any field specific to the Rdata type can be replaced, but the - *rdtype* and *rdclass* fields cannot. - - Returns an instance of the same Rdata subclass as *self*. - """ - - # Get the constructor parameters. - parameters = inspect.signature(self.__init__).parameters # type: ignore - - # Ensure that all of the arguments correspond to valid fields. - # Don't allow rdclass or rdtype to be changed, though. - for key in kwargs: - if key == "rdcomment": - continue - if key not in parameters: - raise AttributeError( - "'{}' object has no attribute '{}'".format( - self.__class__.__name__, key - ) - ) - if key in ("rdclass", "rdtype"): - raise AttributeError( - "Cannot overwrite '{}' attribute '{}'".format( - self.__class__.__name__, key - ) - ) - - # Construct the parameter list. For each field, use the value in - # kwargs if present, and the current value otherwise. - args = (kwargs.get(key, getattr(self, key)) for key in parameters) - - # Create, validate, and return the new object. - rd = self.__class__(*args) - # The comment is not set in the constructor, so give it special - # handling. - rdcomment = kwargs.get("rdcomment", self.rdcomment) - if rdcomment is not None: - object.__setattr__(rd, "rdcomment", rdcomment) - return rd - - # Type checking and conversion helpers. These are class methods as - # they don't touch object state and may be useful to others. - - @classmethod - def _as_rdataclass(cls, value): - return dns.rdataclass.RdataClass.make(value) - - @classmethod - def _as_rdatatype(cls, value): - return dns.rdatatype.RdataType.make(value) - - @classmethod - def _as_bytes( - cls, - value: Any, - encode: bool = False, - max_length: Optional[int] = None, - empty_ok: bool = True, - ) -> bytes: - if encode and isinstance(value, str): - bvalue = value.encode() - elif isinstance(value, bytearray): - bvalue = bytes(value) - elif isinstance(value, bytes): - bvalue = value - else: - raise ValueError("not bytes") - if max_length is not None and len(bvalue) > max_length: - raise ValueError("too long") - if not empty_ok and len(bvalue) == 0: - raise ValueError("empty bytes not allowed") - return bvalue - - @classmethod - def _as_name(cls, value): - # Note that proper name conversion (e.g. with origin and IDNA - # awareness) is expected to be done via from_text. This is just - # a simple thing for people invoking the constructor directly. - if isinstance(value, str): - return dns.name.from_text(value) - elif not isinstance(value, dns.name.Name): - raise ValueError("not a name") - return value - - @classmethod - def _as_uint8(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 255: - raise ValueError("not a uint8") - return value - - @classmethod - def _as_uint16(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 65535: - raise ValueError("not a uint16") - return value - - @classmethod - def _as_uint32(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 4294967295: - raise ValueError("not a uint32") - return value - - @classmethod - def _as_uint48(cls, value): - if not isinstance(value, int): - raise ValueError("not an integer") - if value < 0 or value > 281474976710655: - raise ValueError("not a uint48") - return value - - @classmethod - def _as_int(cls, value, low=None, high=None): - if not isinstance(value, int): - raise ValueError("not an integer") - if low is not None and value < low: - raise ValueError("value too small") - if high is not None and value > high: - raise ValueError("value too large") - return value - - @classmethod - def _as_ipv4_address(cls, value): - if isinstance(value, str): - return dns.ipv4.canonicalize(value) - elif isinstance(value, bytes): - return dns.ipv4.inet_ntoa(value) - else: - raise ValueError("not an IPv4 address") - - @classmethod - def _as_ipv6_address(cls, value): - if isinstance(value, str): - return dns.ipv6.canonicalize(value) - elif isinstance(value, bytes): - return dns.ipv6.inet_ntoa(value) - else: - raise ValueError("not an IPv6 address") - - @classmethod - def _as_bool(cls, value): - if isinstance(value, bool): - return value - else: - raise ValueError("not a boolean") - - @classmethod - def _as_ttl(cls, value): - if isinstance(value, int): - return cls._as_int(value, 0, dns.ttl.MAX_TTL) - elif isinstance(value, str): - return dns.ttl.from_text(value) - else: - raise ValueError("not a TTL") - - @classmethod - def _as_tuple(cls, value, as_value): - try: - # For user convenience, if value is a singleton of the list - # element type, wrap it in a tuple. - return (as_value(value),) - except Exception: - # Otherwise, check each element of the iterable *value* - # against *as_value*. - return tuple(as_value(v) for v in value) - - # Processing order - - @classmethod - def _processing_order(cls, iterable): - items = list(iterable) - random.shuffle(items) - return items - - -@dns.immutable.immutable -class GenericRdata(Rdata): - """Generic Rdata Class - - This class is used for rdata types for which we have no better - implementation. It implements the DNS "unknown RRs" scheme. - """ - - __slots__ = ["data"] - - def __init__(self, rdclass, rdtype, data): - super().__init__(rdclass, rdtype) - self.data = data - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - return r"\# %d " % len(self.data) + _hexify(self.data, **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - token = tok.get() - if not token.is_identifier() or token.value != r"\#": - raise dns.exception.SyntaxError(r"generic rdata does not start with \#") - length = tok.get_int() - hex = tok.concatenate_remaining_identifiers(True).encode() - data = binascii.unhexlify(hex) - if len(data) != length: - raise dns.exception.SyntaxError("generic rdata hex data has wrong length") - return cls(rdclass, rdtype, data) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.data) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - return cls(rdclass, rdtype, parser.get_remaining()) - - -_rdata_classes: Dict[Tuple[dns.rdataclass.RdataClass, dns.rdatatype.RdataType], Any] = ( - {} -) -_module_prefix = "dns.rdtypes" - - -def get_rdata_class(rdclass, rdtype): - cls = _rdata_classes.get((rdclass, rdtype)) - if not cls: - cls = _rdata_classes.get((dns.rdatatype.ANY, rdtype)) - if not cls: - rdclass_text = dns.rdataclass.to_text(rdclass) - rdtype_text = dns.rdatatype.to_text(rdtype) - rdtype_text = rdtype_text.replace("-", "_") - try: - mod = import_module( - ".".join([_module_prefix, rdclass_text, rdtype_text]) - ) - cls = getattr(mod, rdtype_text) - _rdata_classes[(rdclass, rdtype)] = cls - except ImportError: - try: - mod = import_module(".".join([_module_prefix, "ANY", rdtype_text])) - cls = getattr(mod, rdtype_text) - _rdata_classes[(dns.rdataclass.ANY, rdtype)] = cls - _rdata_classes[(rdclass, rdtype)] = cls - except ImportError: - pass - if not cls: - cls = GenericRdata - _rdata_classes[(rdclass, rdtype)] = cls - return cls - - -def from_text( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - tok: Union[dns.tokenizer.Tokenizer, str], - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, -) -> Rdata: - """Build an rdata object from text format. - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_text() class method is called - with the parameters to this function. - - If *tok* is a ``str``, then a tokenizer is created and the string - is used as its input. - - *rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype. - - *tok*, a ``dns.tokenizer.Tokenizer`` or a ``str``. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use if a tokenizer needs to be created. If - ``None``, the default IDNA 2003 encoder/decoder is used. If a - tokenizer is not created, then the codec associated with the tokenizer - is the one that is used. - - Returns an instance of the chosen Rdata subclass. - - """ - if isinstance(tok, str): - tok = dns.tokenizer.Tokenizer(tok, idna_codec=idna_codec) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - cls = get_rdata_class(rdclass, rdtype) - with dns.exception.ExceptionWrapper(dns.exception.SyntaxError): - rdata = None - if cls != GenericRdata: - # peek at first token - token = tok.get() - tok.unget(token) - if token.is_identifier() and token.value == r"\#": - # - # Known type using the generic syntax. Extract the - # wire form from the generic syntax, and then run - # from_wire on it. - # - grdata = GenericRdata.from_text( - rdclass, rdtype, tok, origin, relativize, relativize_to - ) - rdata = from_wire( - rdclass, rdtype, grdata.data, 0, len(grdata.data), origin - ) - # - # If this comparison isn't equal, then there must have been - # compressed names in the wire format, which is an error, - # there being no reasonable context to decompress with. - # - rwire = rdata.to_wire() - if rwire != grdata.data: - raise dns.exception.SyntaxError( - "compressed data in " - "generic syntax form " - "of known rdatatype" - ) - if rdata is None: - rdata = cls.from_text( - rdclass, rdtype, tok, origin, relativize, relativize_to - ) - token = tok.get_eol_as_token() - if token.comment is not None: - object.__setattr__(rdata, "rdcomment", token.comment) - return rdata - - -def from_wire_parser( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - parser: dns.wire.Parser, - origin: Optional[dns.name.Name] = None, -) -> Rdata: - """Build an rdata object from wire format - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_wire() class method is called - with the parameters to this function. - - *rdclass*, a ``dns.rdataclass.RdataClass`` or ``str``, the rdataclass. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdatatype. - - *parser*, a ``dns.wire.Parser``, the parser, which should be - restricted to the rdata length. - - *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, - then names will be relativized to this origin. - - Returns an instance of the chosen Rdata subclass. - """ - - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - cls = get_rdata_class(rdclass, rdtype) - with dns.exception.ExceptionWrapper(dns.exception.FormError): - return cls.from_wire_parser(rdclass, rdtype, parser, origin) - - -def from_wire( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - wire: bytes, - current: int, - rdlen: int, - origin: Optional[dns.name.Name] = None, -) -> Rdata: - """Build an rdata object from wire format - - This function attempts to dynamically load a class which - implements the specified rdata class and type. If there is no - class-and-type-specific implementation, the GenericRdata class - is used. - - Once a class is chosen, its from_wire() class method is called - with the parameters to this function. - - *rdclass*, an ``int``, the rdataclass. - - *rdtype*, an ``int``, the rdatatype. - - *wire*, a ``bytes``, the wire-format message. - - *current*, an ``int``, the offset in wire of the beginning of - the rdata. - - *rdlen*, an ``int``, the length of the wire-format rdata - - *origin*, a ``dns.name.Name`` (or ``None``). If not ``None``, - then names will be relativized to this origin. - - Returns an instance of the chosen Rdata subclass. - """ - parser = dns.wire.Parser(wire, current) - with parser.restrict_to(rdlen): - return from_wire_parser(rdclass, rdtype, parser, origin) - - -class RdatatypeExists(dns.exception.DNSException): - """DNS rdatatype already exists.""" - - supp_kwargs = {"rdclass", "rdtype"} - fmt = ( - "The rdata type with class {rdclass:d} and rdtype {rdtype:d} " - + "already exists." - ) - - -def register_type( - implementation: Any, - rdtype: int, - rdtype_text: str, - is_singleton: bool = False, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, -) -> None: - """Dynamically register a module to handle an rdatatype. - - *implementation*, a module implementing the type in the usual dnspython - way. - - *rdtype*, an ``int``, the rdatatype to register. - - *rdtype_text*, a ``str``, the textual form of the rdatatype. - - *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. - RRsets of the type can have only one member.) - - *rdclass*, the rdataclass of the type, or ``dns.rdataclass.ANY`` if - it applies to all classes. - """ - - rdtype = dns.rdatatype.RdataType.make(rdtype) - existing_cls = get_rdata_class(rdclass, rdtype) - if existing_cls != GenericRdata or dns.rdatatype.is_metatype(rdtype): - raise RdatatypeExists(rdclass=rdclass, rdtype=rdtype) - _rdata_classes[(rdclass, rdtype)] = getattr( - implementation, rdtype_text.replace("-", "_") - ) - dns.rdatatype.register_type(rdtype, rdtype_text, is_singleton) diff --git a/.venv/lib/python3.12/site-packages/dns/rdataclass.py b/.venv/lib/python3.12/site-packages/dns/rdataclass.py deleted file mode 100644 index 89b85a7..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdataclass.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Rdata Classes.""" - -import dns.enum -import dns.exception - - -class RdataClass(dns.enum.IntEnum): - """DNS Rdata Class""" - - RESERVED0 = 0 - IN = 1 - INTERNET = IN - CH = 3 - CHAOS = CH - HS = 4 - HESIOD = HS - NONE = 254 - ANY = 255 - - @classmethod - def _maximum(cls): - return 65535 - - @classmethod - def _short_name(cls): - return "class" - - @classmethod - def _prefix(cls): - return "CLASS" - - @classmethod - def _unknown_exception_class(cls): - return UnknownRdataclass - - -_metaclasses = {RdataClass.NONE, RdataClass.ANY} - - -class UnknownRdataclass(dns.exception.DNSException): - """A DNS class is unknown.""" - - -def from_text(text: str) -> RdataClass: - """Convert text into a DNS rdata class value. - - The input text can be a defined DNS RR class mnemonic or - instance of the DNS generic class syntax. - - For example, "IN" and "CLASS1" will both result in a value of 1. - - Raises ``dns.rdatatype.UnknownRdataclass`` if the class is unknown. - - Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. - - Returns a ``dns.rdataclass.RdataClass``. - """ - - return RdataClass.from_text(text) - - -def to_text(value: RdataClass) -> str: - """Convert a DNS rdata class value to text. - - If the value has a known mnemonic, it will be used, otherwise the - DNS generic class syntax will be used. - - Raises ``ValueError`` if the rdata class value is not >= 0 and <= 65535. - - Returns a ``str``. - """ - - return RdataClass.to_text(value) - - -def is_metaclass(rdclass: RdataClass) -> bool: - """True if the specified class is a metaclass. - - The currently defined metaclasses are ANY and NONE. - - *rdclass* is a ``dns.rdataclass.RdataClass``. - """ - - if rdclass in _metaclasses: - return True - return False - - -### BEGIN generated RdataClass constants - -RESERVED0 = RdataClass.RESERVED0 -IN = RdataClass.IN -INTERNET = RdataClass.INTERNET -CH = RdataClass.CH -CHAOS = RdataClass.CHAOS -HS = RdataClass.HS -HESIOD = RdataClass.HESIOD -NONE = RdataClass.NONE -ANY = RdataClass.ANY - -### END generated RdataClass constants diff --git a/.venv/lib/python3.12/site-packages/dns/rdataset.py b/.venv/lib/python3.12/site-packages/dns/rdataset.py deleted file mode 100644 index 8bff58d..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdataset.py +++ /dev/null @@ -1,516 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdatasets (an rdataset is a set of rdatas of a given type and class)""" - -import io -import random -import struct -from typing import Any, Collection, Dict, List, Optional, Union, cast - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.renderer -import dns.set -import dns.ttl - -# define SimpleSet here for backwards compatibility -SimpleSet = dns.set.Set - - -class DifferingCovers(dns.exception.DNSException): - """An attempt was made to add a DNS SIG/RRSIG whose covered type - is not the same as that of the other rdatas in the rdataset.""" - - -class IncompatibleTypes(dns.exception.DNSException): - """An attempt was made to add DNS RR data of an incompatible type.""" - - -class Rdataset(dns.set.Set): - """A DNS rdataset.""" - - __slots__ = ["rdclass", "rdtype", "covers", "ttl"] - - def __init__( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ttl: int = 0, - ): - """Create a new rdataset of the specified class and type. - - *rdclass*, a ``dns.rdataclass.RdataClass``, the rdataclass. - - *rdtype*, an ``dns.rdatatype.RdataType``, the rdatatype. - - *covers*, an ``dns.rdatatype.RdataType``, the covered rdatatype. - - *ttl*, an ``int``, the TTL. - """ - - super().__init__() - self.rdclass = rdclass - self.rdtype: dns.rdatatype.RdataType = rdtype - self.covers: dns.rdatatype.RdataType = covers - self.ttl = ttl - - def _clone(self): - obj = super()._clone() - obj.rdclass = self.rdclass - obj.rdtype = self.rdtype - obj.covers = self.covers - obj.ttl = self.ttl - return obj - - def update_ttl(self, ttl: int) -> None: - """Perform TTL minimization. - - Set the TTL of the rdataset to be the lesser of the set's current - TTL or the specified TTL. If the set contains no rdatas, set the TTL - to the specified TTL. - - *ttl*, an ``int`` or ``str``. - """ - ttl = dns.ttl.make(ttl) - if len(self) == 0: - self.ttl = ttl - elif ttl < self.ttl: - self.ttl = ttl - - def add( # pylint: disable=arguments-differ,arguments-renamed - self, rd: dns.rdata.Rdata, ttl: Optional[int] = None - ) -> None: - """Add the specified rdata to the rdataset. - - If the optional *ttl* parameter is supplied, then - ``self.update_ttl(ttl)`` will be called prior to adding the rdata. - - *rd*, a ``dns.rdata.Rdata``, the rdata - - *ttl*, an ``int``, the TTL. - - Raises ``dns.rdataset.IncompatibleTypes`` if the type and class - do not match the type and class of the rdataset. - - Raises ``dns.rdataset.DifferingCovers`` if the type is a signature - type and the covered type does not match that of the rdataset. - """ - - # - # If we're adding a signature, do some special handling to - # check that the signature covers the same type as the - # other rdatas in this rdataset. If this is the first rdata - # in the set, initialize the covers field. - # - if self.rdclass != rd.rdclass or self.rdtype != rd.rdtype: - raise IncompatibleTypes - if ttl is not None: - self.update_ttl(ttl) - if self.rdtype == dns.rdatatype.RRSIG or self.rdtype == dns.rdatatype.SIG: - covers = rd.covers() - if len(self) == 0 and self.covers == dns.rdatatype.NONE: - self.covers = covers - elif self.covers != covers: - raise DifferingCovers - if dns.rdatatype.is_singleton(rd.rdtype) and len(self) > 0: - self.clear() - super().add(rd) - - def union_update(self, other): - self.update_ttl(other.ttl) - super().union_update(other) - - def intersection_update(self, other): - self.update_ttl(other.ttl) - super().intersection_update(other) - - def update(self, other): - """Add all rdatas in other to self. - - *other*, a ``dns.rdataset.Rdataset``, the rdataset from which - to update. - """ - - self.update_ttl(other.ttl) - super().update(other) - - def _rdata_repr(self): - def maybe_truncate(s): - if len(s) > 100: - return s[:100] + "..." - return s - - return "[%s]" % ", ".join("<%s>" % maybe_truncate(str(rr)) for rr in self) - - def __repr__(self): - if self.covers == 0: - ctext = "" - else: - ctext = "(" + dns.rdatatype.to_text(self.covers) + ")" - return ( - "" - ) - - def __str__(self): - return self.to_text() - - def __eq__(self, other): - if not isinstance(other, Rdataset): - return False - if ( - self.rdclass != other.rdclass - or self.rdtype != other.rdtype - or self.covers != other.covers - ): - return False - return super().__eq__(other) - - def __ne__(self, other): - return not self.__eq__(other) - - def to_text( - self, - name: Optional[dns.name.Name] = None, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - override_rdclass: Optional[dns.rdataclass.RdataClass] = None, - want_comments: bool = False, - **kw: Dict[str, Any], - ) -> str: - """Convert the rdataset into DNS zone file format. - - See ``dns.name.Name.choose_relativity`` for more information - on how *origin* and *relativize* determine the way names - are emitted. - - Any additional keyword arguments are passed on to the rdata - ``to_text()`` method. - - *name*, a ``dns.name.Name``. If name is not ``None``, emit RRs with - *name* as the owner name. - - *origin*, a ``dns.name.Name`` or ``None``, the origin for relative - names. - - *relativize*, a ``bool``. If ``True``, names will be relativized - to *origin*. - - *override_rdclass*, a ``dns.rdataclass.RdataClass`` or ``None``. - If not ``None``, use this class instead of the Rdataset's class. - - *want_comments*, a ``bool``. If ``True``, emit comments for rdata - which have them. The default is ``False``. - """ - - if name is not None: - name = name.choose_relativity(origin, relativize) - ntext = str(name) - pad = " " - else: - ntext = "" - pad = "" - s = io.StringIO() - if override_rdclass is not None: - rdclass = override_rdclass - else: - rdclass = self.rdclass - if len(self) == 0: - # - # Empty rdatasets are used for the question section, and in - # some dynamic updates, so we don't need to print out the TTL - # (which is meaningless anyway). - # - s.write( - "{}{}{} {}\n".format( - ntext, - pad, - dns.rdataclass.to_text(rdclass), - dns.rdatatype.to_text(self.rdtype), - ) - ) - else: - for rd in self: - extra = "" - if want_comments: - if rd.rdcomment: - extra = f" ;{rd.rdcomment}" - s.write( - "%s%s%d %s %s %s%s\n" - % ( - ntext, - pad, - self.ttl, - dns.rdataclass.to_text(rdclass), - dns.rdatatype.to_text(self.rdtype), - rd.to_text(origin=origin, relativize=relativize, **kw), - extra, - ) - ) - # - # We strip off the final \n for the caller's convenience in printing - # - return s.getvalue()[:-1] - - def to_wire( - self, - name: dns.name.Name, - file: Any, - compress: Optional[dns.name.CompressType] = None, - origin: Optional[dns.name.Name] = None, - override_rdclass: Optional[dns.rdataclass.RdataClass] = None, - want_shuffle: bool = True, - ) -> int: - """Convert the rdataset to wire format. - - *name*, a ``dns.name.Name`` is the owner name to use. - - *file* is the file where the name is emitted (typically a - BytesIO file). - - *compress*, a ``dict``, is the compression table to use. If - ``None`` (the default), names will not be compressed. - - *origin* is a ``dns.name.Name`` or ``None``. If the name is - relative and origin is not ``None``, then *origin* will be appended - to it. - - *override_rdclass*, an ``int``, is used as the class instead of the - class of the rdataset. This is useful when rendering rdatasets - associated with dynamic updates. - - *want_shuffle*, a ``bool``. If ``True``, then the order of the - Rdatas within the Rdataset will be shuffled before rendering. - - Returns an ``int``, the number of records emitted. - """ - - if override_rdclass is not None: - rdclass = override_rdclass - want_shuffle = False - else: - rdclass = self.rdclass - if len(self) == 0: - name.to_wire(file, compress, origin) - file.write(struct.pack("!HHIH", self.rdtype, rdclass, 0, 0)) - return 1 - else: - l: Union[Rdataset, List[dns.rdata.Rdata]] - if want_shuffle: - l = list(self) - random.shuffle(l) - else: - l = self - for rd in l: - name.to_wire(file, compress, origin) - file.write(struct.pack("!HHI", self.rdtype, rdclass, self.ttl)) - with dns.renderer.prefixed_length(file, 2): - rd.to_wire(file, compress, origin) - return len(self) - - def match( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - ) -> bool: - """Returns ``True`` if this rdataset matches the specified class, - type, and covers. - """ - if self.rdclass == rdclass and self.rdtype == rdtype and self.covers == covers: - return True - return False - - def processing_order(self) -> List[dns.rdata.Rdata]: - """Return rdatas in a valid processing order according to the type's - specification. For example, MX records are in preference order from - lowest to highest preferences, with items of the same preference - shuffled. - - For types that do not define a processing order, the rdatas are - simply shuffled. - """ - if len(self) == 0: - return [] - else: - return self[0]._processing_order(iter(self)) - - -@dns.immutable.immutable -class ImmutableRdataset(Rdataset): # lgtm[py/missing-equals] - """An immutable DNS rdataset.""" - - _clone_class = Rdataset - - def __init__(self, rdataset: Rdataset): - """Create an immutable rdataset from the specified rdataset.""" - - super().__init__( - rdataset.rdclass, rdataset.rdtype, rdataset.covers, rdataset.ttl - ) - self.items = dns.immutable.Dict(rdataset.items) - - def update_ttl(self, ttl): - raise TypeError("immutable") - - def add(self, rd, ttl=None): - raise TypeError("immutable") - - def union_update(self, other): - raise TypeError("immutable") - - def intersection_update(self, other): - raise TypeError("immutable") - - def update(self, other): - raise TypeError("immutable") - - def __delitem__(self, i): - raise TypeError("immutable") - - # lgtm complains about these not raising ArithmeticError, but there is - # precedent for overrides of these methods in other classes to raise - # TypeError, and it seems like the better exception. - - def __ior__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def __iand__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def __iadd__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def __isub__(self, other): # lgtm[py/unexpected-raise-in-special-method] - raise TypeError("immutable") - - def clear(self): - raise TypeError("immutable") - - def __copy__(self): - return ImmutableRdataset(super().copy()) - - def copy(self): - return ImmutableRdataset(super().copy()) - - def union(self, other): - return ImmutableRdataset(super().union(other)) - - def intersection(self, other): - return ImmutableRdataset(super().intersection(other)) - - def difference(self, other): - return ImmutableRdataset(super().difference(other)) - - def symmetric_difference(self, other): - return ImmutableRdataset(super().symmetric_difference(other)) - - -def from_text_list( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - ttl: int, - text_rdatas: Collection[str], - idna_codec: Optional[dns.name.IDNACodec] = None, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, -) -> Rdataset: - """Create an rdataset with the specified class, type, and TTL, and with - the specified list of rdatas in text format. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use; if ``None``, the default IDNA 2003 - encoder/decoder is used. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - r = Rdataset(rdclass, rdtype) - r.update_ttl(ttl) - for t in text_rdatas: - rd = dns.rdata.from_text( - r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec - ) - r.add(rd) - return r - - -def from_text( - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - ttl: int, - *text_rdatas: Any, -) -> Rdataset: - """Create an rdataset with the specified class, type, and TTL, and with - the specified rdatas in text format. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - return from_text_list(rdclass, rdtype, ttl, cast(Collection[str], text_rdatas)) - - -def from_rdata_list(ttl: int, rdatas: Collection[dns.rdata.Rdata]) -> Rdataset: - """Create an rdataset with the specified TTL, and with - the specified list of rdata objects. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - if len(rdatas) == 0: - raise ValueError("rdata list must not be empty") - r = None - for rd in rdatas: - if r is None: - r = Rdataset(rd.rdclass, rd.rdtype) - r.update_ttl(ttl) - r.add(rd) - assert r is not None - return r - - -def from_rdata(ttl: int, *rdatas: Any) -> Rdataset: - """Create an rdataset with the specified TTL, and with - the specified rdata objects. - - Returns a ``dns.rdataset.Rdataset`` object. - """ - - return from_rdata_list(ttl, cast(Collection[dns.rdata.Rdata], rdatas)) diff --git a/.venv/lib/python3.12/site-packages/dns/rdatatype.py b/.venv/lib/python3.12/site-packages/dns/rdatatype.py deleted file mode 100644 index e6c5818..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdatatype.py +++ /dev/null @@ -1,332 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Rdata Types.""" - -from typing import Dict - -import dns.enum -import dns.exception - - -class RdataType(dns.enum.IntEnum): - """DNS Rdata Type""" - - TYPE0 = 0 - NONE = 0 - A = 1 - NS = 2 - MD = 3 - MF = 4 - CNAME = 5 - SOA = 6 - MB = 7 - MG = 8 - MR = 9 - NULL = 10 - WKS = 11 - PTR = 12 - HINFO = 13 - MINFO = 14 - MX = 15 - TXT = 16 - RP = 17 - AFSDB = 18 - X25 = 19 - ISDN = 20 - RT = 21 - NSAP = 22 - NSAP_PTR = 23 - SIG = 24 - KEY = 25 - PX = 26 - GPOS = 27 - AAAA = 28 - LOC = 29 - NXT = 30 - SRV = 33 - NAPTR = 35 - KX = 36 - CERT = 37 - A6 = 38 - DNAME = 39 - OPT = 41 - APL = 42 - DS = 43 - SSHFP = 44 - IPSECKEY = 45 - RRSIG = 46 - NSEC = 47 - DNSKEY = 48 - DHCID = 49 - NSEC3 = 50 - NSEC3PARAM = 51 - TLSA = 52 - SMIMEA = 53 - HIP = 55 - NINFO = 56 - CDS = 59 - CDNSKEY = 60 - OPENPGPKEY = 61 - CSYNC = 62 - ZONEMD = 63 - SVCB = 64 - HTTPS = 65 - SPF = 99 - UNSPEC = 103 - NID = 104 - L32 = 105 - L64 = 106 - LP = 107 - EUI48 = 108 - EUI64 = 109 - TKEY = 249 - TSIG = 250 - IXFR = 251 - AXFR = 252 - MAILB = 253 - MAILA = 254 - ANY = 255 - URI = 256 - CAA = 257 - AVC = 258 - AMTRELAY = 260 - TA = 32768 - DLV = 32769 - - @classmethod - def _maximum(cls): - return 65535 - - @classmethod - def _short_name(cls): - return "type" - - @classmethod - def _prefix(cls): - return "TYPE" - - @classmethod - def _extra_from_text(cls, text): - if text.find("-") >= 0: - try: - return cls[text.replace("-", "_")] - except KeyError: - pass - return _registered_by_text.get(text) - - @classmethod - def _extra_to_text(cls, value, current_text): - if current_text is None: - return _registered_by_value.get(value) - if current_text.find("_") >= 0: - return current_text.replace("_", "-") - return current_text - - @classmethod - def _unknown_exception_class(cls): - return UnknownRdatatype - - -_registered_by_text: Dict[str, RdataType] = {} -_registered_by_value: Dict[RdataType, str] = {} - -_metatypes = {RdataType.OPT} - -_singletons = { - RdataType.SOA, - RdataType.NXT, - RdataType.DNAME, - RdataType.NSEC, - RdataType.CNAME, -} - - -class UnknownRdatatype(dns.exception.DNSException): - """DNS resource record type is unknown.""" - - -def from_text(text: str) -> RdataType: - """Convert text into a DNS rdata type value. - - The input text can be a defined DNS RR type mnemonic or - instance of the DNS generic type syntax. - - For example, "NS" and "TYPE2" will both result in a value of 2. - - Raises ``dns.rdatatype.UnknownRdatatype`` if the type is unknown. - - Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. - - Returns a ``dns.rdatatype.RdataType``. - """ - - return RdataType.from_text(text) - - -def to_text(value: RdataType) -> str: - """Convert a DNS rdata type value to text. - - If the value has a known mnemonic, it will be used, otherwise the - DNS generic type syntax will be used. - - Raises ``ValueError`` if the rdata type value is not >= 0 and <= 65535. - - Returns a ``str``. - """ - - return RdataType.to_text(value) - - -def is_metatype(rdtype: RdataType) -> bool: - """True if the specified type is a metatype. - - *rdtype* is a ``dns.rdatatype.RdataType``. - - The currently defined metatypes are TKEY, TSIG, IXFR, AXFR, MAILA, - MAILB, ANY, and OPT. - - Returns a ``bool``. - """ - - return (256 > rdtype >= 128) or rdtype in _metatypes - - -def is_singleton(rdtype: RdataType) -> bool: - """Is the specified type a singleton type? - - Singleton types can only have a single rdata in an rdataset, or a single - RR in an RRset. - - The currently defined singleton types are CNAME, DNAME, NSEC, NXT, and - SOA. - - *rdtype* is an ``int``. - - Returns a ``bool``. - """ - - if rdtype in _singletons: - return True - return False - - -# pylint: disable=redefined-outer-name -def register_type( - rdtype: RdataType, rdtype_text: str, is_singleton: bool = False -) -> None: - """Dynamically register an rdatatype. - - *rdtype*, a ``dns.rdatatype.RdataType``, the rdatatype to register. - - *rdtype_text*, a ``str``, the textual form of the rdatatype. - - *is_singleton*, a ``bool``, indicating if the type is a singleton (i.e. - RRsets of the type can have only one member.) - """ - - _registered_by_text[rdtype_text] = rdtype - _registered_by_value[rdtype] = rdtype_text - if is_singleton: - _singletons.add(rdtype) - - -### BEGIN generated RdataType constants - -TYPE0 = RdataType.TYPE0 -NONE = RdataType.NONE -A = RdataType.A -NS = RdataType.NS -MD = RdataType.MD -MF = RdataType.MF -CNAME = RdataType.CNAME -SOA = RdataType.SOA -MB = RdataType.MB -MG = RdataType.MG -MR = RdataType.MR -NULL = RdataType.NULL -WKS = RdataType.WKS -PTR = RdataType.PTR -HINFO = RdataType.HINFO -MINFO = RdataType.MINFO -MX = RdataType.MX -TXT = RdataType.TXT -RP = RdataType.RP -AFSDB = RdataType.AFSDB -X25 = RdataType.X25 -ISDN = RdataType.ISDN -RT = RdataType.RT -NSAP = RdataType.NSAP -NSAP_PTR = RdataType.NSAP_PTR -SIG = RdataType.SIG -KEY = RdataType.KEY -PX = RdataType.PX -GPOS = RdataType.GPOS -AAAA = RdataType.AAAA -LOC = RdataType.LOC -NXT = RdataType.NXT -SRV = RdataType.SRV -NAPTR = RdataType.NAPTR -KX = RdataType.KX -CERT = RdataType.CERT -A6 = RdataType.A6 -DNAME = RdataType.DNAME -OPT = RdataType.OPT -APL = RdataType.APL -DS = RdataType.DS -SSHFP = RdataType.SSHFP -IPSECKEY = RdataType.IPSECKEY -RRSIG = RdataType.RRSIG -NSEC = RdataType.NSEC -DNSKEY = RdataType.DNSKEY -DHCID = RdataType.DHCID -NSEC3 = RdataType.NSEC3 -NSEC3PARAM = RdataType.NSEC3PARAM -TLSA = RdataType.TLSA -SMIMEA = RdataType.SMIMEA -HIP = RdataType.HIP -NINFO = RdataType.NINFO -CDS = RdataType.CDS -CDNSKEY = RdataType.CDNSKEY -OPENPGPKEY = RdataType.OPENPGPKEY -CSYNC = RdataType.CSYNC -ZONEMD = RdataType.ZONEMD -SVCB = RdataType.SVCB -HTTPS = RdataType.HTTPS -SPF = RdataType.SPF -UNSPEC = RdataType.UNSPEC -NID = RdataType.NID -L32 = RdataType.L32 -L64 = RdataType.L64 -LP = RdataType.LP -EUI48 = RdataType.EUI48 -EUI64 = RdataType.EUI64 -TKEY = RdataType.TKEY -TSIG = RdataType.TSIG -IXFR = RdataType.IXFR -AXFR = RdataType.AXFR -MAILB = RdataType.MAILB -MAILA = RdataType.MAILA -ANY = RdataType.ANY -URI = RdataType.URI -CAA = RdataType.CAA -AVC = RdataType.AVC -AMTRELAY = RdataType.AMTRELAY -TA = RdataType.TA -DLV = RdataType.DLV - -### END generated RdataType constants diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py deleted file mode 100644 index 06a3b97..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class AFSDB(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """AFSDB record""" - - # Use the property mechanism to make "subtype" an alias for the - # "preference" attribute, and "hostname" an alias for the "exchange" - # attribute. - # - # This lets us inherit the UncompressedMX implementation but lets - # the caller use appropriate attribute names for the rdata type. - # - # We probably lose some performance vs. a cut-and-paste - # implementation, but this way we don't copy code, and that's - # good. - - @property - def subtype(self): - "the AFSDB subtype" - return self.preference - - @property - def hostname(self): - "the AFSDB hostname" - return self.exchange diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py deleted file mode 100644 index ed2b072..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdtypes.util - - -class Relay(dns.rdtypes.util.Gateway): - name = "AMTRELAY relay" - - @property - def relay(self): - return self.gateway - - -@dns.immutable.immutable -class AMTRELAY(dns.rdata.Rdata): - """AMTRELAY record""" - - # see: RFC 8777 - - __slots__ = ["precedence", "discovery_optional", "relay_type", "relay"] - - def __init__( - self, rdclass, rdtype, precedence, discovery_optional, relay_type, relay - ): - super().__init__(rdclass, rdtype) - relay = Relay(relay_type, relay) - self.precedence = self._as_uint8(precedence) - self.discovery_optional = self._as_bool(discovery_optional) - self.relay_type = relay.type - self.relay = relay.relay - - def to_text(self, origin=None, relativize=True, **kw): - relay = Relay(self.relay_type, self.relay).to_text(origin, relativize) - return "%d %d %d %s" % ( - self.precedence, - self.discovery_optional, - self.relay_type, - relay, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - precedence = tok.get_uint8() - discovery_optional = tok.get_uint8() - if discovery_optional > 1: - raise dns.exception.SyntaxError("expecting 0 or 1") - discovery_optional = bool(discovery_optional) - relay_type = tok.get_uint8() - if relay_type > 0x7F: - raise dns.exception.SyntaxError("expecting an integer <= 127") - relay = Relay.from_text(relay_type, tok, origin, relativize, relativize_to) - return cls( - rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - relay_type = self.relay_type | (self.discovery_optional << 7) - header = struct.pack("!BB", self.precedence, relay_type) - file.write(header) - Relay(self.relay_type, self.relay).to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (precedence, relay_type) = parser.get_struct("!BB") - discovery_optional = bool(relay_type >> 7) - relay_type &= 0x7F - relay = Relay.from_wire_parser(relay_type, parser, origin) - return cls( - rdclass, rdtype, precedence, discovery_optional, relay_type, relay.relay - ) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py deleted file mode 100644 index a27ae2d..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2016 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class AVC(dns.rdtypes.txtbase.TXTBase): - """AVC record""" - - # See: IANA dns parameters for AVC diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py deleted file mode 100644 index 2e6a7e7..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class CAA(dns.rdata.Rdata): - """CAA (Certification Authority Authorization) record""" - - # see: RFC 6844 - - __slots__ = ["flags", "tag", "value"] - - def __init__(self, rdclass, rdtype, flags, tag, value): - super().__init__(rdclass, rdtype) - self.flags = self._as_uint8(flags) - self.tag = self._as_bytes(tag, True, 255) - if not tag.isalnum(): - raise ValueError("tag is not alphanumeric") - self.value = self._as_bytes(value) - - def to_text(self, origin=None, relativize=True, **kw): - return '%u %s "%s"' % ( - self.flags, - dns.rdata._escapify(self.tag), - dns.rdata._escapify(self.value), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - flags = tok.get_uint8() - tag = tok.get_string().encode() - value = tok.get_string().encode() - return cls(rdclass, rdtype, flags, tag, value) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!B", self.flags)) - l = len(self.tag) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.tag) - file.write(self.value) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - flags = parser.get_uint8() - tag = parser.get_counted_bytes() - value = parser.get_remaining() - return cls(rdclass, rdtype, flags, tag, value) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py deleted file mode 100644 index b613409..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dnskeybase # lgtm[py/import-and-import-from] - -# pylint: disable=unused-import -from dns.rdtypes.dnskeybase import ( # noqa: F401 lgtm[py/unused-import] - REVOKE, - SEP, - ZONE, -) - -# pylint: enable=unused-import - - -@dns.immutable.immutable -class CDNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): - """CDNSKEY record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py deleted file mode 100644 index 8312b97..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dsbase - - -@dns.immutable.immutable -class CDS(dns.rdtypes.dsbase.DSBase): - """CDS record""" - - _digest_length_by_type = { - **dns.rdtypes.dsbase.DSBase._digest_length_by_type, - 0: 1, # delete, RFC 8078 Sec. 4 (including Errata ID 5049) - } diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py deleted file mode 100644 index f369cc8..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.dnssectypes -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - -_ctype_by_value = { - 1: "PKIX", - 2: "SPKI", - 3: "PGP", - 4: "IPKIX", - 5: "ISPKI", - 6: "IPGP", - 7: "ACPKIX", - 8: "IACPKIX", - 253: "URI", - 254: "OID", -} - -_ctype_by_name = { - "PKIX": 1, - "SPKI": 2, - "PGP": 3, - "IPKIX": 4, - "ISPKI": 5, - "IPGP": 6, - "ACPKIX": 7, - "IACPKIX": 8, - "URI": 253, - "OID": 254, -} - - -def _ctype_from_text(what): - v = _ctype_by_name.get(what) - if v is not None: - return v - return int(what) - - -def _ctype_to_text(what): - v = _ctype_by_value.get(what) - if v is not None: - return v - return str(what) - - -@dns.immutable.immutable -class CERT(dns.rdata.Rdata): - """CERT record""" - - # see RFC 4398 - - __slots__ = ["certificate_type", "key_tag", "algorithm", "certificate"] - - def __init__( - self, rdclass, rdtype, certificate_type, key_tag, algorithm, certificate - ): - super().__init__(rdclass, rdtype) - self.certificate_type = self._as_uint16(certificate_type) - self.key_tag = self._as_uint16(key_tag) - self.algorithm = self._as_uint8(algorithm) - self.certificate = self._as_bytes(certificate) - - def to_text(self, origin=None, relativize=True, **kw): - certificate_type = _ctype_to_text(self.certificate_type) - return "%s %d %s %s" % ( - certificate_type, - self.key_tag, - dns.dnssectypes.Algorithm.to_text(self.algorithm), - dns.rdata._base64ify(self.certificate, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - certificate_type = _ctype_from_text(tok.get_string()) - key_tag = tok.get_uint16() - algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string()) - b64 = tok.concatenate_remaining_identifiers().encode() - certificate = base64.b64decode(b64) - return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - prefix = struct.pack( - "!HHB", self.certificate_type, self.key_tag, self.algorithm - ) - file.write(prefix) - file.write(self.certificate) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (certificate_type, key_tag, algorithm) = parser.get_struct("!HHB") - certificate = parser.get_remaining() - return cls(rdclass, rdtype, certificate_type, key_tag, algorithm, certificate) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py deleted file mode 100644 index 665e407..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class CNAME(dns.rdtypes.nsbase.NSBase): - """CNAME record - - Note: although CNAME is officially a singleton type, dnspython allows - non-singleton CNAME rdatasets because such sets have been commonly - used by BIND and other nameservers for load balancing.""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py deleted file mode 100644 index 2f972f6..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011, 2016 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdatatype -import dns.rdtypes.util - - -@dns.immutable.immutable -class Bitmap(dns.rdtypes.util.Bitmap): - type_name = "CSYNC" - - -@dns.immutable.immutable -class CSYNC(dns.rdata.Rdata): - """CSYNC record""" - - __slots__ = ["serial", "flags", "windows"] - - def __init__(self, rdclass, rdtype, serial, flags, windows): - super().__init__(rdclass, rdtype) - self.serial = self._as_uint32(serial) - self.flags = self._as_uint16(flags) - if not isinstance(windows, Bitmap): - windows = Bitmap(windows) - self.windows = tuple(windows.windows) - - def to_text(self, origin=None, relativize=True, **kw): - text = Bitmap(self.windows).to_text() - return "%d %d%s" % (self.serial, self.flags, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - serial = tok.get_uint32() - flags = tok.get_uint16() - bitmap = Bitmap.from_text(tok) - return cls(rdclass, rdtype, serial, flags, bitmap) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!IH", self.serial, self.flags)) - Bitmap(self.windows).to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (serial, flags) = parser.get_struct("!IH") - bitmap = Bitmap.from_wire_parser(parser) - return cls(rdclass, rdtype, serial, flags, bitmap) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py deleted file mode 100644 index 6c134f1..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dsbase - - -@dns.immutable.immutable -class DLV(dns.rdtypes.dsbase.DSBase): - """DLV record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py deleted file mode 100644 index bbf9186..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class DNAME(dns.rdtypes.nsbase.UncompressedNS): - """DNAME record""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.target.to_wire(file, None, origin, canonicalize) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py deleted file mode 100644 index 6d961a9..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dnskeybase # lgtm[py/import-and-import-from] - -# pylint: disable=unused-import -from dns.rdtypes.dnskeybase import ( # noqa: F401 lgtm[py/unused-import] - REVOKE, - SEP, - ZONE, -) - -# pylint: enable=unused-import - - -@dns.immutable.immutable -class DNSKEY(dns.rdtypes.dnskeybase.DNSKEYBase): - """DNSKEY record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py deleted file mode 100644 index 58b3108..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.dsbase - - -@dns.immutable.immutable -class DS(dns.rdtypes.dsbase.DSBase): - """DS record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py deleted file mode 100644 index c843be5..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2015 Red Hat, Inc. -# Author: Petr Spacek -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.euibase - - -@dns.immutable.immutable -class EUI48(dns.rdtypes.euibase.EUIBase): - """EUI48 record""" - - # see: rfc7043.txt - - byte_len = 6 # 0123456789ab (in hex) - text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py deleted file mode 100644 index f6d7e25..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2015 Red Hat, Inc. -# Author: Petr Spacek -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.euibase - - -@dns.immutable.immutable -class EUI64(dns.rdtypes.euibase.EUIBase): - """EUI64 record""" - - # see: rfc7043.txt - - byte_len = 8 # 0123456789abcdef (in hex) - text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab-cd-ef diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py deleted file mode 100644 index 312338f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -def _validate_float_string(what): - if len(what) == 0: - raise dns.exception.FormError - if what[0] == b"-"[0] or what[0] == b"+"[0]: - what = what[1:] - if what.isdigit(): - return - try: - (left, right) = what.split(b".") - except ValueError: - raise dns.exception.FormError - if left == b"" and right == b"": - raise dns.exception.FormError - if not left == b"" and not left.decode().isdigit(): - raise dns.exception.FormError - if not right == b"" and not right.decode().isdigit(): - raise dns.exception.FormError - - -@dns.immutable.immutable -class GPOS(dns.rdata.Rdata): - """GPOS record""" - - # see: RFC 1712 - - __slots__ = ["latitude", "longitude", "altitude"] - - def __init__(self, rdclass, rdtype, latitude, longitude, altitude): - super().__init__(rdclass, rdtype) - if isinstance(latitude, float) or isinstance(latitude, int): - latitude = str(latitude) - if isinstance(longitude, float) or isinstance(longitude, int): - longitude = str(longitude) - if isinstance(altitude, float) or isinstance(altitude, int): - altitude = str(altitude) - latitude = self._as_bytes(latitude, True, 255) - longitude = self._as_bytes(longitude, True, 255) - altitude = self._as_bytes(altitude, True, 255) - _validate_float_string(latitude) - _validate_float_string(longitude) - _validate_float_string(altitude) - self.latitude = latitude - self.longitude = longitude - self.altitude = altitude - flat = self.float_latitude - if flat < -90.0 or flat > 90.0: - raise dns.exception.FormError("bad latitude") - flong = self.float_longitude - if flong < -180.0 or flong > 180.0: - raise dns.exception.FormError("bad longitude") - - def to_text(self, origin=None, relativize=True, **kw): - return "{} {} {}".format( - self.latitude.decode(), self.longitude.decode(), self.altitude.decode() - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - latitude = tok.get_string() - longitude = tok.get_string() - altitude = tok.get_string() - return cls(rdclass, rdtype, latitude, longitude, altitude) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.latitude) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.latitude) - l = len(self.longitude) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.longitude) - l = len(self.altitude) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.altitude) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - latitude = parser.get_counted_bytes() - longitude = parser.get_counted_bytes() - altitude = parser.get_counted_bytes() - return cls(rdclass, rdtype, latitude, longitude, altitude) - - @property - def float_latitude(self): - "latitude as a floating point value" - return float(self.latitude) - - @property - def float_longitude(self): - "longitude as a floating point value" - return float(self.longitude) - - @property - def float_altitude(self): - "altitude as a floating point value" - return float(self.altitude) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py deleted file mode 100644 index c2c45de..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class HINFO(dns.rdata.Rdata): - """HINFO record""" - - # see: RFC 1035 - - __slots__ = ["cpu", "os"] - - def __init__(self, rdclass, rdtype, cpu, os): - super().__init__(rdclass, rdtype) - self.cpu = self._as_bytes(cpu, True, 255) - self.os = self._as_bytes(os, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - return '"{}" "{}"'.format( - dns.rdata._escapify(self.cpu), dns.rdata._escapify(self.os) - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - cpu = tok.get_string(max_length=255) - os = tok.get_string(max_length=255) - return cls(rdclass, rdtype, cpu, os) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.cpu) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.cpu) - l = len(self.os) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.os) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - cpu = parser.get_counted_bytes() - os = parser.get_counted_bytes() - return cls(rdclass, rdtype, cpu, os) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py deleted file mode 100644 index 9166913..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2010, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import binascii -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class HIP(dns.rdata.Rdata): - """HIP record""" - - # see: RFC 5205 - - __slots__ = ["hit", "algorithm", "key", "servers"] - - def __init__(self, rdclass, rdtype, hit, algorithm, key, servers): - super().__init__(rdclass, rdtype) - self.hit = self._as_bytes(hit, True, 255) - self.algorithm = self._as_uint8(algorithm) - self.key = self._as_bytes(key, True) - self.servers = self._as_tuple(servers, self._as_name) - - def to_text(self, origin=None, relativize=True, **kw): - hit = binascii.hexlify(self.hit).decode() - key = base64.b64encode(self.key).replace(b"\n", b"").decode() - text = "" - servers = [] - for server in self.servers: - servers.append(server.choose_relativity(origin, relativize)) - if len(servers) > 0: - text += " " + " ".join((x.to_unicode() for x in servers)) - return "%u %s %s%s" % (self.algorithm, hit, key, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - hit = binascii.unhexlify(tok.get_string().encode()) - key = base64.b64decode(tok.get_string().encode()) - servers = [] - for token in tok.get_remaining(): - server = tok.as_name(token, origin, relativize, relativize_to) - servers.append(server) - return cls(rdclass, rdtype, hit, algorithm, key, servers) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - lh = len(self.hit) - lk = len(self.key) - file.write(struct.pack("!BBH", lh, self.algorithm, lk)) - file.write(self.hit) - file.write(self.key) - for server in self.servers: - server.to_wire(file, None, origin, False) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (lh, algorithm, lk) = parser.get_struct("!BBH") - hit = parser.get_bytes(lh) - key = parser.get_bytes(lk) - servers = [] - while parser.remaining() > 0: - server = parser.get_name(origin) - servers.append(server) - return cls(rdclass, rdtype, hit, algorithm, key, servers) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py deleted file mode 100644 index fb01eab..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class ISDN(dns.rdata.Rdata): - """ISDN record""" - - # see: RFC 1183 - - __slots__ = ["address", "subaddress"] - - def __init__(self, rdclass, rdtype, address, subaddress): - super().__init__(rdclass, rdtype) - self.address = self._as_bytes(address, True, 255) - self.subaddress = self._as_bytes(subaddress, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - if self.subaddress: - return '"{}" "{}"'.format( - dns.rdata._escapify(self.address), dns.rdata._escapify(self.subaddress) - ) - else: - return '"%s"' % dns.rdata._escapify(self.address) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - tokens = tok.get_remaining(max_tokens=1) - if len(tokens) >= 1: - subaddress = tokens[0].unescape().value - else: - subaddress = "" - return cls(rdclass, rdtype, address, subaddress) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.address) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.address) - l = len(self.subaddress) - if l > 0: - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.subaddress) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_counted_bytes() - if parser.remaining() > 0: - subaddress = parser.get_counted_bytes() - else: - subaddress = b"" - return cls(rdclass, rdtype, address, subaddress) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py deleted file mode 100644 index 09804c2..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class L32(dns.rdata.Rdata): - """L32 record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "locator32"] - - def __init__(self, rdclass, rdtype, preference, locator32): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.locator32 = self._as_ipv4_address(locator32) - - def to_text(self, origin=None, relativize=True, **kw): - return f"{self.preference} {self.locator32}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - nodeid = tok.get_identifier() - return cls(rdclass, rdtype, preference, nodeid) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - file.write(dns.ipv4.inet_aton(self.locator32)) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - locator32 = parser.get_remaining() - return cls(rdclass, rdtype, preference, locator32) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py deleted file mode 100644 index fb76808..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdtypes.util - - -@dns.immutable.immutable -class L64(dns.rdata.Rdata): - """L64 record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "locator64"] - - def __init__(self, rdclass, rdtype, preference, locator64): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - if isinstance(locator64, bytes): - if len(locator64) != 8: - raise ValueError("invalid locator64") - self.locator64 = dns.rdata._hexify(locator64, 4, b":") - else: - dns.rdtypes.util.parse_formatted_hex(locator64, 4, 4, ":") - self.locator64 = locator64 - - def to_text(self, origin=None, relativize=True, **kw): - return f"{self.preference} {self.locator64}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - locator64 = tok.get_identifier() - return cls(rdclass, rdtype, preference, locator64) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - file.write(dns.rdtypes.util.parse_formatted_hex(self.locator64, 4, 4, ":")) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - locator64 = parser.get_remaining() - return cls(rdclass, rdtype, preference, locator64) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py deleted file mode 100644 index a36a2c1..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py +++ /dev/null @@ -1,354 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata - -_pows = tuple(10**i for i in range(0, 11)) - -# default values are in centimeters -_default_size = 100.0 -_default_hprec = 1000000.0 -_default_vprec = 1000.0 - -# for use by from_wire() -_MAX_LATITUDE = 0x80000000 + 90 * 3600000 -_MIN_LATITUDE = 0x80000000 - 90 * 3600000 -_MAX_LONGITUDE = 0x80000000 + 180 * 3600000 -_MIN_LONGITUDE = 0x80000000 - 180 * 3600000 - - -def _exponent_of(what, desc): - if what == 0: - return 0 - exp = None - for i, pow in enumerate(_pows): - if what < pow: - exp = i - 1 - break - if exp is None or exp < 0: - raise dns.exception.SyntaxError("%s value out of bounds" % desc) - return exp - - -def _float_to_tuple(what): - if what < 0: - sign = -1 - what *= -1 - else: - sign = 1 - what = round(what * 3600000) - degrees = int(what // 3600000) - what -= degrees * 3600000 - minutes = int(what // 60000) - what -= minutes * 60000 - seconds = int(what // 1000) - what -= int(seconds * 1000) - what = int(what) - return (degrees, minutes, seconds, what, sign) - - -def _tuple_to_float(what): - value = float(what[0]) - value += float(what[1]) / 60.0 - value += float(what[2]) / 3600.0 - value += float(what[3]) / 3600000.0 - return float(what[4]) * value - - -def _encode_size(what, desc): - what = int(what) - exponent = _exponent_of(what, desc) & 0xF - base = what // pow(10, exponent) & 0xF - return base * 16 + exponent - - -def _decode_size(what, desc): - exponent = what & 0x0F - if exponent > 9: - raise dns.exception.FormError("bad %s exponent" % desc) - base = (what & 0xF0) >> 4 - if base > 9: - raise dns.exception.FormError("bad %s base" % desc) - return base * pow(10, exponent) - - -def _check_coordinate_list(value, low, high): - if value[0] < low or value[0] > high: - raise ValueError(f"not in range [{low}, {high}]") - if value[1] < 0 or value[1] > 59: - raise ValueError("bad minutes value") - if value[2] < 0 or value[2] > 59: - raise ValueError("bad seconds value") - if value[3] < 0 or value[3] > 999: - raise ValueError("bad milliseconds value") - if value[4] != 1 and value[4] != -1: - raise ValueError("bad hemisphere value") - - -@dns.immutable.immutable -class LOC(dns.rdata.Rdata): - """LOC record""" - - # see: RFC 1876 - - __slots__ = [ - "latitude", - "longitude", - "altitude", - "size", - "horizontal_precision", - "vertical_precision", - ] - - def __init__( - self, - rdclass, - rdtype, - latitude, - longitude, - altitude, - size=_default_size, - hprec=_default_hprec, - vprec=_default_vprec, - ): - """Initialize a LOC record instance. - - The parameters I{latitude} and I{longitude} may be either a 4-tuple - of integers specifying (degrees, minutes, seconds, milliseconds), - or they may be floating point values specifying the number of - degrees. The other parameters are floats. Size, horizontal precision, - and vertical precision are specified in centimeters.""" - - super().__init__(rdclass, rdtype) - if isinstance(latitude, int): - latitude = float(latitude) - if isinstance(latitude, float): - latitude = _float_to_tuple(latitude) - _check_coordinate_list(latitude, -90, 90) - self.latitude = tuple(latitude) - if isinstance(longitude, int): - longitude = float(longitude) - if isinstance(longitude, float): - longitude = _float_to_tuple(longitude) - _check_coordinate_list(longitude, -180, 180) - self.longitude = tuple(longitude) - self.altitude = float(altitude) - self.size = float(size) - self.horizontal_precision = float(hprec) - self.vertical_precision = float(vprec) - - def to_text(self, origin=None, relativize=True, **kw): - if self.latitude[4] > 0: - lat_hemisphere = "N" - else: - lat_hemisphere = "S" - if self.longitude[4] > 0: - long_hemisphere = "E" - else: - long_hemisphere = "W" - text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( - self.latitude[0], - self.latitude[1], - self.latitude[2], - self.latitude[3], - lat_hemisphere, - self.longitude[0], - self.longitude[1], - self.longitude[2], - self.longitude[3], - long_hemisphere, - self.altitude / 100.0, - ) - - # do not print default values - if ( - self.size != _default_size - or self.horizontal_precision != _default_hprec - or self.vertical_precision != _default_vprec - ): - text += " {:0.2f}m {:0.2f}m {:0.2f}m".format( - self.size / 100.0, - self.horizontal_precision / 100.0, - self.vertical_precision / 100.0, - ) - return text - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - latitude = [0, 0, 0, 0, 1] - longitude = [0, 0, 0, 0, 1] - size = _default_size - hprec = _default_hprec - vprec = _default_vprec - - latitude[0] = tok.get_int() - t = tok.get_string() - if t.isdigit(): - latitude[1] = int(t) - t = tok.get_string() - if "." in t: - (seconds, milliseconds) = t.split(".") - if not seconds.isdigit(): - raise dns.exception.SyntaxError("bad latitude seconds value") - latitude[2] = int(seconds) - l = len(milliseconds) - if l == 0 or l > 3 or not milliseconds.isdigit(): - raise dns.exception.SyntaxError("bad latitude milliseconds value") - if l == 1: - m = 100 - elif l == 2: - m = 10 - else: - m = 1 - latitude[3] = m * int(milliseconds) - t = tok.get_string() - elif t.isdigit(): - latitude[2] = int(t) - t = tok.get_string() - if t == "S": - latitude[4] = -1 - elif t != "N": - raise dns.exception.SyntaxError("bad latitude hemisphere value") - - longitude[0] = tok.get_int() - t = tok.get_string() - if t.isdigit(): - longitude[1] = int(t) - t = tok.get_string() - if "." in t: - (seconds, milliseconds) = t.split(".") - if not seconds.isdigit(): - raise dns.exception.SyntaxError("bad longitude seconds value") - longitude[2] = int(seconds) - l = len(milliseconds) - if l == 0 or l > 3 or not milliseconds.isdigit(): - raise dns.exception.SyntaxError("bad longitude milliseconds value") - if l == 1: - m = 100 - elif l == 2: - m = 10 - else: - m = 1 - longitude[3] = m * int(milliseconds) - t = tok.get_string() - elif t.isdigit(): - longitude[2] = int(t) - t = tok.get_string() - if t == "W": - longitude[4] = -1 - elif t != "E": - raise dns.exception.SyntaxError("bad longitude hemisphere value") - - t = tok.get_string() - if t[-1] == "m": - t = t[0:-1] - altitude = float(t) * 100.0 # m -> cm - - tokens = tok.get_remaining(max_tokens=3) - if len(tokens) >= 1: - value = tokens[0].unescape().value - if value[-1] == "m": - value = value[0:-1] - size = float(value) * 100.0 # m -> cm - if len(tokens) >= 2: - value = tokens[1].unescape().value - if value[-1] == "m": - value = value[0:-1] - hprec = float(value) * 100.0 # m -> cm - if len(tokens) >= 3: - value = tokens[2].unescape().value - if value[-1] == "m": - value = value[0:-1] - vprec = float(value) * 100.0 # m -> cm - - # Try encoding these now so we raise if they are bad - _encode_size(size, "size") - _encode_size(hprec, "horizontal precision") - _encode_size(vprec, "vertical precision") - - return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - milliseconds = ( - self.latitude[0] * 3600000 - + self.latitude[1] * 60000 - + self.latitude[2] * 1000 - + self.latitude[3] - ) * self.latitude[4] - latitude = 0x80000000 + milliseconds - milliseconds = ( - self.longitude[0] * 3600000 - + self.longitude[1] * 60000 - + self.longitude[2] * 1000 - + self.longitude[3] - ) * self.longitude[4] - longitude = 0x80000000 + milliseconds - altitude = int(self.altitude) + 10000000 - size = _encode_size(self.size, "size") - hprec = _encode_size(self.horizontal_precision, "horizontal precision") - vprec = _encode_size(self.vertical_precision, "vertical precision") - wire = struct.pack( - "!BBBBIII", 0, size, hprec, vprec, latitude, longitude, altitude - ) - file.write(wire) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - ( - version, - size, - hprec, - vprec, - latitude, - longitude, - altitude, - ) = parser.get_struct("!BBBBIII") - if version != 0: - raise dns.exception.FormError("LOC version not zero") - if latitude < _MIN_LATITUDE or latitude > _MAX_LATITUDE: - raise dns.exception.FormError("bad latitude") - if latitude > 0x80000000: - latitude = (latitude - 0x80000000) / 3600000 - else: - latitude = -1 * (0x80000000 - latitude) / 3600000 - if longitude < _MIN_LONGITUDE or longitude > _MAX_LONGITUDE: - raise dns.exception.FormError("bad longitude") - if longitude > 0x80000000: - longitude = (longitude - 0x80000000) / 3600000 - else: - longitude = -1 * (0x80000000 - longitude) / 3600000 - altitude = float(altitude) - 10000000.0 - size = _decode_size(size, "size") - hprec = _decode_size(hprec, "horizontal precision") - vprec = _decode_size(vprec, "vertical precision") - return cls(rdclass, rdtype, latitude, longitude, altitude, size, hprec, vprec) - - @property - def float_latitude(self): - "latitude as a floating point value" - return _tuple_to_float(self.latitude) - - @property - def float_longitude(self): - "longitude as a floating point value" - return _tuple_to_float(self.longitude) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py deleted file mode 100644 index 312663f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class LP(dns.rdata.Rdata): - """LP record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "fqdn"] - - def __init__(self, rdclass, rdtype, preference, fqdn): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.fqdn = self._as_name(fqdn) - - def to_text(self, origin=None, relativize=True, **kw): - fqdn = self.fqdn.choose_relativity(origin, relativize) - return "%d %s" % (self.preference, fqdn) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - fqdn = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, preference, fqdn) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - self.fqdn.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - fqdn = parser.get_name(origin) - return cls(rdclass, rdtype, preference, fqdn) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py deleted file mode 100644 index 0c300c5..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class MX(dns.rdtypes.mxbase.MXBase): - """MX record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py deleted file mode 100644 index 2f64917..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import struct - -import dns.immutable -import dns.rdtypes.util - - -@dns.immutable.immutable -class NID(dns.rdata.Rdata): - """NID record""" - - # see: rfc6742.txt - - __slots__ = ["preference", "nodeid"] - - def __init__(self, rdclass, rdtype, preference, nodeid): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - if isinstance(nodeid, bytes): - if len(nodeid) != 8: - raise ValueError("invalid nodeid") - self.nodeid = dns.rdata._hexify(nodeid, 4, b":") - else: - dns.rdtypes.util.parse_formatted_hex(nodeid, 4, 4, ":") - self.nodeid = nodeid - - def to_text(self, origin=None, relativize=True, **kw): - return f"{self.preference} {self.nodeid}" - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - nodeid = tok.get_identifier() - return cls(rdclass, rdtype, preference, nodeid) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.preference)) - file.write(dns.rdtypes.util.parse_formatted_hex(self.nodeid, 4, 4, ":")) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - nodeid = parser.get_remaining() - return cls(rdclass, rdtype, preference, nodeid) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py deleted file mode 100644 index b177bdd..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class NINFO(dns.rdtypes.txtbase.TXTBase): - """NINFO record""" - - # see: draft-reid-dnsext-zs-01 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py deleted file mode 100644 index c3f34ce..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class NS(dns.rdtypes.nsbase.NSBase): - """NS record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py deleted file mode 100644 index 340525a..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdatatype -import dns.rdtypes.util - - -@dns.immutable.immutable -class Bitmap(dns.rdtypes.util.Bitmap): - type_name = "NSEC" - - -@dns.immutable.immutable -class NSEC(dns.rdata.Rdata): - """NSEC record""" - - __slots__ = ["next", "windows"] - - def __init__(self, rdclass, rdtype, next, windows): - super().__init__(rdclass, rdtype) - self.next = self._as_name(next) - if not isinstance(windows, Bitmap): - windows = Bitmap(windows) - self.windows = tuple(windows.windows) - - def to_text(self, origin=None, relativize=True, **kw): - next = self.next.choose_relativity(origin, relativize) - text = Bitmap(self.windows).to_text() - return "{}{}".format(next, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - next = tok.get_name(origin, relativize, relativize_to) - windows = Bitmap.from_text(tok) - return cls(rdclass, rdtype, next, windows) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - # Note that NSEC downcasing, originally mandated by RFC 4034 - # section 6.2 was removed by RFC 6840 section 5.1. - self.next.to_wire(file, None, origin, False) - Bitmap(self.windows).to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - next = parser.get_name(origin) - bitmap = Bitmap.from_wire_parser(parser) - return cls(rdclass, rdtype, next, bitmap) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py deleted file mode 100644 index d71302b..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import binascii -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.rdatatype -import dns.rdtypes.util - -b32_hex_to_normal = bytes.maketrans( - b"0123456789ABCDEFGHIJKLMNOPQRSTUV", b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" -) -b32_normal_to_hex = bytes.maketrans( - b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", b"0123456789ABCDEFGHIJKLMNOPQRSTUV" -) - -# hash algorithm constants -SHA1 = 1 - -# flag constants -OPTOUT = 1 - - -@dns.immutable.immutable -class Bitmap(dns.rdtypes.util.Bitmap): - type_name = "NSEC3" - - -@dns.immutable.immutable -class NSEC3(dns.rdata.Rdata): - """NSEC3 record""" - - __slots__ = ["algorithm", "flags", "iterations", "salt", "next", "windows"] - - def __init__( - self, rdclass, rdtype, algorithm, flags, iterations, salt, next, windows - ): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_uint8(algorithm) - self.flags = self._as_uint8(flags) - self.iterations = self._as_uint16(iterations) - self.salt = self._as_bytes(salt, True, 255) - self.next = self._as_bytes(next, True, 255) - if not isinstance(windows, Bitmap): - windows = Bitmap(windows) - self.windows = tuple(windows.windows) - - def _next_text(self): - next = base64.b32encode(self.next).translate(b32_normal_to_hex).lower().decode() - next = next.rstrip("=") - return next - - def to_text(self, origin=None, relativize=True, **kw): - next = self._next_text() - if self.salt == b"": - salt = "-" - else: - salt = binascii.hexlify(self.salt).decode() - text = Bitmap(self.windows).to_text() - return "%u %u %u %s %s%s" % ( - self.algorithm, - self.flags, - self.iterations, - salt, - next, - text, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - flags = tok.get_uint8() - iterations = tok.get_uint16() - salt = tok.get_string() - if salt == "-": - salt = b"" - else: - salt = binascii.unhexlify(salt.encode("ascii")) - next = tok.get_string().encode("ascii").upper().translate(b32_hex_to_normal) - if next.endswith(b"="): - raise binascii.Error("Incorrect padding") - if len(next) % 8 != 0: - next += b"=" * (8 - len(next) % 8) - next = base64.b32decode(next) - bitmap = Bitmap.from_text(tok) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.salt) - file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) - file.write(self.salt) - l = len(self.next) - file.write(struct.pack("!B", l)) - file.write(self.next) - Bitmap(self.windows).to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (algorithm, flags, iterations) = parser.get_struct("!BBH") - salt = parser.get_counted_bytes() - next = parser.get_counted_bytes() - bitmap = Bitmap.from_wire_parser(parser) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt, next, bitmap) - - def next_name(self, origin=None): - return dns.name.from_text(self._next_text(), origin) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py deleted file mode 100644 index d1e62eb..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.exception -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class NSEC3PARAM(dns.rdata.Rdata): - """NSEC3PARAM record""" - - __slots__ = ["algorithm", "flags", "iterations", "salt"] - - def __init__(self, rdclass, rdtype, algorithm, flags, iterations, salt): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_uint8(algorithm) - self.flags = self._as_uint8(flags) - self.iterations = self._as_uint16(iterations) - self.salt = self._as_bytes(salt, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - if self.salt == b"": - salt = "-" - else: - salt = binascii.hexlify(self.salt).decode() - return "%u %u %u %s" % (self.algorithm, self.flags, self.iterations, salt) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - flags = tok.get_uint8() - iterations = tok.get_uint16() - salt = tok.get_string() - if salt == "-": - salt = "" - else: - salt = binascii.unhexlify(salt.encode()) - return cls(rdclass, rdtype, algorithm, flags, iterations, salt) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.salt) - file.write(struct.pack("!BBHB", self.algorithm, self.flags, self.iterations, l)) - file.write(self.salt) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (algorithm, flags, iterations) = parser.get_struct("!BBH") - salt = parser.get_counted_bytes() - return cls(rdclass, rdtype, algorithm, flags, iterations, salt) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py deleted file mode 100644 index 4d7a4b6..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2016 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class OPENPGPKEY(dns.rdata.Rdata): - """OPENPGPKEY record""" - - # see: RFC 7929 - - def __init__(self, rdclass, rdtype, key): - super().__init__(rdclass, rdtype) - self.key = self._as_bytes(key) - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._base64ify(self.key, chunksize=None, **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - b64 = tok.concatenate_remaining_identifiers().encode() - key = base64.b64decode(b64) - return cls(rdclass, rdtype, key) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.key) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - key = parser.get_remaining() - return cls(rdclass, rdtype, key) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py deleted file mode 100644 index d343dfa..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.edns -import dns.exception -import dns.immutable -import dns.rdata - -# We don't implement from_text, and that's ok. -# pylint: disable=abstract-method - - -@dns.immutable.immutable -class OPT(dns.rdata.Rdata): - """OPT record""" - - __slots__ = ["options"] - - def __init__(self, rdclass, rdtype, options): - """Initialize an OPT rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata, - which is also the payload size. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - - *options*, a tuple of ``bytes`` - """ - - super().__init__(rdclass, rdtype) - - def as_option(option): - if not isinstance(option, dns.edns.Option): - raise ValueError("option is not a dns.edns.option") - return option - - self.options = self._as_tuple(options, as_option) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - for opt in self.options: - owire = opt.to_wire() - file.write(struct.pack("!HH", opt.otype, len(owire))) - file.write(owire) - - def to_text(self, origin=None, relativize=True, **kw): - return " ".join(opt.to_text() for opt in self.options) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - options = [] - while parser.remaining() > 0: - (otype, olen) = parser.get_struct("!HH") - with parser.restrict_to(olen): - opt = dns.edns.option_from_wire_parser(otype, parser) - options.append(opt) - return cls(rdclass, rdtype, options) - - @property - def payload(self): - "payload size" - return self.rdclass diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py deleted file mode 100644 index 98c3616..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class PTR(dns.rdtypes.nsbase.NSBase): - """PTR record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py deleted file mode 100644 index 9b74549..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata - - -@dns.immutable.immutable -class RP(dns.rdata.Rdata): - """RP record""" - - # see: RFC 1183 - - __slots__ = ["mbox", "txt"] - - def __init__(self, rdclass, rdtype, mbox, txt): - super().__init__(rdclass, rdtype) - self.mbox = self._as_name(mbox) - self.txt = self._as_name(txt) - - def to_text(self, origin=None, relativize=True, **kw): - mbox = self.mbox.choose_relativity(origin, relativize) - txt = self.txt.choose_relativity(origin, relativize) - return "{} {}".format(str(mbox), str(txt)) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - mbox = tok.get_name(origin, relativize, relativize_to) - txt = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, mbox, txt) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.mbox.to_wire(file, None, origin, canonicalize) - self.txt.to_wire(file, None, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - mbox = parser.get_name(origin) - txt = parser.get_name(origin) - return cls(rdclass, rdtype, mbox, txt) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py deleted file mode 100644 index 8beb423..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py +++ /dev/null @@ -1,157 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import calendar -import struct -import time - -import dns.dnssectypes -import dns.exception -import dns.immutable -import dns.rdata -import dns.rdatatype - - -class BadSigTime(dns.exception.DNSException): - """Time in DNS SIG or RRSIG resource record cannot be parsed.""" - - -def sigtime_to_posixtime(what): - if len(what) <= 10 and what.isdigit(): - return int(what) - if len(what) != 14: - raise BadSigTime - year = int(what[0:4]) - month = int(what[4:6]) - day = int(what[6:8]) - hour = int(what[8:10]) - minute = int(what[10:12]) - second = int(what[12:14]) - return calendar.timegm((year, month, day, hour, minute, second, 0, 0, 0)) - - -def posixtime_to_sigtime(what): - return time.strftime("%Y%m%d%H%M%S", time.gmtime(what)) - - -@dns.immutable.immutable -class RRSIG(dns.rdata.Rdata): - """RRSIG record""" - - __slots__ = [ - "type_covered", - "algorithm", - "labels", - "original_ttl", - "expiration", - "inception", - "key_tag", - "signer", - "signature", - ] - - def __init__( - self, - rdclass, - rdtype, - type_covered, - algorithm, - labels, - original_ttl, - expiration, - inception, - key_tag, - signer, - signature, - ): - super().__init__(rdclass, rdtype) - self.type_covered = self._as_rdatatype(type_covered) - self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) - self.labels = self._as_uint8(labels) - self.original_ttl = self._as_ttl(original_ttl) - self.expiration = self._as_uint32(expiration) - self.inception = self._as_uint32(inception) - self.key_tag = self._as_uint16(key_tag) - self.signer = self._as_name(signer) - self.signature = self._as_bytes(signature) - - def covers(self): - return self.type_covered - - def to_text(self, origin=None, relativize=True, **kw): - return "%s %d %d %d %s %s %d %s %s" % ( - dns.rdatatype.to_text(self.type_covered), - self.algorithm, - self.labels, - self.original_ttl, - posixtime_to_sigtime(self.expiration), - posixtime_to_sigtime(self.inception), - self.key_tag, - self.signer.choose_relativity(origin, relativize), - dns.rdata._base64ify(self.signature, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - type_covered = dns.rdatatype.from_text(tok.get_string()) - algorithm = dns.dnssectypes.Algorithm.from_text(tok.get_string()) - labels = tok.get_int() - original_ttl = tok.get_ttl() - expiration = sigtime_to_posixtime(tok.get_string()) - inception = sigtime_to_posixtime(tok.get_string()) - key_tag = tok.get_int() - signer = tok.get_name(origin, relativize, relativize_to) - b64 = tok.concatenate_remaining_identifiers().encode() - signature = base64.b64decode(b64) - return cls( - rdclass, - rdtype, - type_covered, - algorithm, - labels, - original_ttl, - expiration, - inception, - key_tag, - signer, - signature, - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack( - "!HBBIIIH", - self.type_covered, - self.algorithm, - self.labels, - self.original_ttl, - self.expiration, - self.inception, - self.key_tag, - ) - file.write(header) - self.signer.to_wire(file, None, origin, canonicalize) - file.write(self.signature) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!HBBIIIH") - signer = parser.get_name(origin) - signature = parser.get_remaining() - return cls(rdclass, rdtype, *header, signer, signature) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py deleted file mode 100644 index 5a4d45c..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class RT(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """RT record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py deleted file mode 100644 index 55d87bf..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.tlsabase - - -@dns.immutable.immutable -class SMIMEA(dns.rdtypes.tlsabase.TLSABase): - """SMIMEA record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py deleted file mode 100644 index 09aa832..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata - - -@dns.immutable.immutable -class SOA(dns.rdata.Rdata): - """SOA record""" - - # see: RFC 1035 - - __slots__ = ["mname", "rname", "serial", "refresh", "retry", "expire", "minimum"] - - def __init__( - self, rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum - ): - super().__init__(rdclass, rdtype) - self.mname = self._as_name(mname) - self.rname = self._as_name(rname) - self.serial = self._as_uint32(serial) - self.refresh = self._as_ttl(refresh) - self.retry = self._as_ttl(retry) - self.expire = self._as_ttl(expire) - self.minimum = self._as_ttl(minimum) - - def to_text(self, origin=None, relativize=True, **kw): - mname = self.mname.choose_relativity(origin, relativize) - rname = self.rname.choose_relativity(origin, relativize) - return "%s %s %d %d %d %d %d" % ( - mname, - rname, - self.serial, - self.refresh, - self.retry, - self.expire, - self.minimum, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - mname = tok.get_name(origin, relativize, relativize_to) - rname = tok.get_name(origin, relativize, relativize_to) - serial = tok.get_uint32() - refresh = tok.get_ttl() - retry = tok.get_ttl() - expire = tok.get_ttl() - minimum = tok.get_ttl() - return cls( - rdclass, rdtype, mname, rname, serial, refresh, retry, expire, minimum - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.mname.to_wire(file, compress, origin, canonicalize) - self.rname.to_wire(file, compress, origin, canonicalize) - five_ints = struct.pack( - "!IIIII", self.serial, self.refresh, self.retry, self.expire, self.minimum - ) - file.write(five_ints) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - mname = parser.get_name(origin) - rname = parser.get_name(origin) - return cls(rdclass, rdtype, mname, rname, *parser.get_struct("!IIIII")) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py deleted file mode 100644 index 1df3b70..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class SPF(dns.rdtypes.txtbase.TXTBase): - """SPF record""" - - # see: RFC 4408 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py deleted file mode 100644 index d2c4b07..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class SSHFP(dns.rdata.Rdata): - """SSHFP record""" - - # See RFC 4255 - - __slots__ = ["algorithm", "fp_type", "fingerprint"] - - def __init__(self, rdclass, rdtype, algorithm, fp_type, fingerprint): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_uint8(algorithm) - self.fp_type = self._as_uint8(fp_type) - self.fingerprint = self._as_bytes(fingerprint, True) - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %s" % ( - self.algorithm, - self.fp_type, - dns.rdata._hexify(self.fingerprint, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_uint8() - fp_type = tok.get_uint8() - fingerprint = tok.concatenate_remaining_identifiers().encode() - fingerprint = binascii.unhexlify(fingerprint) - return cls(rdclass, rdtype, algorithm, fp_type, fingerprint) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!BB", self.algorithm, self.fp_type) - file.write(header) - file.write(self.fingerprint) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("BB") - fingerprint = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], fingerprint) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py deleted file mode 100644 index 5b490b8..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.exception -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class TKEY(dns.rdata.Rdata): - """TKEY Record""" - - __slots__ = [ - "algorithm", - "inception", - "expiration", - "mode", - "error", - "key", - "other", - ] - - def __init__( - self, - rdclass, - rdtype, - algorithm, - inception, - expiration, - mode, - error, - key, - other=b"", - ): - super().__init__(rdclass, rdtype) - self.algorithm = self._as_name(algorithm) - self.inception = self._as_uint32(inception) - self.expiration = self._as_uint32(expiration) - self.mode = self._as_uint16(mode) - self.error = self._as_uint16(error) - self.key = self._as_bytes(key) - self.other = self._as_bytes(other) - - def to_text(self, origin=None, relativize=True, **kw): - _algorithm = self.algorithm.choose_relativity(origin, relativize) - text = "%s %u %u %u %u %s" % ( - str(_algorithm), - self.inception, - self.expiration, - self.mode, - self.error, - dns.rdata._base64ify(self.key, 0), - ) - if len(self.other) > 0: - text += " %s" % (dns.rdata._base64ify(self.other, 0)) - - return text - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_name(relativize=False) - inception = tok.get_uint32() - expiration = tok.get_uint32() - mode = tok.get_uint16() - error = tok.get_uint16() - key_b64 = tok.get_string().encode() - key = base64.b64decode(key_b64) - other_b64 = tok.concatenate_remaining_identifiers(True).encode() - other = base64.b64decode(other_b64) - - return cls( - rdclass, rdtype, algorithm, inception, expiration, mode, error, key, other - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.algorithm.to_wire(file, compress, origin) - file.write( - struct.pack("!IIHH", self.inception, self.expiration, self.mode, self.error) - ) - file.write(struct.pack("!H", len(self.key))) - file.write(self.key) - file.write(struct.pack("!H", len(self.other))) - if len(self.other) > 0: - file.write(self.other) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - algorithm = parser.get_name(origin) - inception, expiration, mode, error = parser.get_struct("!IIHH") - key = parser.get_counted_bytes(2) - other = parser.get_counted_bytes(2) - - return cls( - rdclass, rdtype, algorithm, inception, expiration, mode, error, key, other - ) - - # Constants for the mode field - from RFC 2930: - # 2.5 The Mode Field - # - # The mode field specifies the general scheme for key agreement or - # the purpose of the TKEY DNS message. Servers and resolvers - # supporting this specification MUST implement the Diffie-Hellman key - # agreement mode and the key deletion mode for queries. All other - # modes are OPTIONAL. A server supporting TKEY that receives a TKEY - # request with a mode it does not support returns the BADMODE error. - # The following values of the Mode octet are defined, available, or - # reserved: - # - # Value Description - # ----- ----------- - # 0 - reserved, see section 7 - # 1 server assignment - # 2 Diffie-Hellman exchange - # 3 GSS-API negotiation - # 4 resolver assignment - # 5 key deletion - # 6-65534 - available, see section 7 - # 65535 - reserved, see section 7 - SERVER_ASSIGNMENT = 1 - DIFFIE_HELLMAN_EXCHANGE = 2 - GSSAPI_NEGOTIATION = 3 - RESOLVER_ASSIGNMENT = 4 - KEY_DELETION = 5 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py deleted file mode 100644 index 4dffc55..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.tlsabase - - -@dns.immutable.immutable -class TLSA(dns.rdtypes.tlsabase.TLSABase): - """TLSA record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py deleted file mode 100644 index 7942382..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.exception -import dns.immutable -import dns.rcode -import dns.rdata - - -@dns.immutable.immutable -class TSIG(dns.rdata.Rdata): - """TSIG record""" - - __slots__ = [ - "algorithm", - "time_signed", - "fudge", - "mac", - "original_id", - "error", - "other", - ] - - def __init__( - self, - rdclass, - rdtype, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ): - """Initialize a TSIG rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - - *algorithm*, a ``dns.name.Name``. - - *time_signed*, an ``int``. - - *fudge*, an ``int`. - - *mac*, a ``bytes`` - - *original_id*, an ``int`` - - *error*, an ``int`` - - *other*, a ``bytes`` - """ - - super().__init__(rdclass, rdtype) - self.algorithm = self._as_name(algorithm) - self.time_signed = self._as_uint48(time_signed) - self.fudge = self._as_uint16(fudge) - self.mac = self._as_bytes(mac) - self.original_id = self._as_uint16(original_id) - self.error = dns.rcode.Rcode.make(error) - self.other = self._as_bytes(other) - - def to_text(self, origin=None, relativize=True, **kw): - algorithm = self.algorithm.choose_relativity(origin, relativize) - error = dns.rcode.to_text(self.error, True) - text = ( - f"{algorithm} {self.time_signed} {self.fudge} " - + f"{len(self.mac)} {dns.rdata._base64ify(self.mac, 0)} " - + f"{self.original_id} {error} {len(self.other)}" - ) - if self.other: - text += f" {dns.rdata._base64ify(self.other, 0)}" - return text - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - algorithm = tok.get_name(relativize=False) - time_signed = tok.get_uint48() - fudge = tok.get_uint16() - mac_len = tok.get_uint16() - mac = base64.b64decode(tok.get_string()) - if len(mac) != mac_len: - raise SyntaxError("invalid MAC") - original_id = tok.get_uint16() - error = dns.rcode.from_text(tok.get_string()) - other_len = tok.get_uint16() - if other_len > 0: - other = base64.b64decode(tok.get_string()) - if len(other) != other_len: - raise SyntaxError("invalid other data") - else: - other = b"" - return cls( - rdclass, - rdtype, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.algorithm.to_wire(file, None, origin, False) - file.write( - struct.pack( - "!HIHH", - (self.time_signed >> 32) & 0xFFFF, - self.time_signed & 0xFFFFFFFF, - self.fudge, - len(self.mac), - ) - ) - file.write(self.mac) - file.write(struct.pack("!HHH", self.original_id, self.error, len(self.other))) - file.write(self.other) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - algorithm = parser.get_name() - time_signed = parser.get_uint48() - fudge = parser.get_uint16() - mac = parser.get_counted_bytes(2) - (original_id, error) = parser.get_struct("!HH") - other = parser.get_counted_bytes(2) - return cls( - rdclass, - rdtype, - algorithm, - time_signed, - fudge, - mac, - original_id, - error, - other, - ) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py deleted file mode 100644 index 6d4dae2..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.txtbase - - -@dns.immutable.immutable -class TXT(dns.rdtypes.txtbase.TXTBase): - """TXT record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py deleted file mode 100644 index 2efbb30..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# Copyright (C) 2015 Red Hat, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class URI(dns.rdata.Rdata): - """URI record""" - - # see RFC 7553 - - __slots__ = ["priority", "weight", "target"] - - def __init__(self, rdclass, rdtype, priority, weight, target): - super().__init__(rdclass, rdtype) - self.priority = self._as_uint16(priority) - self.weight = self._as_uint16(weight) - self.target = self._as_bytes(target, True) - if len(self.target) == 0: - raise dns.exception.SyntaxError("URI target cannot be empty") - - def to_text(self, origin=None, relativize=True, **kw): - return '%d %d "%s"' % (self.priority, self.weight, self.target.decode()) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - priority = tok.get_uint16() - weight = tok.get_uint16() - target = tok.get().unescape() - if not (target.is_quoted_string() or target.is_identifier()): - raise dns.exception.SyntaxError("URI target must be a string") - return cls(rdclass, rdtype, priority, weight, target.value) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - two_ints = struct.pack("!HH", self.priority, self.weight) - file.write(two_ints) - file.write(self.target) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (priority, weight) = parser.get_struct("!HH") - target = parser.get_remaining() - if len(target) == 0: - raise dns.exception.FormError("URI target may not be empty") - return cls(rdclass, rdtype, priority, weight, target) - - def _processing_priority(self): - return self.priority - - def _processing_weight(self): - return self.weight - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.weighted_processing_order(iterable) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py deleted file mode 100644 index 8375611..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class X25(dns.rdata.Rdata): - """X25 record""" - - # see RFC 1183 - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_bytes(address, True, 255) - - def to_text(self, origin=None, relativize=True, **kw): - return '"%s"' % dns.rdata._escapify(self.address) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - l = len(self.address) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(self.address) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_counted_bytes() - return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py deleted file mode 100644 index c90e3ee..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import binascii -import struct - -import dns.immutable -import dns.rdata -import dns.rdatatype -import dns.zonetypes - - -@dns.immutable.immutable -class ZONEMD(dns.rdata.Rdata): - """ZONEMD record""" - - # See RFC 8976 - - __slots__ = ["serial", "scheme", "hash_algorithm", "digest"] - - def __init__(self, rdclass, rdtype, serial, scheme, hash_algorithm, digest): - super().__init__(rdclass, rdtype) - self.serial = self._as_uint32(serial) - self.scheme = dns.zonetypes.DigestScheme.make(scheme) - self.hash_algorithm = dns.zonetypes.DigestHashAlgorithm.make(hash_algorithm) - self.digest = self._as_bytes(digest) - - if self.scheme == 0: # reserved, RFC 8976 Sec. 5.2 - raise ValueError("scheme 0 is reserved") - if self.hash_algorithm == 0: # reserved, RFC 8976 Sec. 5.3 - raise ValueError("hash_algorithm 0 is reserved") - - hasher = dns.zonetypes._digest_hashers.get(self.hash_algorithm) - if hasher and hasher().digest_size != len(self.digest): - raise ValueError("digest length inconsistent with hash algorithm") - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %d %s" % ( - self.serial, - self.scheme, - self.hash_algorithm, - dns.rdata._hexify(self.digest, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - serial = tok.get_uint32() - scheme = tok.get_uint8() - hash_algorithm = tok.get_uint8() - digest = tok.concatenate_remaining_identifiers().encode() - digest = binascii.unhexlify(digest) - return cls(rdclass, rdtype, serial, scheme, hash_algorithm, digest) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!IBB", self.serial, self.scheme, self.hash_algorithm) - file.write(header) - file.write(self.digest) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!IBB") - digest = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], digest) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py deleted file mode 100644 index 3824a0a..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class ANY (generic) rdata type classes.""" - -__all__ = [ - "AFSDB", - "AMTRELAY", - "AVC", - "CAA", - "CDNSKEY", - "CDS", - "CERT", - "CNAME", - "CSYNC", - "DLV", - "DNAME", - "DNSKEY", - "DS", - "EUI48", - "EUI64", - "GPOS", - "HINFO", - "HIP", - "ISDN", - "L32", - "L64", - "LOC", - "LP", - "MX", - "NID", - "NINFO", - "NS", - "NSEC", - "NSEC3", - "NSEC3PARAM", - "OPENPGPKEY", - "OPT", - "PTR", - "RP", - "RRSIG", - "RT", - "SMIMEA", - "SOA", - "SPF", - "SSHFP", - "TKEY", - "TLSA", - "TSIG", - "TXT", - "URI", - "X25", - "ZONEMD", -] diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-312.pyc deleted file mode 100644 index 79beeefb762b093fb3a18e5feb131df7c23954e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1061 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 QZU!i4DkwX@WkNpVFBu7q2><{9 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-312.pyc deleted file mode 100644 index 7857b5eafea366830dd0bd251b6f151e7db94397..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 zcmbtW&2JmW6`%d&az#?B57$l;N33nhUZ*u{yO0bgaO)UOV53pnSSU~y$acLulviHv zvNKD`Vkkf@1cYi6$U<7shaTi!l0cWDe?qT4)B;^d-TIIsz4)e>J_Nb6Z+5vP!zeD0 z4#?SgAM@tpz2AHD*KAfr(9XTNUHV%Zp-&i*G`TZq{{aR~WFwn%Q8E5gNgbOY&`P`GmWa_t;+`BPUg37nE+nStYb`ODSdmQS2uIT_U=zMd!8 z@`px?Du7ZcK1L7|_)~$l&+qm$CL#@J@MeHpWT<2z? zvJ7G1oj-H-T!A_P{!YcTZkzL%7Hp3e;`M0Z^6bwF5JG1v%VY>vsAU+DobVWM=+B}1 zY!y9Hvg@fG<;cCmTT>4eeeck7&9^q++Pd00bh;tFtL}{*ZeH5Fv~_$d`0z$+>@4t< zz2VV~x7Xi(cjeLO3lEM?w?=0kWM;lN@jbN-=8@Hn4j|awvAwka0v%)l=1Uk2wxAPz z2rY<%GT^cM8fajr>+_x{iHNUdFUn;-TWplrD+x6?2_Ti9t*MjM{3OjEo218dISv*9 zybwynHUksfjHzW-oYHd7_qs?}G74ot^1S1PD#0$mc+sihkiWe|*_l~5ka4D!3Vb8L zcY@jw^IwNJ4HEh~bgO7@`1!qzwsC#^`lHP7#rd4tXY+3ym%f?Zn_%jTes4gsY01srhB~V>SfxO?*9gReOPzp(8 zE6V>dH_b!RVF|0Asw?&Mb|gub`3f zW_C0C{%}Lu%Z+S!>s~AO;%;toCpX#3ooonu+E_EUnQLkJUG3zKcCw}E4e1}lqjzWS zy|#7r!}EWVTEo*1lLGX$|$?OcYq$DID;84&_ z_5iwu?5@3c@#gxMPZtydo- zxLe5c(h?M69Lf+>RVxTZmV`SAz{Or4+~pYVV##rFs91ivLNKLKoP`<7^n4G>yUWne zxs=^}e?%z|dB&1Ma3_CvsEeRRUFaYID47j)UH#p3^X1K#?*&_P9~AEwTVvmSsGJ4t z-xZt6rt;f28rt#mxRjNFA4J)Xh)k7rEM-RJ=(9ESFY%Qpkl{|R zUVAFa(o1bklGObil1D!N(Tyj9EX}k9Aoh7S(qw63Ujy<9FLne^*(eIM$g-{yR_nqn z)YLQhj)g1i9ZU{)c}}@p4a}Q>tTa=Jt8SzXxg2|dDiWr)rB|Ta7dVbmk}?$bM#k7cex`w+>xjJx4D_d Y@eO@l-zcmX+6YKEDgWH3Ob{*dU)u1d=Kufz diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-312.pyc deleted file mode 100644 index 373c9ded33e233d3140a81f71817dc0b4abde658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 721 zcmZvaF>ljA6vyw*PEt%`p-NQg5W$co13pn!ghY$QP;gNzA?oGi_zvR4j?LMLWHzKq zz{J?kfWj9+Oqn7b8%%7ijD*yQJI9t6Zg}tap5Oo8-Sd6#b`2oc{>k$7HywaqDyUbx zg5s-$3qS#dA#h>oQRz{LBF)tz-PNO(+k$|>$#yMo{Qq(YJOxxc2UM3Dv#G6Va;v7s zPj(uvX{l{!StVQ}hr`xy6*)sqv`_4p$49o%PVE;$caI(0uL?^CtDsQbE&wa9=OPMS zEk{z`Ieg-r9@%H_%nI&u=Es~mLtShUGV>xvh%g9=Vmc2MZxix%?uFHt4k0x53HbsF z)#>^b9>)>GJYgA+7lA*a8G(=9xDNnb2e?c2%z0 z(~>-8`N&HdS7YV!MF6h$5;q!Ir>Yi4Q!b3noDP(8I)19IVX2+Qcgp Q7eJ;94WPX%RZw>O2dX!t?f?J) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-312.pyc deleted file mode 100644 index 6ec3ab3ca921e07c2bd4d1a1281b8fa641b0bbfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 829 zcma)4y>HV%6u+|_$1x3GqE(ApgcO#j!lxFoR0xSuiHQu91urMZcQkgL*qoh6W<#n( z5d&lS2RfkqDNLS%%mQNSmdZ?>c;`5x1qN<-@ArQ9zV$vgnaY0RHq zf#M^8Bg7EH0kUza6&rKx&pqZ5p#Q}w=m}!V0b(k|80VZT=$AcQ>Yg=L1Rz1YE|E=d=KA&`k{i}a-$CxQh_3$yajZ|yb za`o}f?rRgoZNc48Frh%u2?o(=H*$m0fz#)d<|d{6kPQQg7btx-bb{)qPALmrpj9@A z^SWS38u2*SS=@DEp6ffi+fpx4=I!w~p#dN4B|X|rsdW5=3fbk^bJ7d@oQQ}gA{=>c zkGLV@N&zZs3gjnf*SOuH%M zCd)i+BX2@2%j@@tiPH_ZkOz@pmt0lfss?ZT_PHd(<&}u$JdwB}QJMD|pflsaY`qV~ zBA9aj@ivI5iZL$kAiVe$txR>K)ZVVWS)1VH3@`swbgUn?{v4t}Ko=@fR!*fuS>qS0 C>8=X^ diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-312.pyc deleted file mode 100644 index 471424a44a764a9564ad62f9a2483ef141a0d917..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4449 zcmcgvU2Gf25#Hn7@s58-Nv8fR*`aI84gDilwj$ZB{O{UU>PUuV$1PFxO7Tw8(G;of zon%X(5Csqr3TbQmp^yU=Sa17Cz*#_Cd%$*qIAJCyKA~mFWv3*YV8JV_cyAdM0lQw zbw9_^h{;|1(eRH=ZVVcey>|JU$q!o+e%KOm!zlBfyI={0;cADyc^%Y8hcCwb&ly=YIx`YuO-W7}^E0YEJ}+mJ)2hi%s)osGS!3N}a(AYZhRMw7ctO$VH#g%`xoI^{ zXHwB1hzz;iEU+ZcTwE^u)icm zm(EoLacQvOC`y)*h6 zN}WqKa`yZ6fbbM5Iq%nA7Q2dK*Tah?ao^7GNB4n#3jQuR#^3%qh~5LoPzeTo!lN(= zc9JNb-!loWKqeT4T^18Ogf_R#B)qENBM{{LGUJAn2qEu%-SKzpLChyrm2LxhBo}^u8wq?lktmE`~B0Kl;pLDr8UEJRy zSl5A5M+9x2uhYe-wFq=E^cyJet<%N0wrHN%{^B!veRf8rrYOr=Rx@PT^vg+Io`p#3 z8=$X)GK~Q-tZ=M7b-hhZEOO-@%c6Q)3wR3@qGrq>Rs+7i}tmNG0RTS~I5 zs?lNACFhD4Ifcn{pyL|QRUw9JsI+T_+HBg& zr?oNm(unB|1ZyM6H;}Wx;W}{|6F>u{F%>irtcPi#fhHIYocAN<*USXB0Oy84^NcJ6 zdh}?I5=E}Z0u<4rW?qv895n;Lbk%}x-SnMvBTaB1#&RB>DoG;=o;IG;)q#F(V%~Nl zo*siWO)rG!q?R@PRLvv}ZBAQIP0#H+)?PY>ndL9Gw|pOl3qi|Us$1Lyi9Q7k0J?sG27K2CEl*ifiY$hP>%g{reANGf@J;?L!u$NT_qaC?JEMIq_&DF_J?u`o z(E&Wiii<*?-3d+{y+!eV+9e~9-O&Y6=26E;W{RUPZ7VYF`dB6M40S^Ep zf@)sKi+Nu@uu+Ydu_{yrtn_R8p%9QEn`zleyI+N>LD+2tHTxi6K=Vjjq1 zE}-gk5`Zo{DQk+F#T21Z04p_%C4=d;9n$oT5A-XLOB59n44ca6^f<2Ek1MlA?l#3N zNl)W2RC7|<$kpP(hDjC{vV2>|^2x?Io7?Vt0i+9(<-YELe+Dvr2^fH#NV<=#U3fgQ zK2ka|RO&i~KHSo}+WVll)bgEz@GR24I{IL=964Hy94$u%i;=-6?5ENvQt8B%Qe?Q` zt4Pgd>3C5(UXtD@OG8Czs3e^(u+Q4M%WVgXZ3iFerMAO`z+b~H_Xi(d+C0$z@#K^F z&kmQuZ*Pik+m2nI*U%)EV}AyndRW{E6afl%1VWIyPzcLZ38Xu~;zkun7x_H%?U@jA z@Xi_}jaT15j@vP0o~#43ar>@ZIUf^DFL>5$$}l;s4jYZh-+{70wV}#uLlrlnWmHp4 z<)&w-s_T|tn88#so6AD&kwN70!pwwrSI2j*Gd*Nn0IWM2Rh?v49crMbUj_#F)!e?? z^`NT~Y`K529ElYpv9rPNGDmSYTk1Ee~d@vIFj-$hkq7jXg$ zsIQmF-?-M7B5A(LEDgWn1b%S4Md1BgEyUaW<>=T;R^)Fn+boQ>JXSZt^F3PWrb~vi4A$o}M+5;}APOoVDu# zOA*jj%|hHzx*yl=L>5Jc<%a2VAEZCPF@GH^3F{>5#6A^%*?c1o*;js=reTQpqJIQz zi)9#Q`v762FG%Ps()I<3SidIV5J#d^5keIqSP`IO_rn#Tsp6C1_o6kh%d-^T>Lwwn z5)4%W!3uQjz7&SfzDhuXAE>o>mZX>69Mk$z0tPqe;osZy?w&H!QDiz^dE(41rf~e; Y$(57$&a9l-COC#`_jJCnrmVO47jH|SQ~&?~ diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-312.pyc deleted file mode 100644 index 71bc66f7e8e52427f313e3e2d691585248f493e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 844 zcmZuvzi-qq6tafipk=feZ|ww#EQbI9V&!8Or{TGr$u-j8i~NmDBo1T)~Mx!&QD_UG+cmr;#9|iuf#VB8o#RmYjy?4RHg^O0(!fxfNZMm6#TB$>b4!zfAAuR3yc7&-6`tMYE|-)jl8*Ib#df7ewnM8C6FYDaDM-?~b}9s=fyhY^rofioUCApg zx$NxHvV>Kj00K+_4P+rLsGOn(0ct~sx~E=y?2 zyjHRER70(H{+ES!^6WMf)g>AO<*YjigdK-&jXcT>zCTTSLe3BVH}C^#{~p+P4*l^Q z`1%vB7^{<6P7~JTqNvGQY>g{QdOSh2geI~=y9z_i+6F7b#j|4_RNr6 zFBRdKe)zGN6PK}c3iL+*D&i3MqMn$t0Z8Z#U*hBEg@Urc1p!+k|AP$Uv zTbEq&0VsAkm*3(2HIBGs9uGUzTwy10u}hTj=TR1*b-1~)F4;(zlM3Zr(di56@Lapc zNr!8$817#Yoor|IDBy&WaI5GhOXNOR$R$0|u2pr4fo4<;hXq>6R%%AYxp;x%4IuJ@ zh376&Jl;zhwo$R2QbpBSGaVBdo2`UeG}i=gWP5*oF5x2W$45(0#W`_T|)iyeSWD4Q`)(DChPDh8xzVwLRM! zc%~lvW6xgyiH5SNY`@v+ABTBzKYe2B+^3fwrqAyWoZNc*L#zJU{vZ$^D<3JXLD=#^ zFAHEbav2k&afsIT;*O;_`faCUU0|O;j$~0|&Zu}vs+%RpxMeKs9>1{Y z#ZcVFfMXW-4SpJ?%PHUBfyO*6*%zS$bEo_7Y7KMKY^9&tO^-LzCw*&Teh3@V3}99wANA?Uw2#M?z2Y$CN0+EO> zJLnzO=DSY`);cg;N$}t=gsvNRWjC=C0Bwd)tS^vYlXJQgM0GSllyDw~A-M)J7 z%7aU-E+<5p1r$b3_$N%j3EWXma3aIcM@9zmp#8J}jtXPoY>qexDb^t55$lIq1fwX= zbp%{4_{+yOCT^{^?R-zQs7y!=ppWnb>AI~(r0+mcW2%41D=Z2S7ZT4a{r zL0(!BS0pFOPe)~=E_B2rbcn9x#=Fq*3pfyJCqsvCRv!@=7N%$brCOp;S#vTy2eMtj zkbH)Z4J;sj(mxNBjY%#9fuDI^fR*KeE(mn#eigU82;C}qG(569Jl-50zjviId|^FZ zAGq7kFz|FU`}DnmR`%H+#{eYH>p7uty#QU|st^2LidmQwH&7ef#bOL z7~%T9Cda?PFZ~6{Fk0g&D2<% oz%m$%KXHHpV5c8$`Z+gMzq~QMKD}{m{aTyAlnMWr2a4VAf5O40Bme*a diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-312.pyc deleted file mode 100644 index a86c23966ea75349ac374c0f4343c52a9a9320db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 631 zcmZutziZn-6n-Z;wxh;S+NLCsK*3o|h>o~xD8v}Lv>w_LsFp+4U1GVCrAo3v*#d#2 zQ%Cn-DE?DAc`{tLcIuX*PMPxVEYk-1hWEbjdp~*K8Vw83+HYp3(ggUa$jYiED83+= z0tZe4aEOkELj)PFDpnmB|EC%761Z^*+{B*xKgA^+>KpFs!;Sk~u^C>d<1&C#%So&9 zOR=6;YC4dT6b}(hLFlU;gA>Qhjg~3R?yHk?3*ECscu~T8t%|HOHgZG3n6wxRBR&ok z-eBzG*bT}|jWHg1jD3YdZMc0$2T>?!5{oQ}CcZbIUc?1`?WZ2KCt@_Af#0Wbo(-bW zbNjGOQ$G_2vFm+s&qPZ3D5VL{@)+@P?>)tX>^M&2+}olm++(VBvD_|daEuQ z*lYV?IL_RDAf$;@K2lma&*o|0O@&ljA6uz^a#I#N-A*fPHs}5Zu@$*fC+-{vQpA(;-uJ%uF7JC!{+!P*0bx6j#)q#o0Dj11 zZrK#XCjyKB1{nIlhDlUxNMLi`F@GWA|2PFa1XvjYtco}KLURh6iH3`O)3}(+Y^=_> zaayu-Jy_Cy%FJ?8E(uDS#6m0+ z%`E(t+)z%9?+7;n1JD%lUu&ki7Wsj=(?A_)V{uh!5cEI^S|x>NBZt;W#B))IUV4-W z9U3G4A=eH)pK#p`TWv}hjYuoxsLDk36tu!q@6!C8)1u>v`r!!c0IRY zx*;a!fyZ34(j`IH^u3zd?nR9-xLLWeV=^xy<+kHKcj|}6%lA&I(fLntqQ>8Co%-zwbe5XW~#F` z*-_GYUTjkFTCGmx)WiW*k@CxuOSh{8tmZJnFa7Pwr6rzCYlSDl{O$x>k7vWLNCkfc zo$I$|J5=d)QGnvd?h1FRLI~q+0Iz-nYo}SDWZrJP*_guO1QyQ}4OZXXJKjIqpBkGJ WV{>YhCq_92f{s<7to@P=$&BB4?#)&J diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-312.pyc deleted file mode 100644 index 311aaf994d092522157e72c3848d7e6cd88464e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 718 zcmZuvzi-n(6uvv#Nij7-l_+6|Kq^ZHd}>({5{QVQ;G#wX)g4Zb?;uX?*qoh6W<#n3 zOpN^-Q20L>m@-8=HkjC2840Nqcm5$S-0;5dJ-_e0chC3TvP>Y)-r@4~Hywaq%4trg zCdF4t&Hx1zhQNg-q7qSvBF)tz-PI$*H6UPcy4lEE{9g=#r+{jwfa+3nt<7!N^m@Zu zoNl$eVN0!I%R1niIUE?jmFEmJ(K#HxJlG!!Z8Ur#boam+`gLLnuyzX7?F_K;dM={S z)pDfdPn^;I@aUaY;T_KWm{VtT^ z#hhk&BKafd2v;-NNzS{ndTDM;k{Z&Sf}i?LZZ+}2RbffF9YoPQ^Tr_)nsmjsiq$1; z$xm25_EN^xPPu#zfU8>KN-OKu*}`nog;`DVSV_xK)ElUpt3P|W@t|7JL%uCtwe$3u zH0K(Gu(%80jUS-@x1&RJ0i-$K0?7E#f8Srj-U{~qAP9RON1u;B9j~pOm9?|B?3HB~ PK!ytqpj#Izq3re#&bgxG diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-312.pyc deleted file mode 100644 index ee69788146ef0c3b3bfc71c43b33f21b4d0afed9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 628 zcmZutzfaph6n=LONlb}U5kQ~_sX|>c)ThE)A%P+>HA6ks%gOOw8>e>c+D;^QLqckq z7~x+){3)F>A(^dAY#?`H;@!m&Md};g`@Zk})cf6PEd$!_r>ifw2EeT%3#(V4yg+ac z0t5-bAv#irNHSVfEIKg$Por=If}Vgdu&4P?aRo>EhL`$i8hjq+7dF ztnXEt4x}RGYXs*Y_0A+28VLsP1s#H<0a`m2lh&A(lrW&p{yh8NY{4oE(e$qy& zo|zt^yEh0Sp>Oh9Z#M7i(UW# diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-312.pyc deleted file mode 100644 index a651c82e431949c5276af954838bd514991f250b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-312.pyc deleted file mode 100644 index 1fc418db7454a1323616d689281adac677c22cfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2991 zcmcguO>7%Q6rR~1d+qoqO+yn(OPfNIT1jKt6xvc06)Lp^s3Y16fe+nocQ?rIL5HuGeXmM!Ca-5Bww{be9RW5%8GmSHP5zhBTZ(X?MnH&XIX<7Q9(Zb2>L1 zshOzHo2_x^4AQt6r15YjR@bd@!;LkrJ}>hwzw9lgLz6fm++$Ko_12up(?iuuAYO&{ z>nM#i1nMoNoXNE2spI-mzbNDFI$8SLL6X$@Ks_Ck74i)hk@ zop2FN;B>=zl!)5mn|-O%{ZkQV+DmlRB$~~u`B9rQsVZ)G8spjPs-FV+5|@zWZ_bJZ zbRG5!xWFxQ?lMLNG`#V=r(NJRtZ_Ae6p&o|;-~2gPv{G^YbQ~HPhGh87sv_G7U^hS zC$=QZM$WKg*$&AHl`l_PIwkBGgylsdU<*{w4A?=UsTqY*J3usR5)S$*a87o%WSO!f zd)b(N^j>nv%<4&!*DYd>8R}3{H8nkX)}U&#drZ%bB{Rn5WPZ{bGIKrMhYlyHVd)3+ ziaMeU>NKh4XwtojCSOl|kaXPLou4EukEdd;SbO?DSfj53c{q)3OVQcjiqyK;Qf|K` zCDyjLlzJC>%P*9zrSp~Thl;}I(puAwQuji4xv6|%si)GEEDG0yRXB}Q-?bpHZ)t%4 z>W@GG7I+u~^Te>c;2|{ZySc719IW#-4uT8-d~z&%w*Qko2ilKYtl6t27o;winhNdv z=*qsmj6hri118BTmd%5!ZINh-r9dcksw#P7V6qF+wi0i<9f{BN&GxNE_N+wq zl!b4kucb;PakJ;!<2R14cJ{7x_I_vmF#i2`rSnXc=NiN7hzo_uHZY=Y^ec;#1G7uO zlu&`+EKVA}$<=&vCHN0?Bo5%k;8_S3q-8I*;6*+~Sojjy4ri4KIiu$Wtsy22>Xu9` zV&n!BA*cr#N}`}gSh=%#%N!vsOl)OhwC+LKGV7kEA=qWEbcXBC&;T*Bu1DjpNBxB^ z(55c~nMUgpiZ{*WW^+q?F;|Io75TMjYV^G6p?mrpGpcgK$|9Q{>#Zi^&+AD;I#wAWfXgm}|x;O_@~l#7Kx9pyK0 z3Lw3WL0UCObC#~TeK8Rv&%?DFkR8Y?gzCfvx8D*eOZLZL+~}Z|`C4{QOzqf!<_+t; zaFQm0Ortxo=G9pHO02ySJHP9k}g*svR%{+`vLS8JpVRtT#- z((b! zJ#QIijx_sgMm9TYDVG5!A*SLsJVzUT5j{O_Ry^D3ZN78Gq zAA8A35ZTAlQ$W^vjB)ivgkyJ6^Y84h5$JjVapKk8AMakptt+_o0oQ>K7EiI(H`hPg RUqv9omvYbk$u^uz{sz9lUswPD diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-312.pyc deleted file mode 100644 index e1b9853916e3dd3c974f222ed9d0159a5e9dd35b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4827 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 UcVqLh3c@rTfM@RuGDNif7iqfL7XSbN diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-312.pyc deleted file mode 100644 index 380f1bfef3c9195be1b73160d430e3b6a05b707c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3451 zcmcguO>7&-6`uX&l3a?UBwM0nyD}5k-q;aqF|DK8j?%_eYQbUA$VE}2#b&*`l1cF| zvrEaMsaP$9hH{%qEK*Pg0z^Gkg?ey+0?DyG_0WqMx*)st!9`BKsi6-+F72D;E+rd^ z0_4z<_;%i#_ujmj_vV}Vdm!LL(8gZhp1A|PPYB~S-s!a8gH8=;NTV_++4)jQ+U6PO z%Q{~!N$Yf4bY1Lz_mk1+Rix1?NMpdJXIIzlr9*CS_q)w>`rD2u>0P8G_7UL{sw1;M zjvi{i0qollzmAfWh9FKV$!N63%+g7=f@Ua`DESE;!Md8qn&nY64eN$s`HbQ}B*f+T$s zXZx2xen-`i*&STv%IF;!FH>cDo9>7yR7UB&_KtR$(I|~}cczSFckN%K&pxHky3;Nq ziAh|2^k3j5&f<(>LC2OL%c)$-lx52+D~5b+(bNr0!oZ|LB;H~TJu_o@u%>1dSS^n= za}f-?DqttuL$YiTlM&LEKmSqedOoYia6vb5emRt)XM@kKP&G8oGP+qj+Oz zqQbrtY=bpYyBiA(S&1Kh?JW875KkMsF@fM+hmg(Gx~g;_Z^M_?bM; zDyGFijx7#rim5;i>4vHlQZtKoVM#0|C}Z(?oSIGLEFac0ikX^Em2``qTQJD}k}|^t zLoAP(mrebKSrYBKaJZ8&WXu9tMlFAEZQ}MstM6E|??k=t#Dh>@&0F<0L(kMh&um`$ z!Jd$Yy>{&l#A2mPKI1H>;bnVd7M7)(Vh5*0TyA3b0H@&sjn- zXQx)TxOpX0)Fq$As2KwXzyT)Lg2jMKJV4k%!UlG84U^gBVDZkZWJe8Bd?fju)+qt+ z49;geMbKjxL6?salb-=uMv%e2!?n@sXrnh;@wCGI&2Y3Hj&3RU!qGppr_C;s4}(RonUrTyk9~!y3Dyp z-1Q5$%f6ltj)p={#C^cAx8r8!E0dY}i5&k4L@d!Z%O7!vX+NBFvbR71+ zQ(fEwhP%h&p^A&DX|d!Ia&Es446!q*jBW{PK3gC+zFn-As4BU9E~P3Nf=X0o_wsXK zNr3W|7gAVv?<R7+hy-VpVL2M=Mk-7+R0tUa0g`7FvS1=3Di>`>opf z>iG?G^M{STllO&Fj(Gir+J)+c`@%@aK3EMl#1XLfiRRXJVtKppBYE(%B*nWx!xt26 z=(vM5-S>+@&e9WL60*UAWKQl_ln(7;${z6~7#iOLvWy<~4mNwAtM@+F=sg3_;tx0d z!*%~~!+&&>`q+OQz@!;DTaTP=bRgu&7eR0LKtp-mVfpL=3st$@{|g|3|1k5ZSBxT)5EL z$(Jz6B3Z8S3XmO!qNw(>i0b(S_5X|fhk)+zi00lt`tzerYOqcXKBmX0@k)Gce06+n TVs)a8fQ6OPk^hhZdy@YF`t-lP diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-312.pyc deleted file mode 100644 index 80a0f8f4cd1af386d9814790907aba4aa158b324..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2496 zcma)7-%lJx9G~62-P=1j;OJ2SrBVq^w~cHH6tGmIiK#zQ&!pu+@1g5#qh8O=2U%;yYM;~hXs(x%?J5-^bRul zWn>DlCivgIe5TE|{@rHQh7kHTqEI2Zz$uA8n3XpoUob^^sQw9YIk|#h z@}@Au7ZMdT&6$a1xgeS;Q(Wc>5>BR&nKq?mF8&AEY%!A%OW~y1YRXNKbOl}K3N06r zlA*%+k>SNOh<<_%mzXqNB6u1T>=>A)Y}e3zmy8S>fNDGvps{n-zX0(KS4DmtS`o|W z7UY(>GQYuxZVr{vZ1egkyDV&Qp;|^-k?S-mF7n^-82dIJi=%JOA{Wpr)iRyhOK)Zm5Avtz+P-o zkU~?M7KAj-V@vwkSb9FCUU7?9B_-?=ciu9tsD^7|^#jW@)S-Fo%&WF_SuHL2S6pXg z=+v<4Sw4QXq#IZD8SJU1hqR(5)ie6N11-ak?6JzYuMY~ShW+wK|K>KVGfQ0sZElDO4Yhjqw^kp+Y4 zE%2*PfT%L8VctLPK!rdgdcEf89rT?5tl{&|xqgM;14&7c4CGPCCDx4P&@{oe?pyQL zBBuP+YaSyE!;P@((4_BbKECcRwy>K<#$kwfeIS<5PUqpOx~gtwUs*r8{`S56-Tdaj z$Y%TKraVeIA;U<5Xbkoy0T(|41#TqFMx1ISu(?qapC=*Sj7O9d$7LQcVcabz%kl<0 zHjImiu+E_HG(Cg;h*q?X>6VEd-%`-CTRyj1>e0&x|qrCMl*YuXi!oC zDeqJkBkxIq;y+?VPYRh#{pD)0T~?Y`8_}i_kXMgVI(*cU;FZbQ>7%F-)6Y zPKM{zu2}>}l^j_|AD`>r?l%yISqg>4)-IBQ#Q-Se@8D zG`MwWaNWG;+;t%Qvpn`MkNCg%Fa8JrrI`O>S^5Y6NiVy6h7q6Oq7J>*nNgDDCGa-! zUMlIt!z84B+($iDv;j(uHqa4JngOMSD;9Gx(VM)3urA{JqplaXyN+#j9jkR62fpWe zw{yq0a>r}A6P45+-&3{rGn?|604m_Xlp^G(@*wi(!ZL1U-w8)A;@~@>6lX{^m`%)<=lB>?X%5}lS}VCO$g$ddYThY?@CC@KKk(D z6G0Rw>H-+MJk#2EaU51i*(bapP6mOeObhNEY_G^<05+!?Ao-A0EYA7*Wg8O)C`~aR zF?C-D;x|H63_aon76|rufWrM)<9;rm4Z(VYybdP2k;jx>f#bNkgt)fHDEBMsfD`lL njlM7Ywz=*tuKOwfGB;TnxUH-xx78K3j=+TD@;!gDK(PH^|4t2` diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-312.pyc deleted file mode 100644 index b8db10387076e5ef5ff58a351d4145ccea7cb917..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2953 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

MJbt-T(S63Cza`hFs27DKc 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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-312.pyc deleted file mode 100644 index b04fd41191d6d9df3bf76447243c551fc9461f51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 628 zcmZutzfaph6n=LONeo0n4OE~A385|-@Tss?NU$(DL%XD2PLA)|IJIMAI}z@Ngan;B z!oPs{Q#xfrap}Dl8hD=iw=zc(}mc}T)~mP;kiCqea;oT!wYqs2XGoW>DGQK z*7GV&2U3x84Z$@?eYHacag3aHje^?!&!*kqNu(DgqSvhzP0q(|C^;{dIS(T-2^3!C z{B+_5Ri?qYh&;}3p;Ysq-mzg6N|wYjOQNao4Vf1S$!tIMm^GE-DGU4oi}P$4jSsBX z`z-Y{xfi?MC-p~WfbfCoDeM_ekRh7_<^3W*Ti2d0ymv(9X0cib~$ zCpKHjZ7pG|pb)9ZMo?j%0Qn`4;i-K@>WjCVYQx|M5WGa{L(1j><*ENam)#{z)%HC6 z=lqv*`~KTs^Z5*cG5zXxS&<3(7MWxKACC6hFl`WvSe!#j(ae?jkQYik|5|&N=lqX z;GJAauy{+D<4fWiDRY*%s+1%vZAq(KNv5eZu`-st%5`o~tgMv+U7Dt>oTc<=GHc`# zSIV9tYCaH(FPvJ=02yV}beR=o<{2$hMr$SwBZ0lT|-{vDRN9v@~yFa$B3)`?CN1f=&r-`4AWFQs&4z$Tx>{&nG)hGKc?!pWq zEbI3rI*U(qzvKM=L|WOKth#e$sJl*9bx5YE}LkUNY82e6KQv@jC9w@s&Xh< zb?5K~!IHMcNCp^*KfXtmi#IVEm-pDUMZ>YJ(Z~}waYq}0d-4wVq}mrq-oiX(fuien z&GvOY$moWrFW5DI>NrE#gN*IjHP1KDpVGM{pL&7d&{~jr&2SdzbBwtx5UiRPNX#;P zBS`7z=_R|o#L&?}idp{BJnRt{eA@{I<_+diz3j58;rrA=TU8E3k2+mD9Eieu%@S?#=Ar5c;3K64js^Sow4ylF4lo~cbP(%Pct*yptQCI7rz zJ2p8rt$DUjADuVM3&tGvGRnf)%;cX@Vwwv{{B$Q^9u z9&6+tyE=TWaP8zZ?|SZMH%3O$s145`W+&i*!8L|h`vi~;Oc$8%9v?u}0!^G$z1k1R zp9IKczIcZlRrxOFs>lw2T_C&6p0mNyIFWSpA;4fVCFp|&>3EJqkE1=x;5@8Bh)g&lc+zF&RwID&2vCQ;A8b7h$O^fo zkl_OxZ(Mn!IW)1BxltHw6~-Hd@n&ISP5u&sX=d~E){9r=X5Y+?G83WJTNg3i5r_6a zfB#C7F(qB@4E*1&jUV7xUj(-@e!CjL45dG;@_e8GKjtU2)V z-<4ndyUYK-M3MeWqR5>@k?L{+^cljAz#pJ6;D3TV<_M!z1G+_RPE9ctq&+DjhxMIB zS%Nr)*m{`VA!13n=noSBLDQoh%Y&V?=#d_UeQ|btJ#=4d=tyJeNOS06%#MAn!bGDm z(JValpRxl=+F4a7o-G0?o{qke!%kN-sx<6$deyZS99-vg{nrbI6KhhsZn-9iq3w8% z>wCH$b_MnzyeG(pov2Eot61z2{Nv+Du=)o1xKEkXadchfSXctFd_5Z$0)|-^5{y>w zB_Kbnl5fO8NdCQtH!tn^A!^Fpk-jjJ$5!t+&yVOa_C(B&rWYIVUk&N-A}_y_$ci#y+B*q75?i$S!IUd3J> z9`qTB4#hHmM|nDe?5FHGVDVqxGeB+$9LKd~!tME%41PuS!Ml0stwX;()Z+Fxxc#^J c3GT$&_+@ony{xTkZ2~O(hClF6RD{R>3r`1U5dZ)H diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-312.pyc deleted file mode 100644 index 6205b079f70545a1d773a88f831216d98ddd7b24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 640 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 QZBe*Q*uQq*D%szE0Zhlx-T(jq diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-312.pyc deleted file mode 100644 index 1dec1ae3a828c3166777a5e09a783a7fb49354d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3382 zcma)8O>7&-6`tYllDkV$luap-tXOv8*v=ZT2wS!j|DdQ{`A02G1+tT-Opz_UyOdU1 zF4ftkZ3$GM0s=y{4J;xpr~m~bw+AcGq3Nm4Dd{Nz0u*MLK?7*v{ z8U~r5MbxMoX_JU0bQ7neSCG`@h^Z^*e>n2u*@?54=XbQ|y+l*;M3q=a$QYA(qT5qB zhszq7Ny`y+TO%1;&zqLRS{cJuB5i-b0Zo6R;XwC0TtRjqT#gjcn;56{V-=C?JLPe@Y@06Y8v?sgpKgssu1(j63bh+=>G)JJtYS6fhYrrc`;s{% zF{!KH{0HWe;BZ!9RwE8C%etxCvg`_|uMxVBBl^rECkSMjplNLnt@N7nz$xLciQZ=olx2u&roBC<8 zg^1zVIYJX;KGDYW|9CG5te2ttb`gEa$Ce{izJK-UwF95=Qhi|eJ40^|tqZk*qb2q? z-SxhK%4_A<*3Q@Z4#8Zk?;3h1`F3*sP;J+7n8)kA{gqTXwRUX1cipP>j+EFpg$7tg zN`L~GP?yl?HvR@(g~kuY?M=lq&w=+DHMGE*hwWhfbbyi8ip~~%tW38fyt&9vv;|gW zV0D0vM{K%fs>Puc=yoVHMK;UM15O|**%avmy@&Yld~|z+Mc19k zly=k5vvW>Zg=|%~14ZHptwc^3(sxof9g%1Tr0sQmUUNb-H(YBLb)<_O(o1zCc3!r% zoA&(9wkS7w2SLGl5xPZGkH&AETt2zVd>S3BC!Sg!{a0deGqJCl*ta%%XYBS^Eit^A zI9^R0uO*%@jn?DOtg&~xZ+E}P{zd$=cz5)J@%P6+zIt!ZL^VEfkDqXV_mLt(o#_^l z`#?Z=ndxRRfN9Wz07U+WoCkRj{9ri>)(aq~e%RImxmXn2pd_06>;MiO!!C9zJiav_ zQVU`Tpg>DuF;e7-1kr#fRrLC>o#Z7314B%kwe(%XiU zfIAF}EFHQtU@<+sL5LcJ$ZiIrn-#Y&vg#;(JOg zAZumhSEuWVz9y*lR}=f!)H^e`Xa1P0C61Lw{~q7D^8D(BwXyZWd($62TZ>=3$6xdy z)E-0uws#@41q9qe0e~R?ns*Nff)<2@E893|9aDTt}Ur4RA9x&+nfF?#7O1 zdGa4%1h-QtHP3zT+__8V;8tM_&GbqKcLOeR%?Sf+3X1KpwCrz-x)k<`2AkCl&Eb`N zZkA}4<$}iPQZi=V)Rl}uYX~+hidYXFUishwM)D1vXpi0!P39TUw#J}?OQI{j!d8T` z@at3w*ZHnn;E&ADC!g`JG&NU_ zRZf;qe$MayZrFp$k%fI8S2MBQghPJ;)DN+jTr5!$^#WwGWXN6`#JmL*IQz_n;5|G;$xVW}$Zc2U8P zp!;?S{evC+XV00%u?H;29cy%R+!egljlw&=8ozR%iE@{4gMryr$R&3%+|ZT?^ZOyj z7d>!E7hA4zNpveLISlrl7(9`NwVR4IOJBea15=Nu=W+!*a}BaapejVo*cnG?aXF8} zO;CYr_g&=eL2urN{29-E-zS$~O8?Qi0NoaYF>XAIaL*Sg_BHDLf_^<)VHD!t*!|1h lo4CJ<`yYf3;Y)ZWQ5h%?RQ8njG!UJ`Z-xf`?TXxe{tL$hzrFwf diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-312.pyc deleted file mode 100644 index 5a30d623051ed1086a348c036efb262883fba8ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2306 zcmaJ?&2JM&6rb4-d+o$)NTNcZ0ZJ&a2VxZ{MFmAg0g6=BAmC8k!)UWRPS&w^-I-0m zk&#j<63Kx=4wX6dkV=&*lz*Vd-h07HtI#b6Do(wnm;>U}H@oY#OQVkBH*em1J0HLI zdoRD_a#;l9&A23mYdM~kcvedil`4bXEqe1# zHVJ)-Y-t(UGJK==V!K*pS6A!J8*-#?#HLcFfeqy^u_{`zEu7*w)cFFI_u&2%m9UNA zp135hpfZM&wUT0|ZFw1&RGP{lTeFpAoa~&oGqwsoji&6Zo$B*sR?r<>%HBZ6K#;w9 z?NagDr`JBabZc>UPd~({(YM{rsXefco=j)UO8X=DB_=E%*VsbLw@F@@EIodGPK;g7n4$^SD zs>ykfVz%FaZv+z{nuJzDxH$R{jHNeU7G}K~EwDQEnYZ9rvjxkuY2kB+TZM@Q>Mj(j z&UB&P@Mk^u)WioT3*7PPiMnacnKP6ZY?l|JGkD=b@m8US(nP(%G&p8)xX;$T`5|!d z!ysNSp=VldHQm^jFPzI=rVfxi+~zzBzn!MY*qaAPQLt7O)VPAAnz{1Okwb z5ksgFqYLI_RQj_NTLJrS0VHuq&<054;HLZo{ssd9q+*bP@LBM?sdbC^24VtZfbBiY zb1l=SF8m0iH516|&Jf3@uJ4o`%7CWS6|4(V5vW0C`pjt?ip-`)CXlTvXQJ(}9Ed>n zy*c)dSnd(aTn}jCdw~pJh6&*r@CwAk5y74*Gp`l_Khg($Vm$_*@Q*<(p=}N2hqm-n zZT-}y{^6E>zOA3%)F)TuXZpyNexR)%c*HmL@fG>^{LsUX9$j9a+RPt+q8*R&k>Cb< z353IZh>am1c&B^-BX{6#7hen=AVO0L)Y}XSFyfpVaZV{aRT^lPSF1D1c?eE0Xqm3( zI+j@l3MTa)^G^e)$Zdzwe-~$LEWkzi5Qru8R2zJdUCll`-8#E=_BZX_|2{9F+nMNj z|4X;KYC;tCZKB{HH$(j%L5vhT2wV1LK2ZfktlX?|J!-) zbDk7ih%=VZbCK1m1cFh`vR-7fI=GpUAp$`)B z)x24aJ!wL0&mtu3hHMnh3kJeIT%*ubZ8i=TW(aXY_;PWVwrI6Vh=G~-@5x0Ce-g5o zDI!#eeZp}8#Or1Br?UT*CaK3d8dfjj?G#Gwetz}FOIcNojtq-!NoYD&$3ovrNs1k> zgchbX+|$UiG1xK4L9d&jcPv`>9nWRMiOs3i=6!P-2$vBdQ8x^up)RQlWMe@l31TDS zL-o4onc@DAQFNAQgKxC+5oD9>94y5X`4ot48Drczg0TJqjr<}0c7cR@OX|I`ugA9V i-ZtL*O4^5whr_MWwb9nWwSyf5OL(a?`nPa|hx`KtulkPw diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-312.pyc deleted file mode 100644 index 05e7b1ce5a47c903c9733e34e1a09627a89b2b72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3520 zcma)8Z%iD=6`$EZ_n!j{=4|J{=io*?*Kl#%IBi6!V`C?=1*u}Ul21vO_3i?DK5nnG zi}{lyr4dprmmC@E8T;)sqr2?O&q$x{6YO!FsKrp=(I@kUQhD^pNoDk z`MsPM455$+j@a#=#1qKNL>Cr`F2bJVu52(Wv<0Kx{DamqNyn8mWx4f$V=iZVNw za}(C2kz-}UVb*o9d``1;BR6c?TCV@PQM#Thnpbn>S!cp3o$NpHT+TKfqpz%L*VJ*t z&Via7)6vj&?xm3{IeuXKShNE=lhv7$zC5ImR#} zVno$lAXNep2>sqRMBaRZka@Wx7f^Oj3-ZK#s6w92Mh-Cn0~D$mQiJRCVK z!MqX*P}sktb=~-9gEFKbFWWB6mkT^2#gOFgLgxb}VJuWQHqGGeD3VvK=<1 zIj)3Ia%Br4XwTBw&CXQy;@ZUzhL_H5CfZgm zEMNHf_3B$|Z>>Lnv-h{zTiHKc`fUI2^R?9JyNQ>4fz_$MCc3x7u-)@?v$rr0#Nk&z z4+JV4Ej024F#LuU3NX;f9s!?_p+-FjyaD+@9ND3n$8r*CJPE)00FaA>a!kDmCY-0> zt0!m-(DO@LrQoeD^ww|0X?lx}Kz2YsImV2W6l88c-z zIM;*W;fwDEc4K`Iw{`*f2Zqe&sll7QH^h(PAI3jw*+>ok+eY)eb0|$glT@(Gk{7lT z_da_X7Tp*#imGE?M@I|SruhzZuot4=0+G3R(;dDC4Ere{;MdlDD>KV8py$)hKc;_| zzMC4XwGQ5i4E}dKV&wC990Xfc$v8%x_`mF?(`!SJA#9Lp_BI@YO&0=hy#Z#M56y=` zt~vfaF;WQ!a?gX$oJM_kQp6SzMSEH$R1t#;G9Q8068r^oh*G)Gi00jmKaB)BLE``U zM*1H3zQSJ)6*2 z$_;q}94ua1??JX?K^pOpi>D)Ec=M7%+url%IuS#Axu;>L{UMMA@?c-*Qg}1jc`w}zd3wL= zV0B_`;$GLOjjmIlPJK33>pHh8ektPGgG9&5$nwaIlXnw`x1ywVf0K$@^5}Mqv~{e$ zaO3>?%eB_ycOu8X4CAgXe(sKqsQA;7GacfeJLEIz@MBp4;VMWm`4cJhc?V{z_io#1Vdj`&681ZhLm+VOc z9wa(@5jl}Ev*0b@%rZ=YZi`>lHKSNm6!?@BaQUa4_7aB#bB3fF7lwd*wMhOhbv|f6 zv>IP{^^vqsezE>dN$#m9sQi8Suu%I$AuRXRMHp@2Xlow{CAS4ZP9rb*2uJ**`LWp; zOT(dV1m9@*5aS?sTljQFGrUI`pPG}CQ;vES3SAUe`Ph V&aUK^b9Dk!_!{BhHZE`_{{h7$4kQ2o diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-312.pyc deleted file mode 100644 index f2e38265170cd9cacfa7576214347920bc37c883..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 631 zcmZutJxe1|6uoaICUG<%?z$)_2(by`Jak(TwAsoau8q??Ci9+V;(R$X35=Z}tXpmA z{sr-;Y?Uf7cDrn0r;L@A_q|MTg}ueS=bZb={ncm~fVOpbck)*U_#?^0swF6XBDet# zoOoan6*Y?p(wkIFIxzlE)8Gqm?FzV#J@tQzOW0F4yw`iP@3~}mc%h2p09Gv{t;&OB zZKu>!ASEg05Zr)JS6dnF@HuHb0pJ|Re^&|Sgr%EwrKN$IV6-oRed z_5FTocRZ2nNb`BAmGjIX>DY^=+5i9m diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-312.pyc deleted file mode 100644 index b1eb282d0dd7829a505617209a4f2351d4c74cec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2560 zcmb7F+i%lW7(c$oNpopQfpin1(@KYtR%=CKTDgcx6KI>rP>UulLh_95v@TAZ9lNCo zE_6~Q5@{$PZEB?LA&)`jf#>}Td!Y(bJF^F-J?*U{JRqL-9oxCU=q^X{Ip6i1U6f_v-!opZECw74b=Xkt~gLgj|$keKl3 z*^8Cv?8R}cs}|Ngws6H-@fc^tQMrL@u|SiXb#B1)9kqd+p0dWd=r-88luK{YftW%r zS{(EbtzBl53P=~p-9O#SmL0Gq=li3K?onBh^Ei8{q+?HzWus^~vJ8}B%SEN2V{$Gg z$rpK#wRJP^MX;utifwy5)|@i<^-R2|Ec+^1wu#F)8EY@*qy?*>OSq&v*jhH!1xd9u zUAkb{sx-B%7ndc|xFVIx&Vp4uKK0(TWE+nDZb?zED)YK6X+>KKPO_yBvtLNF7pF>P zOwd&s0=j$WaS+(kFg;&GzY2+3WJ}024mKwr3F6LZX5+%$3(bl4=sQ*Ro9IsZ;Ku2@ zr<>_^`bd?%Ep)&Osl5mSLvB9=U*|`dHb_iieRV7jdW6csysfg}*Nb)E`(OPyjarQK z?Yoo{?NV+pn*%bgo8c~Q0FiXI!d+h-zrHGReww@yb-}W1UBOv-eBQzZ z#qk*1!6J`I9eTWljd`Q!#k$57-J`Ew>*u?l`roDeWA9nojkggJlZ<&;Ei_jRF&CD9jM=^-S|Om zPJD0NPq*eaGwsy$Ltz?^z+UQLcu2DRa?*f+WHF0jSpeG`F6(kG-)$VLk^f{R(2#nP zQa9oXn_(*F5QgRmmE#HXy5m=g81)#{v~dFDnDi^CdFv`ZK-hzXP4q)3JJvu1NlN`P zAyW{0Ns0K;=mKllhY|^_ z9C%dHU3?7)Pk@^(!L){+r1sZu)^0YI+o?%Xr=xYNW;OEdqLx`cZIwq@UFyUxnOBaCZXf9{MC|ThMI^hYnBRPctdQ{a%@@ldpsV4t9V2Hvr^FE ziO}#kNbn??M50TCkB+LFrYwsTPJz@WE#Rz=15ASGPZ^^46sG6v=r=YERkSbD*ncOr z_VF{8;U+sVhC99+L;T2-&*q*o94B=cV0LLjC28&?IPfD+X(m)WBb1K`e|KmC33#if z^c7VvIfhmA7+?dLQ79}q$`t}&VnY7eixLWJilcbZzMcyt+$>S}``$yr8KlF$5Kaa0 zJA-F{B|mAOg=v?eD5^7osQr)8=wp<6jD~l4M02+eeSK(~%4|`YXY^Z?R87~%YGd`o SwZk0*Ec_ll_9s#JC;SB&&>LO= diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-312.pyc deleted file mode 100644 index 8526334a4c2e2c3ac4c84e4e597c95cb2a6bb5a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6559 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 F{sU8A7@PnA diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-312.pyc deleted file mode 100644 index 1f3320322c779033d008bdbcaf367f9c13c03bb8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 646 zcmZutzi-n(6nHyYoPoC?W?gGSjVBn=ZzNV!Q!BG^1O zc#Du%^DwS6Ekc-}gnWj|%)fYory}84W;~Z-5z#58f^mEhDT@6CPZv0jCO9kdsYvhn zcXzRha(*WZ>C5ntE6h@bCCiIU;|Ig%cr^C2LN@ed>uzeGHncm2Z;h@rFS7ceU3PS< zA0^2=4<|7%T}{htMytp9$?Hj|cNcPoUt#;a3C_m5owqw@sJBGDUk*ao-*-Ru bKlIPM{?gNaZ|Ut-pykR1XZxoqusi+$>-Cd| diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-312.pyc deleted file mode 100644 index 374314f401a34a6de9549619366518ac23b955c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 646 zcmZut&r9P#6rM?H8-E0ZRje$69xIqZy^2slMA-IV7nV&ALz)?FYSLtrv=DDXK|Oiw z{tMDS#Zym(EbL+Tv^SS{>d7}rx~t$E-uu4qeQ)05J*w&gg12`zI{yjzw;;1u%t7)U zzz{LSu#I$_W=Y2!cV>=R2jurO37sG&4G@!|M|ow=VJF?;t=?IB%LN=aH7hWQ*5}ceulow;=Usd`1sZl)wUp94Kj7aA! z0-7oZ*)0zJ4?x9vgCht}GR8P5BE0$oeS9h+Y3^p@dSimiV_bfgFfQLV?wfbbiMlmb WwL6X4u529(jT;9;gEa-(6iGU}NU~s4TW~5#*|a6m zd!(J%U;qy=P#ak12tgqqcF=lR3t@nFT6bEH9d=kfb%@9A(4nVY1~-;(seL>P?T>x9$ z5cPyEc3I>Na*N3)t`Q~aao1*EUX`IbNe#`Sx*~dfxl$+_9#8#7unlS!N}foKA~oze z&>Ie2@dV@6qDc);ESnXxyj0fw6A70RgBDlP$}@z#&(w$$8VK2xUGg4Jh|9QagALRS zamhUB4i=YdKs*+gsO_EU=Ns$t#qawC#!a;Qoz~^MeA{@zanl{2cFz^Me5FX++gI%J zjUVDGb@@hn*Jlp#F5hT9E94_)8jg^+mlh4`NvaAb;ixL~3bu-eM{!RnF3^)vw`5kF z$um?$g@_8rDN$S##mQ2f1;yc&gcq}oQV|Ya*GdK3_5`XsEAW+QK!1>4v0hwN{TNlX zajcV3S^M^SZq6zjIl2f4vX)J4E~i<#k^7NpYq^PKqq3YUneXHlSDZPkGCA@5nVfAp z#_7d^wosTg?3`Y)bHP09+{@EH%>imB7FQ@9+G-Mo_6~dxH1;}F->i`b0*Mc7io4Rt z_ObfYpG(T#&||f?t8drOHiwjr*e~TrbO4V4kRW889969}D9m;j4yLbXQrGTEA3mG5Q7cQfN3-q^`EGZ!}aKHBuDz2u<( z%WPfQyt0?fY`wnuIyRR#FJseD?x!V1{+dV`d7nsenU28MG`y1Sg3$2Ea{$N>;K9v@ zB-nPq);~g_{jA&;|0gdAyn4W@5e|Gi4)z{29NFbV?1@9{$wTZZ7A^yV+!bBPjk~g& zaFcGT5mMo@fp}u_5do8LB;3eeUk*4G|p^)x_Nt#DK@~>hZf{4>|lJePl0r^v;d# zAJ<>~aBAn=$FF`e_3615$0EMiHom$~6)(7tuc-iE--&))&W$xf+VP+5??6Eh$2t+_ zdr=OM6q)}&Ne4#gk&1!#2<*Ut(g?}$Ht#a?h?xVWlQJ*_9$ug^;fYIFlte&WNGCTT zAqt*b!FK49=6ErDy!yh*<8M-U;o2M)E1D(4lQgTm2$`1c1J+Awg^E=%wL-}RN{biG zWdq)M$9{y*B!IF%gYYd3rOU7o+ky%hO-gRboANzrbXOYPexW`Bcsc*MbfKUApV`sc zl`4=~_Ksd#s4moB`0e@MoZrd*Vf6Q-A7B5|n}2-s)2IGA{^xN3+mO5w--nkrg&(-{ zgTG5-0k3uA4tLncoeQ1*yw*;2;3WDmU*sV~e8IcIYz+K+u$#`b{XbxSC;SQ?A^HqV zKw;@gXaWKWPD>H#lc4J24PmiBA^#1iBg%bjL~h6i0S#pQdymJj1`(^-hz+{5S0T=R z392>nWoEdRuBPikGxL3T6*QcSHz8ElL60<TMCtzw`!q48y1|(rReHt|Md92Q&EfwZ=no%mLs>0A=(AxMsUGO)8A{+SCC>juZ z->j2=#j^K-18*>Emmb7q{&Z_3!OQ!jL>T;X=GuKO$;&Mcgnbr!j|=>X{S1irS&n~$ z@huPhuD@!3a7Cui!q8p{GQtVtmS!wErd9Dc5CffAE-yKSckni$i~mO^jyyqp~0l{nb3YdaC{Rtc%? z#7O@I#GleBQzWy�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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-312.pyc deleted file mode 100644 index 1eb3a5fa9678281ac9e61f94155d8d93443dece0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 640 zcmZutF;Ck-6uz@fVla^^(I8M2(UA=JR2YMh!omU>Xgk!)$?+Y;!H$jXL~=KzNWjDh ze*xvEFlCB#Y-M6gdp58;HvV`DmACFE1?$1NS$e;qGXj*T-Tl@ zYdM9MCtQ&11Atq^^Q|p~VM|Swx+=79hhLjN4H-X2+zBGqs%t{0v}gOAQn5s-AF#eB z@iL|7ecLN4WlCA#PGATNbON7~DC9{L3|yx}oPcq1-@qbsjC<^^wRhOpw&^L2kloq*<0q8~Fon2xt zS^+8#8}CDysu<&}gmCo{eVUe#vT#|us7>(N7_a?NFkZddxoKQCCdS6t*q9ibV`DQz OAZIF4)_==}+~Y6429MCB{U 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 N3mvrOCt09&yaL|fj$!}+ diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-312.pyc deleted file mode 100644 index 132981be3d911a8e2a1f6810ae092274acda6cfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4147 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 Qi$H`(rRV+@3jZVj0@HB!OaK4? diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-312.pyc deleted file mode 100644 index c7f97a818611f0d2cb2e72607ad15d236dd77c0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4164 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 GFa5v5uo`Lr diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index dd9f2b23309c3bf7fe77cbdfb00b2b9f0baf0a52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 260 zcmX@j%ge<81V%xt(`GX>FgylvV1N_zW`iSA>2> zer~FMQ9)`+QGQuwa)y3#eoCsoZ)S0_zFt{sUYUMQW|DqEWl2VUp0S>xk$!PzNvdu^ zVsdt3dTOzLN?x&kQ3}Y$Vtr>1{rLFIyv&mLc)fzkUmP~M`6;D2sdhyiK#M?LC>8?} fAD9^#89%BpFe-mwV_@a@z|6$W)W}}M29yE-X{Jc3 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py deleted file mode 100644 index e09d611..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class A(dns.rdata.Rdata): - """A record.""" - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_ipv4_address(address) - - def to_text(self, origin=None, relativize=True, **kw): - return self.address - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_identifier() - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(dns.ipv4.inet_aton(self.address)) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_remaining() - return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py deleted file mode 100644 index 0cd139e..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.exception -import dns.immutable -import dns.ipv6 -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class AAAA(dns.rdata.Rdata): - """AAAA record.""" - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_ipv6_address(address) - - def to_text(self, origin=None, relativize=True, **kw): - return self.address - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_identifier() - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(dns.ipv6.inet_aton(self.address)) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_remaining() - return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py deleted file mode 100644 index 44cb3fe..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import codecs -import struct - -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.ipv6 -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class APLItem: - """An APL list item.""" - - __slots__ = ["family", "negation", "address", "prefix"] - - def __init__(self, family, negation, address, prefix): - self.family = dns.rdata.Rdata._as_uint16(family) - self.negation = dns.rdata.Rdata._as_bool(negation) - if self.family == 1: - self.address = dns.rdata.Rdata._as_ipv4_address(address) - self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 32) - elif self.family == 2: - self.address = dns.rdata.Rdata._as_ipv6_address(address) - self.prefix = dns.rdata.Rdata._as_int(prefix, 0, 128) - else: - self.address = dns.rdata.Rdata._as_bytes(address, max_length=127) - self.prefix = dns.rdata.Rdata._as_uint8(prefix) - - def __str__(self): - if self.negation: - return "!%d:%s/%s" % (self.family, self.address, self.prefix) - else: - return "%d:%s/%s" % (self.family, self.address, self.prefix) - - def to_wire(self, file): - if self.family == 1: - address = dns.ipv4.inet_aton(self.address) - elif self.family == 2: - address = dns.ipv6.inet_aton(self.address) - else: - address = binascii.unhexlify(self.address) - # - # Truncate least significant zero bytes. - # - last = 0 - for i in range(len(address) - 1, -1, -1): - if address[i] != 0: - last = i + 1 - break - address = address[0:last] - l = len(address) - assert l < 128 - if self.negation: - l |= 0x80 - header = struct.pack("!HBB", self.family, self.prefix, l) - file.write(header) - file.write(address) - - -@dns.immutable.immutable -class APL(dns.rdata.Rdata): - """APL record.""" - - # see: RFC 3123 - - __slots__ = ["items"] - - def __init__(self, rdclass, rdtype, items): - super().__init__(rdclass, rdtype) - for item in items: - if not isinstance(item, APLItem): - raise ValueError("item not an APLItem") - self.items = tuple(items) - - def to_text(self, origin=None, relativize=True, **kw): - return " ".join(map(str, self.items)) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - items = [] - for token in tok.get_remaining(): - item = token.unescape().value - if item[0] == "!": - negation = True - item = item[1:] - else: - negation = False - (family, rest) = item.split(":", 1) - family = int(family) - (address, prefix) = rest.split("/", 1) - prefix = int(prefix) - item = APLItem(family, negation, address, prefix) - items.append(item) - - return cls(rdclass, rdtype, items) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - for item in self.items: - item.to_wire(file) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - items = [] - while parser.remaining() > 0: - header = parser.get_struct("!HBB") - afdlen = header[2] - if afdlen > 127: - negation = True - afdlen -= 128 - else: - negation = False - address = parser.get_bytes(afdlen) - l = len(address) - if header[0] == 1: - if l < 4: - address += b"\x00" * (4 - l) - elif header[0] == 2: - if l < 16: - address += b"\x00" * (16 - l) - else: - # - # This isn't really right according to the RFC, but it - # seems better than throwing an exception - # - address = codecs.encode(address, "hex_codec") - item = APLItem(header[0], negation, address, header[1]) - items.append(item) - return cls(rdclass, rdtype, items) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py deleted file mode 100644 index 723492f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 - -import dns.exception -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class DHCID(dns.rdata.Rdata): - """DHCID record""" - - # see: RFC 4701 - - __slots__ = ["data"] - - def __init__(self, rdclass, rdtype, data): - super().__init__(rdclass, rdtype) - self.data = self._as_bytes(data) - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._base64ify(self.data, **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - b64 = tok.concatenate_remaining_identifiers().encode() - data = base64.b64decode(b64) - return cls(rdclass, rdtype, data) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.data) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - data = parser.get_remaining() - return cls(rdclass, rdtype, data) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py deleted file mode 100644 index 15464cb..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.svcbbase - - -@dns.immutable.immutable -class HTTPS(dns.rdtypes.svcbbase.SVCBBase): - """HTTPS record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py deleted file mode 100644 index e3a6615..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import struct - -import dns.exception -import dns.immutable -import dns.rdtypes.util - - -class Gateway(dns.rdtypes.util.Gateway): - name = "IPSECKEY gateway" - - -@dns.immutable.immutable -class IPSECKEY(dns.rdata.Rdata): - """IPSECKEY record""" - - # see: RFC 4025 - - __slots__ = ["precedence", "gateway_type", "algorithm", "gateway", "key"] - - def __init__( - self, rdclass, rdtype, precedence, gateway_type, algorithm, gateway, key - ): - super().__init__(rdclass, rdtype) - gateway = Gateway(gateway_type, gateway) - self.precedence = self._as_uint8(precedence) - self.gateway_type = gateway.type - self.algorithm = self._as_uint8(algorithm) - self.gateway = gateway.gateway - self.key = self._as_bytes(key) - - def to_text(self, origin=None, relativize=True, **kw): - gateway = Gateway(self.gateway_type, self.gateway).to_text(origin, relativize) - return "%d %d %d %s %s" % ( - self.precedence, - self.gateway_type, - self.algorithm, - gateway, - dns.rdata._base64ify(self.key, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - precedence = tok.get_uint8() - gateway_type = tok.get_uint8() - algorithm = tok.get_uint8() - gateway = Gateway.from_text( - gateway_type, tok, origin, relativize, relativize_to - ) - b64 = tok.concatenate_remaining_identifiers().encode() - key = base64.b64decode(b64) - return cls( - rdclass, rdtype, precedence, gateway_type, algorithm, gateway.gateway, key - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!BBB", self.precedence, self.gateway_type, self.algorithm) - file.write(header) - Gateway(self.gateway_type, self.gateway).to_wire( - file, compress, origin, canonicalize - ) - file.write(self.key) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!BBB") - gateway_type = header[1] - gateway = Gateway.from_wire_parser(gateway_type, parser, origin) - key = parser.get_remaining() - return cls( - rdclass, rdtype, header[0], gateway_type, header[2], gateway.gateway, key - ) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py deleted file mode 100644 index 6073df4..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.mxbase - - -@dns.immutable.immutable -class KX(dns.rdtypes.mxbase.UncompressedDowncasingMX): - """KX record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py deleted file mode 100644 index 195d1cb..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -def _write_string(file, s): - l = len(s) - assert l < 256 - file.write(struct.pack("!B", l)) - file.write(s) - - -@dns.immutable.immutable -class NAPTR(dns.rdata.Rdata): - """NAPTR record""" - - # see: RFC 3403 - - __slots__ = ["order", "preference", "flags", "service", "regexp", "replacement"] - - def __init__( - self, rdclass, rdtype, order, preference, flags, service, regexp, replacement - ): - super().__init__(rdclass, rdtype) - self.flags = self._as_bytes(flags, True, 255) - self.service = self._as_bytes(service, True, 255) - self.regexp = self._as_bytes(regexp, True, 255) - self.order = self._as_uint16(order) - self.preference = self._as_uint16(preference) - self.replacement = self._as_name(replacement) - - def to_text(self, origin=None, relativize=True, **kw): - replacement = self.replacement.choose_relativity(origin, relativize) - return '%d %d "%s" "%s" "%s" %s' % ( - self.order, - self.preference, - dns.rdata._escapify(self.flags), - dns.rdata._escapify(self.service), - dns.rdata._escapify(self.regexp), - replacement, - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - order = tok.get_uint16() - preference = tok.get_uint16() - flags = tok.get_string() - service = tok.get_string() - regexp = tok.get_string() - replacement = tok.get_name(origin, relativize, relativize_to) - return cls( - rdclass, rdtype, order, preference, flags, service, regexp, replacement - ) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - two_ints = struct.pack("!HH", self.order, self.preference) - file.write(two_ints) - _write_string(file, self.flags) - _write_string(file, self.service) - _write_string(file, self.regexp) - self.replacement.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (order, preference) = parser.get_struct("!HH") - strings = [] - for _ in range(3): - s = parser.get_counted_bytes() - strings.append(s) - replacement = parser.get_name(origin) - return cls( - rdclass, - rdtype, - order, - preference, - strings[0], - strings[1], - strings[2], - replacement, - ) - - def _processing_priority(self): - return (self.order, self.preference) - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py deleted file mode 100644 index a4854b3..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii - -import dns.exception -import dns.immutable -import dns.rdata -import dns.tokenizer - - -@dns.immutable.immutable -class NSAP(dns.rdata.Rdata): - """NSAP record.""" - - # see: RFC 1706 - - __slots__ = ["address"] - - def __init__(self, rdclass, rdtype, address): - super().__init__(rdclass, rdtype) - self.address = self._as_bytes(address) - - def to_text(self, origin=None, relativize=True, **kw): - return "0x%s" % binascii.hexlify(self.address).decode() - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - if address[0:2] != "0x": - raise dns.exception.SyntaxError("string does not start with 0x") - address = address[2:].replace(".", "") - if len(address) % 2 != 0: - raise dns.exception.SyntaxError("hexstring has odd length") - address = binascii.unhexlify(address.encode()) - return cls(rdclass, rdtype, address) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.address) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_remaining() - return cls(rdclass, rdtype, address) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py deleted file mode 100644 index ce1c663..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import dns.immutable -import dns.rdtypes.nsbase - - -@dns.immutable.immutable -class NSAP_PTR(dns.rdtypes.nsbase.UncompressedNS): - """NSAP-PTR record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py deleted file mode 100644 index cdca153..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class PX(dns.rdata.Rdata): - """PX record.""" - - # see: RFC 2163 - - __slots__ = ["preference", "map822", "mapx400"] - - def __init__(self, rdclass, rdtype, preference, map822, mapx400): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.map822 = self._as_name(map822) - self.mapx400 = self._as_name(mapx400) - - def to_text(self, origin=None, relativize=True, **kw): - map822 = self.map822.choose_relativity(origin, relativize) - mapx400 = self.mapx400.choose_relativity(origin, relativize) - return "%d %s %s" % (self.preference, map822, mapx400) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - map822 = tok.get_name(origin, relativize, relativize_to) - mapx400 = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, preference, map822, mapx400) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - pref = struct.pack("!H", self.preference) - file.write(pref) - self.map822.to_wire(file, None, origin, canonicalize) - self.mapx400.to_wire(file, None, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - map822 = parser.get_name(origin) - mapx400 = parser.get_name(origin) - return cls(rdclass, rdtype, preference, map822, mapx400) - - def _processing_priority(self): - return self.preference - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py deleted file mode 100644 index 5adef98..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class SRV(dns.rdata.Rdata): - """SRV record""" - - # see: RFC 2782 - - __slots__ = ["priority", "weight", "port", "target"] - - def __init__(self, rdclass, rdtype, priority, weight, port, target): - super().__init__(rdclass, rdtype) - self.priority = self._as_uint16(priority) - self.weight = self._as_uint16(weight) - self.port = self._as_uint16(port) - self.target = self._as_name(target) - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - return "%d %d %d %s" % (self.priority, self.weight, self.port, target) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - priority = tok.get_uint16() - weight = tok.get_uint16() - port = tok.get_uint16() - target = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, priority, weight, port, target) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - three_ints = struct.pack("!HHH", self.priority, self.weight, self.port) - file.write(three_ints) - self.target.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - (priority, weight, port) = parser.get_struct("!HHH") - target = parser.get_name(origin) - return cls(rdclass, rdtype, priority, weight, port, target) - - def _processing_priority(self): - return self.priority - - def _processing_weight(self): - return self.weight - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.weighted_processing_order(iterable) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py deleted file mode 100644 index ff3e932..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import dns.immutable -import dns.rdtypes.svcbbase - - -@dns.immutable.immutable -class SVCB(dns.rdtypes.svcbbase.SVCBBase): - """SVCB record""" diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py deleted file mode 100644 index 881a784..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import socket -import struct - -import dns.immutable -import dns.ipv4 -import dns.rdata - -try: - _proto_tcp = socket.getprotobyname("tcp") - _proto_udp = socket.getprotobyname("udp") -except OSError: - # Fall back to defaults in case /etc/protocols is unavailable. - _proto_tcp = 6 - _proto_udp = 17 - - -@dns.immutable.immutable -class WKS(dns.rdata.Rdata): - """WKS record""" - - # see: RFC 1035 - - __slots__ = ["address", "protocol", "bitmap"] - - def __init__(self, rdclass, rdtype, address, protocol, bitmap): - super().__init__(rdclass, rdtype) - self.address = self._as_ipv4_address(address) - self.protocol = self._as_uint8(protocol) - self.bitmap = self._as_bytes(bitmap) - - def to_text(self, origin=None, relativize=True, **kw): - bits = [] - for i, byte in enumerate(self.bitmap): - for j in range(0, 8): - if byte & (0x80 >> j): - bits.append(str(i * 8 + j)) - text = " ".join(bits) - return "%s %d %s" % (self.address, self.protocol, text) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - address = tok.get_string() - protocol = tok.get_string() - if protocol.isdigit(): - protocol = int(protocol) - else: - protocol = socket.getprotobyname(protocol) - bitmap = bytearray() - for token in tok.get_remaining(): - value = token.unescape().value - if value.isdigit(): - serv = int(value) - else: - if protocol != _proto_udp and protocol != _proto_tcp: - raise NotImplementedError("protocol must be TCP or UDP") - if protocol == _proto_udp: - protocol_text = "udp" - else: - protocol_text = "tcp" - serv = socket.getservbyname(value, protocol_text) - i = serv // 8 - l = len(bitmap) - if l < i + 1: - for _ in range(l, i + 1): - bitmap.append(0) - bitmap[i] = bitmap[i] | (0x80 >> (serv % 8)) - bitmap = dns.rdata._truncate_bitmap(bitmap) - return cls(rdclass, rdtype, address, protocol, bitmap) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(dns.ipv4.inet_aton(self.address)) - protocol = struct.pack("!B", self.protocol) - file.write(protocol) - file.write(self.bitmap) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - address = parser.get_bytes(4) - protocol = parser.get_uint8() - bitmap = parser.get_remaining() - return cls(rdclass, rdtype, address, protocol, bitmap) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py deleted file mode 100644 index dcec4dd..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Class IN rdata type classes.""" - -__all__ = [ - "A", - "AAAA", - "APL", - "DHCID", - "HTTPS", - "IPSECKEY", - "KX", - "NAPTR", - "NSAP", - "NSAP_PTR", - "PX", - "SRV", - "SVCB", - "WKS", -] diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-312.pyc deleted file mode 100644 index 939d842cddea035ff796811757d4c6498b30c412..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2108 zcma)7&2Jk;6rb4-d+peDYSSidDHM@LwUpSQcI85f+G1;}9oA!)QP425^vWIG3ZZ)SG-33{I#TScHjjY-?!B%6yUM!K{t@3Jq z(;B@$@?iWItNwW8*!IZ8=E%fPpKpzvTQTmI`jAJQ!34m-xeURte-zj{&JW~%x=JJf zoz!VE2t(o*Q7(|~jO#~5DLgxL=iNmSX*X{vJTz=yRxm3c1}qe}LpbY3s=f_=g?L{g zk1Msc-fHiWbxa~~9o!}(9AkX==Rtwe^x5bH7E;n$VOj>Aq`9G~#=sbd zMD-T1C9-3Y+Trc$*k*NXt2(}t|E*HJ_ul=B4=!z0CLUQ6Nzd44MuVOa=+HBLRnK7x zcS2@y*86C7?+nuz94nT;kFraVNeWjYIcPI7Z%Ke+qyq#a1MfmqaD5n}9R_}S9TF56 z)2=5X%LzJNyuR@eqmpC$f$uuD2cQ_-hjeTfjFjDSrHEmi>XT4gI<&`DX|1?g{PxWH zg>Now*G_KMPCnp2dOvtuwKKn3Xa8ep{|hMQ04V0afl@}GNYSz3De!0NIXMb-?ZHW| zYfB|$0u?+gg)e@LiL|@n7zfxMOL7;hDK_@_-oY>N<}Q(^)zR(h_-1u{t9t6y0I6%O zYjwSKtp%)gF*%?TK3Bfo5%63U8S4am&O^P#*q3v*mqzl8@xTE$JWk3BLd978e#k?x zbu<*eR~-S550}TlBgc@P!dN+d%^cx*jKRgnzZ!-2@1q#|5`h=9BN(^@?8P$q!>Bzy zc=X=T(&gudW}fd`)O>v>M{)C&>+ax}>)0qqH>xeu|NdStPZSETGRhC-`(R?f>SJI#I;FHfM(Cj@d+=_S|}ooYAK1Kp{11&1tGQS0VB1QkbGdY-I=7Du6NDM zCT$`k1u0UKLzN^-kJB_{AT=1 zxm+Yb=Wnb}p2`#QCmI>$Ql;|)D651MPCe3~vD8wjr_xBJ*`R_>m$D`Hx)*yb#4Irp01Lx8(cQpy<$(9IZFzY&ZYEHmK?K4+7weeZu&(=KmcC9rR z-UmZ_HTZa7ZRA(0x;-$oH88p{F#7Wsn*$e?jEAKT$koZNE3#2>c`cY8|&ko&LcV0x=-Fpg;4u>FnF)JSiEEM;{m3Lw zg#QBB?(JV`FSnmsLlSZ9;7%FgJYy7G1O=+or_RFk{5r2e{fLToK7)U^^61Ev z2${=;AG(vSkX0+v9ZyNDL6(6u6lN7E;a@=NQ-je{!NC!AZ2HuOP;QBB}I}H5v zY9v@NCS6ZNmJ>8vc$MQhMkUAg1K)LQ51`S#8|maMm@2#HN|E6?HOHX5bZ*;LX{ESa z{BC0P(zlnk`p<6kpIzfWc|Ura{S(iu_y1#V{|hc=7cS<%;ZjDpNYS+6W$>q}IXMV* z9Y9O2WlJSw0vJ3mg?|GvGSUY7VklsTEXkcewvUhl?sxHHyu}M-w=%d@8QG|eY*x;_ z9wt?-eygh2Z`Fa-uO`P-!UxQ^n*tuNB4f>f&v>Yp82f6*_R>h6F&;SJhUZFoL8usu zpA&fuwvKw@7py7ZIpT5%JaQP>IgFLl_stQW#~3^U_!p$`B7PiS0*Ua8*$E6>1NL%} z{9zp5J$mv{&%*T=hGt&uSkyeVmm|5OyPw~Dt{di9M+ak1L#;&33vukZrlo-wsKqB7 zuUsugWdPy0xbKKo=mPrQgl!U^+icE+_HD!;rX(;$O>#T5lN`9)!Uq&e0B|?mOcKl+ zm=|yJgd|(y%k|3oFd5}X@-mp%ulf|&o=z$43=?{6hrF>vdUr@4C{Vs;J~;i&=`A|A hK?h%GC+OJH#LDI6%PUuwuXG5Q(6DymZw$nn`~wnO(fI%X diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-312.pyc deleted file mode 100644 index 731259ca4aa590c4e2262bf5df1157758869ffcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6902 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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-312.pyc deleted file mode 100644 index 4e054defc08be0ad028b99d3dc0a9d364a463a93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2252 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 OF6@l2_eWG%4*VBZ@;rY4 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-312.pyc deleted file mode 100644 index 459a745f38f5674b59e2d1aff0a7f3b04d2371aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 645 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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-312.pyc deleted file mode 100644 index e03d675309e19c78bac080fab7e47d00403bfb76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2697 zcmbVO&2JM&6rWk|de>isZVR}42!ja1g4h&VDyp=hs!Bvl-4>84`M_$kGf6hsyY9?} ze8^O36{(IYL^xDQB@XGSpd9*#^b+G%p<51AoO+{}1LD*-`(Z~kQl*ZPx9`n+^YxqG zd*fenxio@y>eBl7w1&`QVZ<#NDcw7utRR92uAyr9#Z@V=6VX?WzDiZ1(o{B{qW|5l zPeP-JNQ;OhV9btwY^+V~jJ5uEJrU~bQCBrJhl}!G!m3zNU+{_IQ1>dB-@^VWs$zm* zpSYSJ(gd!`Ei{fvVo|FqL?iMdt|qCPLL^0$MckWTBWaQZUy7r&W_ zQ4>u;-^;C;SjS;s6B+SQ(Hy#mD@EB?_;iCZUo#Bbu|31^Q-;Znu{n=&CMGK?zRYQD z+*cW~Y9{A?k`ZqX#t1J&8$qvOaA6mqExfo=o^9B0(z+BmejVEU){5I^jEF;V9&mluWo;}HqqHrYRNaVU5G+f@AzN{cr);OI2~ckAUu{V}vtYA>*-#Jre??0}n>D)aKm*+CAVYh$)!a?7@Tb&}53 z?D4q}%_IRn5L%R(*qEPmnLS}Uewxvm>DkxqdFo5oX1LggK$;DRDAjWfkIs4_rAm=? z6kL1|!~%LSu;uGFbn<~LX+6(!4?QMkEL9d74cBpNK7Lj8tzripY)Qr7oO5FKN zjVL4;Q~rkP37~@3RO9hP5{MK0zjxA`=^O0j;`z6$I!$%zmDu0^#;C;mxuHssnj%$U zj}Zfb4_bu@kp2?Fmrl;kAK)IdorwZ*DK9v#SKyw>yuys_O%_hhzQ83P&F6tHQP`x( z3oapr8g(YT$zsY+Pf*VYoqht6$WPH(i#9ylb^OfbIma_+KV-~hkjk`CGcD>TU@X(c z`l)FrVxpg-|FaRTI%lu`tsYW=hsF%11C1L6Jb_}Oo*Gf zy&x;z0R^%GZ814Yn=8Y&hkw%w|2|kR z-NmT?On0S6Xr)i+@KD^un5H{n5XCW=WWO3@Y?B^xH=l!Z(Z>vG7}h&5qF+Zk{~ z9LWe{=Xdp~v!(m27<>@K0(zw9H}#`!{b)x&w*5Uq{k>95RIXG&RL+N+&5C!=G3ylG zy|iJ}T{2x0de$($oHlEbM>P!MTEWw3@S5v!!w4QDmWQSNOz{5IDLiI`2~M-J5T}JN z7rl-atz> z5+z@G^y%fNiL^4*O@Q%45?UTBhXUVINs1iLgce+KFnKW@CV38uLaA31m_W`C`s{jr z+B3(1cqtLWi0PT32U4CoKsx59`awzFlkMrI9WI6V58V{s| z8Xc}~ruD8>w&lDN#_=%sdl4&5Ny}xW)qgsP==p+`^M89dt+j6jqR-pq+FR`bZ%6G_V@!*43+Bu diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-312.pyc deleted file mode 100644 index dec701f1daefab350bfc45c196b76c1ca2b7ae02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3445 zcma)8&2JmW6`%d&@szwvAhBUKF)6ER(*U*OB!UwcsA|{(YVeQ+v%8d+Cb{hF zO12oPQ30VLfd+~WEtON)qCn;9qND$S-g*&17gE-=9qFus z_%ZB5BN;{|@SRMCTZVQ2z-0Iu$uTOoDP;sTr1G0gM$`g9qJ~vrlj*J?sS#C#v7i=E zhtxpdNJy17NhS>X;3nzXM{48&lNq{AlBaC;=ABCD=ACm?Qw*x6lAIkXQ!S@at)ytS zSj?7xG&g4lpnotwJFEDi#~twS$4zwp4CJ>=gILhJtu9u{Cjuc=rpoTIu8tv9k_X*j zt8#mctE&>(4+`1_2Q6VfzR$Kh*K=%zKHKhhuaSq$VlrS0=1N(kwj|4XNw;L#4#`&NKi7{CHC<=dl|t4uZIPjYi3$4nQ3=DGks$*ee+JLyhg(?ekaY`X{a7(NfSHc%?ppE zbL5^ahjp^;_!&s^nOYN@iSK6_+YvNIStnu3R zYlrdKR(y7U?9o&^zEBGsMJIQbK9@g}cXRv82Qyb&(W?j2ReBmGQt$}*4S5W2=hr|W z0bJe#k8gmzkM#8bjGvSY2v=3~E7iL6oveZ^q#M&%4OFFSaLUqQ5@<;Tege=AendxxDiL{Mm?CLM$wIX1mfP6 zQebPo3uJ>FOJr=a@&5Mv?a}kK&{1rpF|$3hlWXSpFSTRWFbAWUgPql8W&cV${#GsU z_h|gn*LSWrf3hz=3bdow4y0>t@Viw8CdT|b!T%>PQ17~-&wDiv)ZONs)e}y+Uk}%X zDwFR|t>;vJeCJb*i}q(t{G8(+ip{DMk@46Vr6kZAX-B$tdYcwx9#Dc)Vu5 zg(wNmfHR$2(mdgH_;B+U5U6O;(X9{bAMVUINB)%feWD$`T;q?V$X2Kx`gH!WG=Y4$ z9zK+&ThjE-Li3efaX<1{x`O&hJ#r|`w4|AxrRMzZ*#6Ale1!EUxr?x0!h$bEnD9L8 z_?OSR_(F?77g>PHYG43abQ-=RDbcgg^}$FiXQ`=C7fJtn{g`-WeAKv@8Fo;Dd6Hef z%4r`ueab7~XTAw!gM2YMeK>ldHF}{vdJz#c-bidGnq%$QMMTd;BfXt&UTDXY{|7l; zbijnjJII*==6UyqS4xtMTb3E8ESu=+A%8?JQ$v9RrI(h0)uCWpYn5@l{7x8H07nxc z0F^j;dh4V5M+eeb7u5Z5#m{m+Z3&qEN6bl}KJnT*3B-(_e}7ZF&B6VRF9&=um^Qy+ z=>>bz-$nlh;31~bB=!!U=C4hlm*a;%B#? za-#4S)8RlmW*v5zhtUYgPg%YC3RJJp_47 zX%7^wZ0Sac&UJNqvAAMks%!~OFgkV+IjUx@tUc_qPWn?$_JchS!L<=?p{cXnL*g!t zFO6@cAG@)?O5Xz!Az|JCa?CLd(|Lt3r@ka3Pw*FgLWZA^ao7the0=6tXAYT(7BlgT ey~HdqwbNUZ^~tS7J<%a3Lu}Z|ubd%gss94f-OsH6 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-312.pyc deleted file mode 100644 index f0844207002f4f86a7addf25f636d4d6b11a95d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3694 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 D_yGo{ diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-312.pyc deleted file mode 100644 index 9ce4ca1743dc1633934bcc24e287998621d2a9ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 639 zcmZutF;Ck-6uz@fVla`AXb`B1U?c-R0b>wD7!X4A^{U) z_zNgMrBkLz*NslysN5-2-#JbJi5uSgzVE$v-_!e1sjMJ)2cO610`f0O7Ozx*>;b?C zF~qQkESzV>!W?%O9g7Lb?`aA;MNAnYroxQ=s=0vOe22Gscl9loY^=_;aZ$3$DXwe3 zC2Ki_mM2`0Yz@E&@qBAbVc1errLGF?%eRx}cSFXVh&w^VT6In6l=f_&Qz}*{^#j)T zBwnTTs&9KmrA#Rc97-QiCOu|fNhk0*i9()4!N7Go#0eNDpWN6X=79GG#B>yK>6J^ldzzK?GJ diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-312.pyc deleted file mode 100644 index 50b7f79c8e1c91bc27ddd7cfe9abfb15d04de72d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4604 zcma)9O>7&-6`tiTxw|4MiXvs|Cyr@LR6{!!t;kLsB$2|_cH%f@e(WYOn_#=7^)oF+&Gp*E+aJFS;p`4}nj8v&$tJ zg$X*q&d$tx^JeDFd*6HWFSol3L3!%UI~R_1BlIQdSjEw3Y%>%>%Sb{Jl}2&%PsJ&t zZ;R8$pK1K!|>9m^2 z>-3D2-yk~L@IsX!6st5OqwSx<$cLai84-QOZ4woyr;%j)0~KdV=mI6tSGl-d;w0uO z6?e!ik0f5QU!|IR;-oG(7ZvAaR&q*g8>Op+u26C385DNu^xLP-6uO`aVmXn+Qkd3R zQIfE%C_0zNIW?EarFF;oq?!@)3F}_TZW;K1b0$z6qUV9raYtiS>Z7LS#bh|Q>m$A+X zLNc3F1wro-L`6vEXAcXF(_^x}&XeJpWL7b0C_`Hl`SSFi$Y(f}oJS1dTHP=uG5dE+a>9URH5#Hkr5>N#rCsayqFbB4e|1 zb~chuo{!|`)Qh?7#Mt;#5hbb02lHa$k~l3Z5h<%g%=MJW$yfw#aV$TFJ+MH*MLO2$ z_QQyB463i^(K_c|V5?mJN}w`&j|wCYl$b?Vz5lU~yKi<^uGIPu zLEl~9HMpEDXDe^jc0E~QK5}k@9F%C@6m&TFh&Hyn4R4=DMMiCSE8H1MH7o!)n#H=C zW9Yppgx&>?qP^%)n|CTv;PH)lz;giTtmcqtjV*v?3m{#g7F`;XYTj&9G*X+EquC`} zf!HBi0J62F^{3F{K?W^G8SvN?+5e8xvbG{8+2I@=a0x~M`*s$MyFeGfSHQy@F-(rJ zL3Fe+57i?oSl~iRFeC*-3Rog9XJ;}p7FF4_6gw8P)3WXm^LaTd>9nF^ok``A*_KI# zdF%s8x+8~^)5)yfh2^xUCTEicS+`xftTW)PicTe=0hX#$DV+JsO|dV{x_qlEab;DA30sjn}#dfGn}b z9>8}EoPirAl;?r6_)i9@K}$kvpVeypQ=wMdYPId2w*eNCh^Vm|2jdN_1B~Eeg&`}{ z1~W5?8ayut&;IE3U=9a=eC%~hA}uB+iU*p3LOaaz@UovUD8d95#e0GM;TLL#O2|ew=KVT!BtQywdGh z5>*l6t%_%|34lGp%q_xRolc|`Obib98SHBA5+-`a!=&qOp`4)RT4>mSO<;z_ju1`= z$#_=kE2u;L6b(#f6vu8`#~5uUiS|y$I7vqe`L+Szv|x)BOd?t;zDup=S13(8-+6S zz&G^!!sov5>czk9|9tG&`ryI4W2b6^uio{)T5pi*BjNJt2P5I7)9YmE1J%HR)#IPO zPzywtn0k+Yg(~x<=z5QD>BJ3ZDO&gRER8R{Up{heZhheKmG>)qE8?xt@AhmV${SjW z*8PLar^}}+d%y6HY|=3FpdXlS@|Clz(x-3SzE}-CQ|&)mIg`(| zC=aZ84^+JeR!`Kt6D7yLJ-tiMtemL4wEE7cvo+7;U2f9E^@bOKb`c_N1J_-kXTWu; z6RxY^`DRHGzt~Ya4L#iF@4Pb>G19bLHn&)XLdU-n;c441&Ly zQFheu-=Dnm1JShHBm) ze78S>rJW7av9mF#Vka=kP~edW&m^6byB@WXzs?_C?TiQf)J)Kd4($^%DrgV9wy2$ zDiREom!bOlD*6xO-{O$>sri!+8ODBa+r`>nq&9nz!~5X1Gh6g-`_wiKolP5QO)~c2 zrVF}THl{&%NLq%G8KT48n54b<6jZt!0@YYDlbKP)^XZ1SamJY8ainELF+5JDK&piB z(_|j|+8Hwr@~z1vchK!`lAvcSbQ_tAl0@ne_q?giNi0K`99g*r6#y+oZI2+T`#y4i ziTwAG=RWG$WE_;g?j2p)wdNhIdPgfWHSfXsSH3;6*^O-W4|o4!_Zrn-rTQP*q7*e% z8onN02w#sZM79y>gLZ5KUm2vYH$8~+KxR+*A9#C-MBfLmY_iaOWW>tfjzlNWXA^vM KivDbpg8F|t%hq)O diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 8468c7821f59d68286d9bd799e928043987a5628..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmXw!%}T>S6oqG!loqA_AnsfQbkl_ytV>0RX`!JSLQ`t(W{8=w4W>!R#6aBn6z<)L zkK!BLtuLT<*OfD%2kzlJXLIlUXty_zRCn}#e`X-`slvasKUnQZctH%!&>S)Rif6di zI{MRrlY~N;u4fi3EJL%bb8bee`MF6F~;9V2p|3!+nd^Rv)GgEFPR-=e*gdg diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py deleted file mode 100644 index 3997f84..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS rdata type classes""" - -__all__ = [ - "ANY", - "IN", - "CH", - "dnskeybase", - "dsbase", - "euibase", - "mxbase", - "nsbase", - "svcbbase", - "tlsabase", - "txtbase", - "util", -] diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d4fd5a1da4cffc3e14ed35ccc955035a3728e477..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 340 zcmXv}u};G<5VeztMukWqBsQcBEE$N^vLYk|0}NyUG0??wY|~hE<0$7w4I5v<&W89D z{vazq0Cej_(!fb~@7=qT?%hWa3{e~7+xN$jhtOx&{D<>LR=X{EK>|(C6bbx_C%ASZ z_vzp=xeH6dEDLR2$dGFWAR*dm-1Fq2ajuibxwvYEB8Qo*QwFlx60pI6T&P}{FMG`U zfe#f=H;QjHuzuMt?S^;@t8@}M&45y2IHet7T2uOlR-M`PJ;}^mlCqGtG?n5R;YLVu zqkxmRl6gh6N=Z@M%;YEWXiPv^d0a3)V^ayFbxKOnJp+VNl`BhWT-0m-bZ*2#%d>L7 by@K0>y|#S07~}6Fgb#nbVB2{fl%4tuLUL-g diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-312.pyc deleted file mode 100644 index 0f162fabe290039c5e6c1abec9c41574a6a7ce43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3864 zcmaJ^O>7&-6`tk(aQU|qE!h@rD`srj3Def}Uu>sQoW`--%7N*^G7^NB?Rs}8Ek$zK z*_CYyG(Z6cOaV=8p(tpdEEff0Ll^hd^yZ#gpqCI#A$ILUnw)%7Egu4(`ev3((uCRp z`R2`gZ)SGi``(+`f5c*81nuahwcOhggubT(zeyct^B{*%0~yHR3MlLKT$W?wKsLa7 zJ}a-{x=9>(_XX&xWy(-_eu%+*UHSrU+|F zFlWrS?DhFwJ>KX1ebzdHtb)V&H+*fc&klh+*&F*@b{ORQ{86vZ#w%PZ_?!wdDc|w_{o$jWG`l>^qwt3;8NB$=~FilM%1C;OfJhC0dO9l6)QqYbcJ&TZT2~} zeqN)5PRyhvS60=MR>Z36hE=s_84Cr1=H5khpyt%)pWtUk}g-AIjeN+(2=8Q+jQ_x%9=i}&0;%klgI=W$X?diF_zb&D7&!4-25 z72B1|#Bwa%D!3u7Fl!OhnJc$M361rEz8r=0GM7wyMx)>t{A{6yGjC>~p<-t1QiL@9BN6 zD)z12-5Z`G;al|js?=v2^W_!vh|8oDSF{((m{4$=CDTz=1}8wcW~&Qk$vJX@jKCB( z_61dGdZ>#Y|3MIcdj1% zMovB3p13=4KYcI#@IY(V;nwyeb>TPR_Sl~L$~|TEdTZ=3jAPFdd+zVOw|6~pw3#@% zo;cY|oct=(N}K_m(jM8_IK6Uub$@H*U|sk^X4$3t$pm|%5d3LCHdlc_3FjavzzsH# zcEH!K0cjUhJ-O2bWS+WVpykE-G5XtEhi zu8w{=_F$|TJ^b(zkd2U-4F3m-!{H4ihQeMPi5;KDcJ>XJ7H$01-P7=EzpFib^ZHJhpS@%udD(RYoOYm0-E^1H&O0%kI;F4qJ zOiXN7!X^{N@2Blp|KrgUo0jWV+Iy!Yr2OHMAk4_-I2}QCh>vy4W7s(fDBHgWr8v zm)oN|8<~|%YjmO>YAf+|7VGS0&hEV<&C>`OW9xN|24Gj z&dIyyR;M1Gdi?nlzBP3AYuP(R_5P?+Z!=8&9T+%Zx`^5;uUQqK=`heBPrC<0PX*E~ z@_el`|AtuQwmfdSM*;`9*wSLls^BAvLls&NUI}mh;>;O9j|31|&>dHx_mIKZiPzxd z#aztTt7Ph0fffa64r>NrioUZKYcJ9=phLZ868w5S z?+B;B+CB{g%6WWeWB1DLyPvLJ{xbU@`*`%v<9`}|BK%GHtI~>JtV?Y z5;Np|up#sqAoNk5Q!W+g<|lQ2jHYcjFQGiYORakeCzUl~W8&r98LFeFtWC>LCz(7C z53{VHE>^u6i@OI?s!w^IuJI9&8u~WAb3MMV8Q-^hq7{D!JWR7?UsKuFQYL!2a_-@g z2h$I)KGq&pzB>3M{+HB~nby$s*YY%TI-u%uiqBkzJ9;K_&Z|yQLJJ+CnNR4Q#0{#d zVd-$8hC|;jSdI->5qiXvA5(3F-2;jkZpa3CkBUF0WSZ(?om+yA3k6k8apV9#EnWXFxT#pX*U6BYoOk-4vuj{z%m{!SU zL$g?1aI`CsKOw61j$81EM&HN{PL~||P2ye6LS19>7Tra>M6b`@OVWbW`}`pEs_8u; zy)nI#C%vzh)8sQ4QU}>fKsI=e<2EM{r#wZGrzrM4+VK>PvfuCv2}LGq!Iu)xZL1%@ zJ-Iyj5&^prMFH{F_|o_~H`e6FUIyOg&U1H08i|!eWADn|O+?4w!9ZezsW!443Px_t aEzQ;Vb|`jx_ww$V)CNgqsj?)nNBl23LLbfm diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-312.pyc deleted file mode 100644 index 8f45bd47fed55612947f2d91542122202811bee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4163 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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/euibase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/euibase.cpython-312.pyc deleted file mode 100644 index ad19cf7d87b090db21f076f5532972ac97af8ffe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3523 zcmbVOO>7&-6`tMYlDiZoQlcWuKXPbWcC)r5x~`EQw>F$gaq6O?Ys+m>q3vS5JCar+ zxy;PUB8iou1_mO*PGuho$!H2INPr~J#X02~)HxW)p`_VD?$(DCIr*lTJ{az$Z+5wq zoU%P+fSrBw-prdfGvE7WzHD!gA}Gf$tWTT)>1);qO1L4^uY<6H3}kRwH17Yoao%eS z<2>f4rC`X`zm<+hZz6-giVOk9c=K-cXVP@+`8I2<3%+i>VH%H=I3@ImwK94`=lvS} zQ2lq%dB&{b|cj3Y8KWJA2lHT_FQ)Chx3 z#u1~B!$>lQIw0neIxXj@njkXdB)1c%Tc zxfNtLjY}a1y(=Q*a1OuD`)Uq3Xc~0iE{?Dc<5_W#8mtuAvae&>>EN^i75ulxwA$^N zR}dm6?dG0OcWfKl5T`r;TW4@ETZ_{(_%cNPC)nZEcCdv0ECw;X&6SWMW?TXG?-9dK zIhi7cW^1V_jizi1ewT5o98O&-*_bM_E7I8lCa$EaX5O?_)sLM)<)ZiwRSgc~7-p4BLI!^E&l;N9~htE#6}Rm$2a zC=1Unrl+hNPLl$*iFMi3r_#D*;Pj|T_4LqXoWGpTnwQdrl09YRe=zj@V`*yIc(9=9 zGukAkX^185&q31wkxPJ_p+bqUT`VUWP#OwWwjZ=~5~`;Q=%LiU6sbzRt2@>X{Y_FH zBzLTwx_N5tKrMM_F?3ye*w(RVFBg}J*G{gE+#bC(dcRG1*!9B7;LX8}F6Dlg^5EqY zpXENyeXiABe)C@UU;Do7t0aC^7x-kyCgR&WHU$(rT!*QU-b58N7?#@LtN#Wnz-$A1 zkf9Bng1T9zx8uE`ZH-6BKF;6`;XU3FKIHzuK_-PVkC@9kn_*DlU0I)+&CgJ?j9r=H zf<`nO4lG0%HeJyJh#OI-aM7G7dB+7dhEX?c5p&YayHSF(nr&WY9{8C#%7SA@NM2yQ zBDSU4xM-K-!KOD9hrvp}2Ni62ylc6%RN9EAs`1oX_eZ_&_g3Pgcg}y_UyYC6+YePm z8YMl@S7+HBlxC)%fd#D~N0@HzTF?bDvLVh#?B;$uksv2v*IMkQm!|`EGrwCg>ohy9 zZxt_3Tlg!3r(njnIlDa=X7Dh73l1;@me)q83}`ikfR9JeZwC;Xmz;Y{SbPjE41z@257`NMFc}UkR+i$J`- zD8RHzn=9y9O1i)sVTbDqwlzccvgV7d*}j$Es%^O;7M;tPZiw9-E{}_pr2@=7#Mq|Dx^IS2qI=m{a#n}e zx@z(M#n2`%$Uo;ECU0*Nl&5-kPWOD{tyr7KpxiPd8Xz0%WLjCc)e8)`sRZ#7G&q7SIDJ zwj5oG-Z;MU>djaGF75Ftef!Fo?5*r)Ap9>%|NkqS7n|fFZjlT5Avk2W9ZNR<3B%Gy z36o>++XntH+<+9))5AXthq7zHN45{_gok3bSk>=a?tKp5+bqLu{6VM|&{rMZ8y$PA z9eY>lZReI#>lj!R{t@r;fHPc+zf_T4^0q_aGZz&hb1?%|=8PY9jJ?6~S`Nb-EUKzG z%b3kFIi{*_&1%_(C8DZ^rF+jbm1ZrQs;c+gA^RWzx6OND_FlMKW*-m>)CLz|9RgB!}Poa8x75yvJ1Gx*S7g;#_BpMbw;KeOwSoVZF zzdASeSV)M6>jE@3c_#I9VxMPw%=4n+8J;l7Teat0kqBIH5DvWEhRj@U*48d%vHuW} zna+QV$Xlj8th;!D5s~NN6XbhiFOO%^kNk}~Mb1N$t%jb23bKvk>iZFw_!@Qp6D1%f oUVNwT_k9~&ZdLBW z=1oUc-Kd(e)oC;DoIOub8kS%ppP4i$J_pz$+D0zxhN;dR>4vE8%z+LRW+HAxRoRlN z+PFo*w(!GfYTTMMQgq62s5N71<0;M3jnuGdYpLXnk)KKB%sZ*6IcMC;pG%(YOWCGl zoSDjKcQa##oznAm$~(zUP2NKwC8y>nzTHCH^H(ys!;fLo?gR4u0{NR9TMlo_oogNI zz2C}--D7WkmHIOE@Kov8J4@mp;=7%1eii#Nwmx3!Of89D#LBRXXcddX06o?K|K(4B ztfGyfKiDQ}cle97ew-I|4tAoh1Fa}#`2tu8fX9y%q!YSw!cGXD9b2?<%d!oX8o7*P z&X~>|Gjc+rG0@_MENYIKc{duY7YvubJInUk=nZCg+JdrS$5I{Ro>OS@ZRL+o!lHc| z$O74I?O1*9!FxNcecP>lkIsKHSZcktB=5$Lt{rD4yRg;Oza8)2lKUw}CoK#lXy|4% zLHRZiFp@Cg!!r=TK6q>MVOXzcFtI2VLqRB-mw7NY8nGBI%A3CFMhTTc)dufI#|+2E zsVgIo5KTl}LCe{+31ny-h$}eOUD}Srqd1IJZKXO^6|*)z9A+;hp3NdzYE61pYxb>H znL(4;=YTAbJ((QsTD|??cB%cuQgpZV=<1~hm)1iMqmPD5tyh-hzs8T;e|PQL`mY|z zrTD;>Jm8tH@)AKw#e9E;0mOug@h1E@0MnIr7?OgRF)tOliCX9QJBv~f2Ut`=GahIY zi1P!DIY59I;yZvNa}25c+aE$G2-yyu)*M&FXX=FuX_msf^#F{hn>DGC2ziJVvt|w= zqh?J)ptNn~7q?N%VdSGK@yFudOHdo zwOFlL7=W#I3djO^+Wy8)d+&C8Z>jwxLbhx5@`KCk=S!`p4uxw4lAr*2_S*5&4-1L@ zOHFWI%;FdVY~1Smv`w9&mIg2|^JBo85Y3#q!W%&>ud{d=Hrr66!3}oftt+1`f3_vR z<$166o#3aj&$|IkZHBG)n(D>my03=|yct|fbOp#sm>(>dfDRrR;inVi28R#n~7U>dTy zowFQURav&Bop7+*z!LnV0m)jY3e3<`NX{TZjM4Kz+?ZboXhtrlsv8`|QkY@jXAHd* zp8_Lz8#@c+`$h71ao}l7_gZ1A`P9NMUx5`iC%+iM z5)_I>339gRHhq*2;+dfj-zt3ldPU=D<<7LD%#K5zSJ3iNeBDr8<}F95>IDHK599od zK^;R^97`D*8CCFgV`Vy-aZW3lyzc2?Ds&TyCI?~01Ok=1HJjIs%1N{6uhVgQu7~ng2JuI8S?V;wMO0=EWkzb3LAe7gQEJ|Ko9mj+ZT%3}XE- zEZUg0Ys=KpUC+LK*u^V8zKMJ5`}mW`@?h1+^cQ&4K}VwhD3afxo{A$mY4E!cCtXBGUi5UJ zP@>5L#*ct^V-UQP@EgsTa!e~v&sOHl$;oN27M$ict83S>{!1-8)?8HrM%r@8yQLrO}m-mp@)nm(?=CF~m5&YmY7MUFQyL2yy-Q eZ?eoB~5C0dQt;90` diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-312.pyc deleted file mode 100644 index d4851f76b1ff84569201cf418f0f81e007d1bafa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2825 zcmb_e&2JM|5P$pWwVjZd5)u+9tBNAFNo*BVXe)#$LTM|tZiQ(jeCTSk`;u&8@49a{ z32}{-N|8uZA;_Uhj=5AK$|e6tFGMMVE*w&E>Mh_L5U0-UhvNo%Kk05MP5de}L(22n{7=4#ERrDGL@q&XvVQGRA>>N?EcLOI+Z} zGEJq4mA0e>u62FI%2+b!(lljdtyG&Pvq)}oQ5NHYI|yUal=uIC6!UrWmZ0reS?it-xS;Kjyzt!c@4;~+zRnq$|bQu?ns0*xCXz$ zM`DgN$Rx-Pehp!Ri)0O=C*dG-WSuK&Vjy|bHOc}-*KNo4bsadv(;cHq8SbS?f#}i7 zSdd~?80A1_mOlqeP#dImJ(TLYhjSIa`B$aFgj=NrR--<1XKZt#V7eA9d~JJXAwNT% znL@=LE!5`x3D-H7|L|TGt4PtoO%Vz@e0uyUcvDY-+XP3LBf-IR}|iSa#09kxJN<{EJwDpMQeCdDjcgwsTomtQn_xxEmisWl@Mht^`7MwY z3?7UJzsdF$v9dkrw?w}c@`jiMxsvHiI~@O-9849^c_ zm)YaC6J!#7od*2WY`C@+R+V+3ZOV6bpWgKAhvHDg^HZP{w5 z{h7`BcJ9ie{Iu)vz4O2P^g!un_*zZ8U-F>`VWM3G1|lI$#tD-c;MfowQbSIj;JZ`o2u^!&+PUwu?z@r83Mep>@Qb`o zk1@9z`Q07+-IAOEYwvv^^JG^chkI6zFCSkWTbtY*-p*ZKl>Y?3FRYG09&`;pPzEFC zTadvrtem0qmw|cPG-wC_Iwlf)9K5Lz_H{mj9kH6V8C{QY7RT%g4HVO@))@7?FbYA3 zX*jN9n?}X1(}QR$5R-6={mhRA1vt>_2LgC29e1-!*+vTSu#iDf%jkO5 zwWce`cj)?$(?%uMq;%bKO%OvV^D3_I>3UdeST7tMWW(xKrBJFY_AW@+X(SrzI^)`B z(n>|wHI89t^zh|e4zreFUW5d3^1cA_a)JCMezKE0b}uvk<(|k(LrsO31|Vh1->3{(?ta)a)S_LK|*v!mh0^ZUm&+2ciR=PJ|8zy$+hE6dd}_ zNs^FS#ZvvZx9e6z>gcqu&Q3r{Q!$De1Q^13amTuhDm-HrqDdaSNY!?-~pB^xlxeNEcZxR?n Lqvt$u;Z6Pl`DJkt diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-312.pyc deleted file mode 100644 index bc9f3256b62872370d574b8d5d5700b0ede6eb1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28482 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 V0@_t9!6Je+;}h&pLYN`@{|7q42I~L- diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-312.pyc deleted file mode 100644 index 59e9f406637a53a4aa748834fba5f6191c9463c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3361 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 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/util.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/util.cpython-312.pyc deleted file mode 100644 index 29b293a904afdd3987ac64b2bb993021be28d152..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12831 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 Sj#sRh1GIm&)lPfKEc`F!={Fky diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py deleted file mode 100644 index db300f8..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2004-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import base64 -import enum -import struct - -import dns.dnssectypes -import dns.exception -import dns.immutable -import dns.rdata - -# wildcard import -__all__ = ["SEP", "REVOKE", "ZONE"] # noqa: F822 - - -class Flag(enum.IntFlag): - SEP = 0x0001 - REVOKE = 0x0080 - ZONE = 0x0100 - - -@dns.immutable.immutable -class DNSKEYBase(dns.rdata.Rdata): - """Base class for rdata that is like a DNSKEY record""" - - __slots__ = ["flags", "protocol", "algorithm", "key"] - - def __init__(self, rdclass, rdtype, flags, protocol, algorithm, key): - super().__init__(rdclass, rdtype) - self.flags = Flag(self._as_uint16(flags)) - self.protocol = self._as_uint8(protocol) - self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) - self.key = self._as_bytes(key) - - def to_text(self, origin=None, relativize=True, **kw): - return "%d %d %d %s" % ( - self.flags, - self.protocol, - self.algorithm, - dns.rdata._base64ify(self.key, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - flags = tok.get_uint16() - protocol = tok.get_uint8() - algorithm = tok.get_string() - b64 = tok.concatenate_remaining_identifiers().encode() - key = base64.b64decode(b64) - return cls(rdclass, rdtype, flags, protocol, algorithm, key) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!HBB", self.flags, self.protocol, self.algorithm) - file.write(header) - file.write(self.key) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!HBB") - key = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], key) - - -### BEGIN generated Flag constants - -SEP = Flag.SEP -REVOKE = Flag.REVOKE -ZONE = Flag.ZONE - -### END generated Flag constants diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py deleted file mode 100644 index cd21f02..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2010, 2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.dnssectypes -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class DSBase(dns.rdata.Rdata): - """Base class for rdata that is like a DS record""" - - __slots__ = ["key_tag", "algorithm", "digest_type", "digest"] - - # Digest types registry: - # https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml - _digest_length_by_type = { - 1: 20, # SHA-1, RFC 3658 Sec. 2.4 - 2: 32, # SHA-256, RFC 4509 Sec. 2.2 - 3: 32, # GOST R 34.11-94, RFC 5933 Sec. 4 in conjunction with RFC 4490 Sec. 2.1 - 4: 48, # SHA-384, RFC 6605 Sec. 2 - } - - def __init__(self, rdclass, rdtype, key_tag, algorithm, digest_type, digest): - super().__init__(rdclass, rdtype) - self.key_tag = self._as_uint16(key_tag) - self.algorithm = dns.dnssectypes.Algorithm.make(algorithm) - self.digest_type = dns.dnssectypes.DSDigest.make(self._as_uint8(digest_type)) - self.digest = self._as_bytes(digest) - try: - if len(self.digest) != self._digest_length_by_type[self.digest_type]: - raise ValueError("digest length inconsistent with digest type") - except KeyError: - if self.digest_type == 0: # reserved, RFC 3658 Sec. 2.4 - raise ValueError("digest type 0 is reserved") - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %d %s" % ( - self.key_tag, - self.algorithm, - self.digest_type, - dns.rdata._hexify(self.digest, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - key_tag = tok.get_uint16() - algorithm = tok.get_string() - digest_type = tok.get_uint8() - digest = tok.concatenate_remaining_identifiers().encode() - digest = binascii.unhexlify(digest) - return cls(rdclass, rdtype, key_tag, algorithm, digest_type, digest) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!HBB", self.key_tag, self.algorithm, self.digest_type) - file.write(header) - file.write(self.digest) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("!HBB") - digest = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], digest) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py deleted file mode 100644 index 751087b..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (C) 2015 Red Hat, Inc. -# Author: Petr Spacek -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED 'AS IS' AND RED HAT DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii - -import dns.immutable -import dns.rdata - - -@dns.immutable.immutable -class EUIBase(dns.rdata.Rdata): - """EUIxx record""" - - # see: rfc7043.txt - - __slots__ = ["eui"] - # define these in subclasses - # byte_len = 6 # 0123456789ab (in hex) - # text_len = byte_len * 3 - 1 # 01-23-45-67-89-ab - - def __init__(self, rdclass, rdtype, eui): - super().__init__(rdclass, rdtype) - self.eui = self._as_bytes(eui) - if len(self.eui) != self.byte_len: - raise dns.exception.FormError( - "EUI%s rdata has to have %s bytes" % (self.byte_len * 8, self.byte_len) - ) - - def to_text(self, origin=None, relativize=True, **kw): - return dns.rdata._hexify(self.eui, chunksize=2, separator=b"-", **kw) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - text = tok.get_string() - if len(text) != cls.text_len: - raise dns.exception.SyntaxError( - "Input text must have %s characters" % cls.text_len - ) - for i in range(2, cls.byte_len * 3 - 1, 3): - if text[i] != "-": - raise dns.exception.SyntaxError("Dash expected at position %s" % i) - text = text.replace("-", "") - try: - data = binascii.unhexlify(text.encode()) - except (ValueError, TypeError) as ex: - raise dns.exception.SyntaxError("Hex decoding error: %s" % str(ex)) - return cls(rdclass, rdtype, data) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(self.eui) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - eui = parser.get_bytes(cls.byte_len) - return cls(rdclass, rdtype, eui) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py deleted file mode 100644 index 6d5e3d8..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""MX-like base classes.""" - -import struct - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata -import dns.rdtypes.util - - -@dns.immutable.immutable -class MXBase(dns.rdata.Rdata): - """Base class for rdata that is like an MX record.""" - - __slots__ = ["preference", "exchange"] - - def __init__(self, rdclass, rdtype, preference, exchange): - super().__init__(rdclass, rdtype) - self.preference = self._as_uint16(preference) - self.exchange = self._as_name(exchange) - - def to_text(self, origin=None, relativize=True, **kw): - exchange = self.exchange.choose_relativity(origin, relativize) - return "%d %s" % (self.preference, exchange) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - preference = tok.get_uint16() - exchange = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, preference, exchange) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - pref = struct.pack("!H", self.preference) - file.write(pref) - self.exchange.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - preference = parser.get_uint16() - exchange = parser.get_name(origin) - return cls(rdclass, rdtype, preference, exchange) - - def _processing_priority(self): - return self.preference - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) - - -@dns.immutable.immutable -class UncompressedMX(MXBase): - """Base class for rdata that is like an MX record, but whose name - is not compressed when converted to DNS wire format, and whose - digestable form is not downcased.""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - super()._to_wire(file, None, origin, False) - - -@dns.immutable.immutable -class UncompressedDowncasingMX(MXBase): - """Base class for rdata that is like an MX record, but whose name - is not compressed when convert to DNS wire format.""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - super()._to_wire(file, None, origin, canonicalize) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py deleted file mode 100644 index 904224f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""NS-like base classes.""" - -import dns.exception -import dns.immutable -import dns.name -import dns.rdata - - -@dns.immutable.immutable -class NSBase(dns.rdata.Rdata): - """Base class for rdata that is like an NS record.""" - - __slots__ = ["target"] - - def __init__(self, rdclass, rdtype, target): - super().__init__(rdclass, rdtype) - self.target = self._as_name(target) - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - return str(target) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - target = tok.get_name(origin, relativize, relativize_to) - return cls(rdclass, rdtype, target) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.target.to_wire(file, compress, origin, canonicalize) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - target = parser.get_name(origin) - return cls(rdclass, rdtype, target) - - -@dns.immutable.immutable -class UncompressedNS(NSBase): - """Base class for rdata that is like an NS record, but whose name - is not compressed when convert to DNS wire format, and whose - digestable form is not downcased.""" - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - self.target.to_wire(file, None, origin, False) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py deleted file mode 100644 index 0565241..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py +++ /dev/null @@ -1,553 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import base64 -import enum -import struct - -import dns.enum -import dns.exception -import dns.immutable -import dns.ipv4 -import dns.ipv6 -import dns.name -import dns.rdata -import dns.rdtypes.util -import dns.renderer -import dns.tokenizer -import dns.wire - -# Until there is an RFC, this module is experimental and may be changed in -# incompatible ways. - - -class UnknownParamKey(dns.exception.DNSException): - """Unknown SVCB ParamKey""" - - -class ParamKey(dns.enum.IntEnum): - """SVCB ParamKey""" - - MANDATORY = 0 - ALPN = 1 - NO_DEFAULT_ALPN = 2 - PORT = 3 - IPV4HINT = 4 - ECH = 5 - IPV6HINT = 6 - DOHPATH = 7 - - @classmethod - def _maximum(cls): - return 65535 - - @classmethod - def _short_name(cls): - return "SVCBParamKey" - - @classmethod - def _prefix(cls): - return "KEY" - - @classmethod - def _unknown_exception_class(cls): - return UnknownParamKey - - -class Emptiness(enum.IntEnum): - NEVER = 0 - ALWAYS = 1 - ALLOWED = 2 - - -def _validate_key(key): - force_generic = False - if isinstance(key, bytes): - # We decode to latin-1 so we get 0-255 as valid and do NOT interpret - # UTF-8 sequences - key = key.decode("latin-1") - if isinstance(key, str): - if key.lower().startswith("key"): - force_generic = True - if key[3:].startswith("0") and len(key) != 4: - # key has leading zeros - raise ValueError("leading zeros in key") - key = key.replace("-", "_") - return (ParamKey.make(key), force_generic) - - -def key_to_text(key): - return ParamKey.to_text(key).replace("_", "-").lower() - - -# Like rdata escapify, but escapes ',' too. - -_escaped = b'",\\' - - -def _escapify(qstring): - text = "" - for c in qstring: - if c in _escaped: - text += "\\" + chr(c) - elif c >= 0x20 and c < 0x7F: - text += chr(c) - else: - text += "\\%03d" % c - return text - - -def _unescape(value): - if value == "": - return value - unescaped = b"" - l = len(value) - i = 0 - while i < l: - c = value[i] - i += 1 - if c == "\\": - if i >= l: # pragma: no cover (can't happen via tokenizer get()) - raise dns.exception.UnexpectedEnd - c = value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - codepoint = int(c) * 100 + int(c2) * 10 + int(c3) - if codepoint > 255: - raise dns.exception.SyntaxError - unescaped += b"%c" % (codepoint) - continue - unescaped += c.encode() - return unescaped - - -def _split(value): - l = len(value) - i = 0 - items = [] - unescaped = b"" - while i < l: - c = value[i] - i += 1 - if c == ord("\\"): - if i >= l: # pragma: no cover (can't happen via tokenizer get()) - raise dns.exception.UnexpectedEnd - c = value[i] - i += 1 - unescaped += b"%c" % (c) - elif c == ord(","): - items.append(unescaped) - unescaped = b"" - else: - unescaped += b"%c" % (c) - items.append(unescaped) - return items - - -@dns.immutable.immutable -class Param: - """Abstract base class for SVCB parameters""" - - @classmethod - def emptiness(cls): - return Emptiness.NEVER - - -@dns.immutable.immutable -class GenericParam(Param): - """Generic SVCB parameter""" - - def __init__(self, value): - self.value = dns.rdata.Rdata._as_bytes(value, True) - - @classmethod - def emptiness(cls): - return Emptiness.ALLOWED - - @classmethod - def from_value(cls, value): - if value is None or len(value) == 0: - return None - else: - return cls(_unescape(value)) - - def to_text(self): - return '"' + dns.rdata._escapify(self.value) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - value = parser.get_bytes(parser.remaining()) - if len(value) == 0: - return None - else: - return cls(value) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - file.write(self.value) - - -@dns.immutable.immutable -class MandatoryParam(Param): - def __init__(self, keys): - # check for duplicates - keys = sorted([_validate_key(key)[0] for key in keys]) - prior_k = None - for k in keys: - if k == prior_k: - raise ValueError(f"duplicate key {k:d}") - prior_k = k - if k == ParamKey.MANDATORY: - raise ValueError("listed the mandatory key as mandatory") - self.keys = tuple(keys) - - @classmethod - def from_value(cls, value): - keys = [k.encode() for k in value.split(",")] - return cls(keys) - - def to_text(self): - return '"' + ",".join([key_to_text(key) for key in self.keys]) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - keys = [] - last_key = -1 - while parser.remaining() > 0: - key = parser.get_uint16() - if key < last_key: - raise dns.exception.FormError("manadatory keys not ascending") - last_key = key - keys.append(key) - return cls(keys) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for key in self.keys: - file.write(struct.pack("!H", key)) - - -@dns.immutable.immutable -class ALPNParam(Param): - def __init__(self, ids): - self.ids = dns.rdata.Rdata._as_tuple( - ids, lambda x: dns.rdata.Rdata._as_bytes(x, True, 255, False) - ) - - @classmethod - def from_value(cls, value): - return cls(_split(_unescape(value))) - - def to_text(self): - value = ",".join([_escapify(id) for id in self.ids]) - return '"' + dns.rdata._escapify(value.encode()) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - ids = [] - while parser.remaining() > 0: - id = parser.get_counted_bytes() - ids.append(id) - return cls(ids) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for id in self.ids: - file.write(struct.pack("!B", len(id))) - file.write(id) - - -@dns.immutable.immutable -class NoDefaultALPNParam(Param): - # We don't ever expect to instantiate this class, but we need - # a from_value() and a from_wire_parser(), so we just return None - # from the class methods when things are OK. - - @classmethod - def emptiness(cls): - return Emptiness.ALWAYS - - @classmethod - def from_value(cls, value): - if value is None or value == "": - return None - else: - raise ValueError("no-default-alpn with non-empty value") - - def to_text(self): - raise NotImplementedError # pragma: no cover - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - if parser.remaining() != 0: - raise dns.exception.FormError - return None - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - raise NotImplementedError # pragma: no cover - - -@dns.immutable.immutable -class PortParam(Param): - def __init__(self, port): - self.port = dns.rdata.Rdata._as_uint16(port) - - @classmethod - def from_value(cls, value): - value = int(value) - return cls(value) - - def to_text(self): - return f'"{self.port}"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - port = parser.get_uint16() - return cls(port) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - file.write(struct.pack("!H", self.port)) - - -@dns.immutable.immutable -class IPv4HintParam(Param): - def __init__(self, addresses): - self.addresses = dns.rdata.Rdata._as_tuple( - addresses, dns.rdata.Rdata._as_ipv4_address - ) - - @classmethod - def from_value(cls, value): - addresses = value.split(",") - return cls(addresses) - - def to_text(self): - return '"' + ",".join(self.addresses) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - addresses = [] - while parser.remaining() > 0: - ip = parser.get_bytes(4) - addresses.append(dns.ipv4.inet_ntoa(ip)) - return cls(addresses) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for address in self.addresses: - file.write(dns.ipv4.inet_aton(address)) - - -@dns.immutable.immutable -class IPv6HintParam(Param): - def __init__(self, addresses): - self.addresses = dns.rdata.Rdata._as_tuple( - addresses, dns.rdata.Rdata._as_ipv6_address - ) - - @classmethod - def from_value(cls, value): - addresses = value.split(",") - return cls(addresses) - - def to_text(self): - return '"' + ",".join(self.addresses) + '"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - addresses = [] - while parser.remaining() > 0: - ip = parser.get_bytes(16) - addresses.append(dns.ipv6.inet_ntoa(ip)) - return cls(addresses) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - for address in self.addresses: - file.write(dns.ipv6.inet_aton(address)) - - -@dns.immutable.immutable -class ECHParam(Param): - def __init__(self, ech): - self.ech = dns.rdata.Rdata._as_bytes(ech, True) - - @classmethod - def from_value(cls, value): - if "\\" in value: - raise ValueError("escape in ECH value") - value = base64.b64decode(value.encode()) - return cls(value) - - def to_text(self): - b64 = base64.b64encode(self.ech).decode("ascii") - return f'"{b64}"' - - @classmethod - def from_wire_parser(cls, parser, origin=None): # pylint: disable=W0613 - value = parser.get_bytes(parser.remaining()) - return cls(value) - - def to_wire(self, file, origin=None): # pylint: disable=W0613 - file.write(self.ech) - - -_class_for_key = { - ParamKey.MANDATORY: MandatoryParam, - ParamKey.ALPN: ALPNParam, - ParamKey.NO_DEFAULT_ALPN: NoDefaultALPNParam, - ParamKey.PORT: PortParam, - ParamKey.IPV4HINT: IPv4HintParam, - ParamKey.ECH: ECHParam, - ParamKey.IPV6HINT: IPv6HintParam, -} - - -def _validate_and_define(params, key, value): - (key, force_generic) = _validate_key(_unescape(key)) - if key in params: - raise SyntaxError(f'duplicate key "{key:d}"') - cls = _class_for_key.get(key, GenericParam) - emptiness = cls.emptiness() - if value is None: - if emptiness == Emptiness.NEVER: - raise SyntaxError("value cannot be empty") - value = cls.from_value(value) - else: - if force_generic: - value = cls.from_wire_parser(dns.wire.Parser(_unescape(value))) - else: - value = cls.from_value(value) - params[key] = value - - -@dns.immutable.immutable -class SVCBBase(dns.rdata.Rdata): - """Base class for SVCB-like records""" - - # see: draft-ietf-dnsop-svcb-https-11 - - __slots__ = ["priority", "target", "params"] - - def __init__(self, rdclass, rdtype, priority, target, params): - super().__init__(rdclass, rdtype) - self.priority = self._as_uint16(priority) - self.target = self._as_name(target) - for k, v in params.items(): - k = ParamKey.make(k) - if not isinstance(v, Param) and v is not None: - raise ValueError(f"{k:d} not a Param") - self.params = dns.immutable.Dict(params) - # Make sure any parameter listed as mandatory is present in the - # record. - mandatory = params.get(ParamKey.MANDATORY) - if mandatory: - for key in mandatory.keys: - # Note we have to say "not in" as we have None as a value - # so a get() and a not None test would be wrong. - if key not in params: - raise ValueError(f"key {key:d} declared mandatory but not present") - # The no-default-alpn parameter requires the alpn parameter. - if ParamKey.NO_DEFAULT_ALPN in params: - if ParamKey.ALPN not in params: - raise ValueError("no-default-alpn present, but alpn missing") - - def to_text(self, origin=None, relativize=True, **kw): - target = self.target.choose_relativity(origin, relativize) - params = [] - for key in sorted(self.params.keys()): - value = self.params[key] - if value is None: - params.append(key_to_text(key)) - else: - kv = key_to_text(key) + "=" + value.to_text() - params.append(kv) - if len(params) > 0: - space = " " - else: - space = "" - return "%d %s%s%s" % (self.priority, target, space, " ".join(params)) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - priority = tok.get_uint16() - target = tok.get_name(origin, relativize, relativize_to) - if priority == 0: - token = tok.get() - if not token.is_eol_or_eof(): - raise SyntaxError("parameters in AliasMode") - tok.unget(token) - params = {} - while True: - token = tok.get() - if token.is_eol_or_eof(): - tok.unget(token) - break - if token.ttype != dns.tokenizer.IDENTIFIER: - raise SyntaxError("parameter is not an identifier") - equals = token.value.find("=") - if equals == len(token.value) - 1: - # 'key=', so next token should be a quoted string without - # any intervening whitespace. - key = token.value[:-1] - token = tok.get(want_leading=True) - if token.ttype != dns.tokenizer.QUOTED_STRING: - raise SyntaxError("whitespace after =") - value = token.value - elif equals > 0: - # key=value - key = token.value[:equals] - value = token.value[equals + 1 :] - elif equals == 0: - # =key - raise SyntaxError('parameter cannot start with "="') - else: - # key - key = token.value - value = None - _validate_and_define(params, key, value) - return cls(rdclass, rdtype, priority, target, params) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - file.write(struct.pack("!H", self.priority)) - self.target.to_wire(file, None, origin, False) - for key in sorted(self.params): - file.write(struct.pack("!H", key)) - value = self.params[key] - with dns.renderer.prefixed_length(file, 2): - # Note that we're still writing a length of zero if the value is None - if value is not None: - value.to_wire(file, origin) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - priority = parser.get_uint16() - target = parser.get_name(origin) - if priority == 0 and parser.remaining() != 0: - raise dns.exception.FormError("parameters in AliasMode") - params = {} - prior_key = -1 - while parser.remaining() > 0: - key = parser.get_uint16() - if key < prior_key: - raise dns.exception.FormError("keys not in order") - prior_key = key - vlen = parser.get_uint16() - pcls = _class_for_key.get(key, GenericParam) - with parser.restrict_to(vlen): - value = pcls.from_wire_parser(parser, origin) - params[key] = value - return cls(rdclass, rdtype, priority, target, params) - - def _processing_priority(self): - return self.priority - - @classmethod - def _processing_order(cls, iterable): - return dns.rdtypes.util.priority_processing_order(iterable) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py deleted file mode 100644 index a059d2c..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2005-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import binascii -import struct - -import dns.immutable -import dns.rdata -import dns.rdatatype - - -@dns.immutable.immutable -class TLSABase(dns.rdata.Rdata): - """Base class for TLSA and SMIMEA records""" - - # see: RFC 6698 - - __slots__ = ["usage", "selector", "mtype", "cert"] - - def __init__(self, rdclass, rdtype, usage, selector, mtype, cert): - super().__init__(rdclass, rdtype) - self.usage = self._as_uint8(usage) - self.selector = self._as_uint8(selector) - self.mtype = self._as_uint8(mtype) - self.cert = self._as_bytes(cert) - - def to_text(self, origin=None, relativize=True, **kw): - kw = kw.copy() - chunksize = kw.pop("chunksize", 128) - return "%d %d %d %s" % ( - self.usage, - self.selector, - self.mtype, - dns.rdata._hexify(self.cert, chunksize=chunksize, **kw), - ) - - @classmethod - def from_text( - cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None - ): - usage = tok.get_uint8() - selector = tok.get_uint8() - mtype = tok.get_uint8() - cert = tok.concatenate_remaining_identifiers().encode() - cert = binascii.unhexlify(cert) - return cls(rdclass, rdtype, usage, selector, mtype, cert) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - header = struct.pack("!BBB", self.usage, self.selector, self.mtype) - file.write(header) - file.write(self.cert) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - header = parser.get_struct("BBB") - cert = parser.get_remaining() - return cls(rdclass, rdtype, header[0], header[1], header[2], cert) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py deleted file mode 100644 index 44d6df5..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""TXT-like base class.""" - -from typing import Any, Dict, Iterable, Optional, Tuple, Union - -import dns.exception -import dns.immutable -import dns.rdata -import dns.renderer -import dns.tokenizer - - -@dns.immutable.immutable -class TXTBase(dns.rdata.Rdata): - """Base class for rdata that is like a TXT record (see RFC 1035).""" - - __slots__ = ["strings"] - - def __init__( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - strings: Iterable[Union[bytes, str]], - ): - """Initialize a TXT-like rdata. - - *rdclass*, an ``int`` is the rdataclass of the Rdata. - - *rdtype*, an ``int`` is the rdatatype of the Rdata. - - *strings*, a tuple of ``bytes`` - """ - super().__init__(rdclass, rdtype) - self.strings: Tuple[bytes] = self._as_tuple( - strings, lambda x: self._as_bytes(x, True, 255) - ) - - def to_text( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - txt = "" - prefix = "" - for s in self.strings: - txt += '{}"{}"'.format(prefix, dns.rdata._escapify(s)) - prefix = " " - return txt - - @classmethod - def from_text( - cls, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - tok: dns.tokenizer.Tokenizer, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, - ) -> dns.rdata.Rdata: - strings = [] - for token in tok.get_remaining(): - token = token.unescape_to_bytes() - # The 'if' below is always true in the current code, but we - # are leaving this check in in case things change some day. - if not ( - token.is_quoted_string() or token.is_identifier() - ): # pragma: no cover - raise dns.exception.SyntaxError("expected a string") - if len(token.value) > 255: - raise dns.exception.SyntaxError("string too long") - strings.append(token.value) - if len(strings) == 0: - raise dns.exception.UnexpectedEnd - return cls(rdclass, rdtype, strings) - - def _to_wire(self, file, compress=None, origin=None, canonicalize=False): - for s in self.strings: - with dns.renderer.prefixed_length(file, 1): - file.write(s) - - @classmethod - def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): - strings = [] - while parser.remaining() > 0: - s = parser.get_counted_bytes() - strings.append(s) - return cls(rdclass, rdtype, strings) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py deleted file mode 100644 index 54908fd..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006, 2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import collections -import random -import struct -from typing import Any, List - -import dns.exception -import dns.ipv4 -import dns.ipv6 -import dns.name -import dns.rdata - - -class Gateway: - """A helper class for the IPSECKEY gateway and AMTRELAY relay fields""" - - name = "" - - def __init__(self, type, gateway=None): - self.type = dns.rdata.Rdata._as_uint8(type) - self.gateway = gateway - self._check() - - @classmethod - def _invalid_type(cls, gateway_type): - return f"invalid {cls.name} type: {gateway_type}" - - def _check(self): - if self.type == 0: - if self.gateway not in (".", None): - raise SyntaxError(f"invalid {self.name} for type 0") - self.gateway = None - elif self.type == 1: - # check that it's OK - dns.ipv4.inet_aton(self.gateway) - elif self.type == 2: - # check that it's OK - dns.ipv6.inet_aton(self.gateway) - elif self.type == 3: - if not isinstance(self.gateway, dns.name.Name): - raise SyntaxError(f"invalid {self.name}; not a name") - else: - raise SyntaxError(self._invalid_type(self.type)) - - def to_text(self, origin=None, relativize=True): - if self.type == 0: - return "." - elif self.type in (1, 2): - return self.gateway - elif self.type == 3: - return str(self.gateway.choose_relativity(origin, relativize)) - else: - raise ValueError(self._invalid_type(self.type)) # pragma: no cover - - @classmethod - def from_text( - cls, gateway_type, tok, origin=None, relativize=True, relativize_to=None - ): - if gateway_type in (0, 1, 2): - gateway = tok.get_string() - elif gateway_type == 3: - gateway = tok.get_name(origin, relativize, relativize_to) - else: - raise dns.exception.SyntaxError( - cls._invalid_type(gateway_type) - ) # pragma: no cover - return cls(gateway_type, gateway) - - # pylint: disable=unused-argument - def to_wire(self, file, compress=None, origin=None, canonicalize=False): - if self.type == 0: - pass - elif self.type == 1: - file.write(dns.ipv4.inet_aton(self.gateway)) - elif self.type == 2: - file.write(dns.ipv6.inet_aton(self.gateway)) - elif self.type == 3: - self.gateway.to_wire(file, None, origin, False) - else: - raise ValueError(self._invalid_type(self.type)) # pragma: no cover - - # pylint: enable=unused-argument - - @classmethod - def from_wire_parser(cls, gateway_type, parser, origin=None): - if gateway_type == 0: - gateway = None - elif gateway_type == 1: - gateway = dns.ipv4.inet_ntoa(parser.get_bytes(4)) - elif gateway_type == 2: - gateway = dns.ipv6.inet_ntoa(parser.get_bytes(16)) - elif gateway_type == 3: - gateway = parser.get_name(origin) - else: - raise dns.exception.FormError(cls._invalid_type(gateway_type)) - return cls(gateway_type, gateway) - - -class Bitmap: - """A helper class for the NSEC/NSEC3/CSYNC type bitmaps""" - - type_name = "" - - def __init__(self, windows=None): - last_window = -1 - self.windows = windows - for window, bitmap in self.windows: - if not isinstance(window, int): - raise ValueError(f"bad {self.type_name} window type") - if window <= last_window: - raise ValueError(f"bad {self.type_name} window order") - if window > 256: - raise ValueError(f"bad {self.type_name} window number") - last_window = window - if not isinstance(bitmap, bytes): - raise ValueError(f"bad {self.type_name} octets type") - if len(bitmap) == 0 or len(bitmap) > 32: - raise ValueError(f"bad {self.type_name} octets") - - def to_text(self) -> str: - text = "" - for window, bitmap in self.windows: - bits = [] - for i, byte in enumerate(bitmap): - for j in range(0, 8): - if byte & (0x80 >> j): - rdtype = window * 256 + i * 8 + j - bits.append(dns.rdatatype.to_text(rdtype)) - text += " " + " ".join(bits) - return text - - @classmethod - def from_text(cls, tok: "dns.tokenizer.Tokenizer") -> "Bitmap": - rdtypes = [] - for token in tok.get_remaining(): - rdtype = dns.rdatatype.from_text(token.unescape().value) - if rdtype == 0: - raise dns.exception.SyntaxError(f"{cls.type_name} with bit 0") - rdtypes.append(rdtype) - return cls.from_rdtypes(rdtypes) - - @classmethod - def from_rdtypes(cls, rdtypes: List[dns.rdatatype.RdataType]) -> "Bitmap": - rdtypes = sorted(rdtypes) - window = 0 - octets = 0 - prior_rdtype = 0 - bitmap = bytearray(b"\0" * 32) - windows = [] - for rdtype in rdtypes: - if rdtype == prior_rdtype: - continue - prior_rdtype = rdtype - new_window = rdtype // 256 - if new_window != window: - if octets != 0: - windows.append((window, bytes(bitmap[0:octets]))) - bitmap = bytearray(b"\0" * 32) - window = new_window - offset = rdtype % 256 - byte = offset // 8 - bit = offset % 8 - octets = byte + 1 - bitmap[byte] = bitmap[byte] | (0x80 >> bit) - if octets != 0: - windows.append((window, bytes(bitmap[0:octets]))) - return cls(windows) - - def to_wire(self, file: Any) -> None: - for window, bitmap in self.windows: - file.write(struct.pack("!BB", window, len(bitmap))) - file.write(bitmap) - - @classmethod - def from_wire_parser(cls, parser: "dns.wire.Parser") -> "Bitmap": - windows = [] - while parser.remaining() > 0: - window = parser.get_uint8() - bitmap = parser.get_counted_bytes() - windows.append((window, bitmap)) - return cls(windows) - - -def _priority_table(items): - by_priority = collections.defaultdict(list) - for rdata in items: - by_priority[rdata._processing_priority()].append(rdata) - return by_priority - - -def priority_processing_order(iterable): - items = list(iterable) - if len(items) == 1: - return items - by_priority = _priority_table(items) - ordered = [] - for k in sorted(by_priority.keys()): - rdatas = by_priority[k] - random.shuffle(rdatas) - ordered.extend(rdatas) - return ordered - - -_no_weight = 0.1 - - -def weighted_processing_order(iterable): - items = list(iterable) - if len(items) == 1: - return items - by_priority = _priority_table(items) - ordered = [] - for k in sorted(by_priority.keys()): - rdatas = by_priority[k] - total = sum(rdata._processing_weight() or _no_weight for rdata in rdatas) - while len(rdatas) > 1: - r = random.uniform(0, total) - for n, rdata in enumerate(rdatas): - weight = rdata._processing_weight() or _no_weight - if weight > r: - break - r -= weight - total -= weight - ordered.append(rdata) # pylint: disable=undefined-loop-variable - del rdatas[n] # pylint: disable=undefined-loop-variable - ordered.append(rdatas[0]) - return ordered - - -def parse_formatted_hex(formatted, num_chunks, chunk_size, separator): - if len(formatted) != num_chunks * (chunk_size + 1) - 1: - raise ValueError("invalid formatted hex string") - value = b"" - for _ in range(num_chunks): - chunk = formatted[0:chunk_size] - value += int(chunk, 16).to_bytes(chunk_size // 2, "big") - formatted = formatted[chunk_size:] - if len(formatted) > 0 and formatted[0] != separator: - raise ValueError("invalid formatted hex string") - formatted = formatted[1:] - return value diff --git a/.venv/lib/python3.12/site-packages/dns/renderer.py b/.venv/lib/python3.12/site-packages/dns/renderer.py deleted file mode 100644 index a77481f..0000000 --- a/.venv/lib/python3.12/site-packages/dns/renderer.py +++ /dev/null @@ -1,346 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Help for building DNS wire format messages""" - -import contextlib -import io -import random -import struct -import time - -import dns.exception -import dns.tsig - -QUESTION = 0 -ANSWER = 1 -AUTHORITY = 2 -ADDITIONAL = 3 - - -@contextlib.contextmanager -def prefixed_length(output, length_length): - output.write(b"\00" * length_length) - start = output.tell() - yield - end = output.tell() - length = end - start - if length > 0: - try: - output.seek(start - length_length) - try: - output.write(length.to_bytes(length_length, "big")) - except OverflowError: - raise dns.exception.FormError - finally: - output.seek(end) - - -class Renderer: - """Helper class for building DNS wire-format messages. - - Most applications can use the higher-level L{dns.message.Message} - class and its to_wire() method to generate wire-format messages. - This class is for those applications which need finer control - over the generation of messages. - - Typical use:: - - r = dns.renderer.Renderer(id=1, flags=0x80, max_size=512) - r.add_question(qname, qtype, qclass) - r.add_rrset(dns.renderer.ANSWER, rrset_1) - r.add_rrset(dns.renderer.ANSWER, rrset_2) - r.add_rrset(dns.renderer.AUTHORITY, ns_rrset) - r.add_rrset(dns.renderer.ADDITIONAL, ad_rrset_1) - r.add_rrset(dns.renderer.ADDITIONAL, ad_rrset_2) - r.add_edns(0, 0, 4096) - r.write_header() - r.add_tsig(keyname, secret, 300, 1, 0, '', request_mac) - wire = r.get_wire() - - If padding is going to be used, then the OPT record MUST be - written after everything else in the additional section except for - the TSIG (if any). - - output, an io.BytesIO, where rendering is written - - id: the message id - - flags: the message flags - - max_size: the maximum size of the message - - origin: the origin to use when rendering relative names - - compress: the compression table - - section: an int, the section currently being rendered - - counts: list of the number of RRs in each section - - mac: the MAC of the rendered message (if TSIG was used) - """ - - def __init__(self, id=None, flags=0, max_size=65535, origin=None): - """Initialize a new renderer.""" - - self.output = io.BytesIO() - if id is None: - self.id = random.randint(0, 65535) - else: - self.id = id - self.flags = flags - self.max_size = max_size - self.origin = origin - self.compress = {} - self.section = QUESTION - self.counts = [0, 0, 0, 0] - self.output.write(b"\x00" * 12) - self.mac = "" - self.reserved = 0 - self.was_padded = False - - def _rollback(self, where): - """Truncate the output buffer at offset *where*, and remove any - compression table entries that pointed beyond the truncation - point. - """ - - self.output.seek(where) - self.output.truncate() - keys_to_delete = [] - for k, v in self.compress.items(): - if v >= where: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.compress[k] - - def _set_section(self, section): - """Set the renderer's current section. - - Sections must be rendered order: QUESTION, ANSWER, AUTHORITY, - ADDITIONAL. Sections may be empty. - - Raises dns.exception.FormError if an attempt was made to set - a section value less than the current section. - """ - - if self.section != section: - if self.section > section: - raise dns.exception.FormError - self.section = section - - @contextlib.contextmanager - def _track_size(self): - start = self.output.tell() - yield start - if self.output.tell() > self.max_size: - self._rollback(start) - raise dns.exception.TooBig - - @contextlib.contextmanager - def _temporarily_seek_to(self, where): - current = self.output.tell() - try: - self.output.seek(where) - yield - finally: - self.output.seek(current) - - def add_question(self, qname, rdtype, rdclass=dns.rdataclass.IN): - """Add a question to the message.""" - - self._set_section(QUESTION) - with self._track_size(): - qname.to_wire(self.output, self.compress, self.origin) - self.output.write(struct.pack("!HH", rdtype, rdclass)) - self.counts[QUESTION] += 1 - - def add_rrset(self, section, rrset, **kw): - """Add the rrset to the specified section. - - Any keyword arguments are passed on to the rdataset's to_wire() - routine. - """ - - self._set_section(section) - with self._track_size(): - n = rrset.to_wire(self.output, self.compress, self.origin, **kw) - self.counts[section] += n - - def add_rdataset(self, section, name, rdataset, **kw): - """Add the rdataset to the specified section, using the specified - name as the owner name. - - Any keyword arguments are passed on to the rdataset's to_wire() - routine. - """ - - self._set_section(section) - with self._track_size(): - n = rdataset.to_wire(name, self.output, self.compress, self.origin, **kw) - self.counts[section] += n - - def add_opt(self, opt, pad=0, opt_size=0, tsig_size=0): - """Add *opt* to the additional section, applying padding if desired. The - padding will take the specified precomputed OPT size and TSIG size into - account. - - Note that we don't have reliable way of knowing how big a GSS-TSIG digest - might be, so we we might not get an even multiple of the pad in that case.""" - if pad: - ttl = opt.ttl - assert opt_size >= 11 - opt_rdata = opt[0] - size_without_padding = self.output.tell() + opt_size + tsig_size - remainder = size_without_padding % pad - if remainder: - pad = b"\x00" * (pad - remainder) - else: - pad = b"" - options = list(opt_rdata.options) - options.append(dns.edns.GenericOption(dns.edns.OptionType.PADDING, pad)) - opt = dns.message.Message._make_opt(ttl, opt_rdata.rdclass, options) - self.was_padded = True - self.add_rrset(ADDITIONAL, opt) - - def add_edns(self, edns, ednsflags, payload, options=None): - """Add an EDNS OPT record to the message.""" - - # make sure the EDNS version in ednsflags agrees with edns - ednsflags &= 0xFF00FFFF - ednsflags |= edns << 16 - opt = dns.message.Message._make_opt(ednsflags, payload, options) - self.add_opt(opt) - - def add_tsig( - self, - keyname, - secret, - fudge, - id, - tsig_error, - other_data, - request_mac, - algorithm=dns.tsig.default_algorithm, - ): - """Add a TSIG signature to the message.""" - - s = self.output.getvalue() - - if isinstance(secret, dns.tsig.Key): - key = secret - else: - key = dns.tsig.Key(keyname, secret, algorithm) - tsig = dns.message.Message._make_tsig( - keyname, algorithm, 0, fudge, b"", id, tsig_error, other_data - ) - (tsig, _) = dns.tsig.sign(s, key, tsig[0], int(time.time()), request_mac) - self._write_tsig(tsig, keyname) - - def add_multi_tsig( - self, - ctx, - keyname, - secret, - fudge, - id, - tsig_error, - other_data, - request_mac, - algorithm=dns.tsig.default_algorithm, - ): - """Add a TSIG signature to the message. Unlike add_tsig(), this can be - used for a series of consecutive DNS envelopes, e.g. for a zone - transfer over TCP [RFC2845, 4.4]. - - For the first message in the sequence, give ctx=None. For each - subsequent message, give the ctx that was returned from the - add_multi_tsig() call for the previous message.""" - - s = self.output.getvalue() - - if isinstance(secret, dns.tsig.Key): - key = secret - else: - key = dns.tsig.Key(keyname, secret, algorithm) - tsig = dns.message.Message._make_tsig( - keyname, algorithm, 0, fudge, b"", id, tsig_error, other_data - ) - (tsig, ctx) = dns.tsig.sign( - s, key, tsig[0], int(time.time()), request_mac, ctx, True - ) - self._write_tsig(tsig, keyname) - return ctx - - def _write_tsig(self, tsig, keyname): - if self.was_padded: - compress = None - else: - compress = self.compress - self._set_section(ADDITIONAL) - with self._track_size(): - keyname.to_wire(self.output, compress, self.origin) - self.output.write( - struct.pack("!HHI", dns.rdatatype.TSIG, dns.rdataclass.ANY, 0) - ) - with prefixed_length(self.output, 2): - tsig.to_wire(self.output) - - self.counts[ADDITIONAL] += 1 - with self._temporarily_seek_to(10): - self.output.write(struct.pack("!H", self.counts[ADDITIONAL])) - - def write_header(self): - """Write the DNS message header. - - Writing the DNS message header is done after all sections - have been rendered, but before the optional TSIG signature - is added. - """ - - with self._temporarily_seek_to(0): - self.output.write( - struct.pack( - "!HHHHHH", - self.id, - self.flags, - self.counts[0], - self.counts[1], - self.counts[2], - self.counts[3], - ) - ) - - def get_wire(self): - """Return the wire format message.""" - - return self.output.getvalue() - - def reserve(self, size: int) -> None: - """Reserve *size* bytes.""" - if size < 0: - raise ValueError("reserved amount must be non-negative") - if size > self.max_size: - raise ValueError("cannot reserve more than the maximum size") - self.reserved += size - self.max_size -= size - - def release_reserved(self) -> None: - """Release the reserved bytes.""" - self.max_size += self.reserved - self.reserved = 0 diff --git a/.venv/lib/python3.12/site-packages/dns/resolver.py b/.venv/lib/python3.12/site-packages/dns/resolver.py deleted file mode 100644 index f08f824..0000000 --- a/.venv/lib/python3.12/site-packages/dns/resolver.py +++ /dev/null @@ -1,2054 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS stub resolver.""" - -import contextlib -import random -import socket -import sys -import threading -import time -import warnings -from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union -from urllib.parse import urlparse - -import dns._ddr -import dns.edns -import dns.exception -import dns.flags -import dns.inet -import dns.ipv4 -import dns.ipv6 -import dns.message -import dns.name -import dns.nameserver -import dns.query -import dns.rcode -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.svcbbase -import dns.reversename -import dns.tsig - -if sys.platform == "win32": - import dns.win32util - - -class NXDOMAIN(dns.exception.DNSException): - """The DNS query name does not exist.""" - - supp_kwargs = {"qnames", "responses"} - fmt = None # we have our own __str__ implementation - - # pylint: disable=arguments-differ - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _check_kwargs(self, qnames, responses=None): - if not isinstance(qnames, (list, tuple, set)): - raise AttributeError("qnames must be a list, tuple or set") - if len(qnames) == 0: - raise AttributeError("qnames must contain at least one element") - if responses is None: - responses = {} - elif not isinstance(responses, dict): - raise AttributeError("responses must be a dict(qname=response)") - kwargs = dict(qnames=qnames, responses=responses) - return kwargs - - def __str__(self) -> str: - if "qnames" not in self.kwargs: - return super().__str__() - qnames = self.kwargs["qnames"] - if len(qnames) > 1: - msg = "None of DNS query names exist" - else: - msg = "The DNS query name does not exist" - qnames = ", ".join(map(str, qnames)) - return "{}: {}".format(msg, qnames) - - @property - def canonical_name(self): - """Return the unresolved canonical name.""" - if "qnames" not in self.kwargs: - raise TypeError("parametrized exception required") - for qname in self.kwargs["qnames"]: - response = self.kwargs["responses"][qname] - try: - cname = response.canonical_name() - if cname != qname: - return cname - except Exception: - # We can just eat this exception as it means there was - # something wrong with the response. - pass - return self.kwargs["qnames"][0] - - def __add__(self, e_nx): - """Augment by results from another NXDOMAIN exception.""" - qnames0 = list(self.kwargs.get("qnames", [])) - responses0 = dict(self.kwargs.get("responses", {})) - responses1 = e_nx.kwargs.get("responses", {}) - for qname1 in e_nx.kwargs.get("qnames", []): - if qname1 not in qnames0: - qnames0.append(qname1) - if qname1 in responses1: - responses0[qname1] = responses1[qname1] - return NXDOMAIN(qnames=qnames0, responses=responses0) - - def qnames(self): - """All of the names that were tried. - - Returns a list of ``dns.name.Name``. - """ - return self.kwargs["qnames"] - - def responses(self): - """A map from queried names to their NXDOMAIN responses. - - Returns a dict mapping a ``dns.name.Name`` to a - ``dns.message.Message``. - """ - return self.kwargs["responses"] - - def response(self, qname): - """The response for query *qname*. - - Returns a ``dns.message.Message``. - """ - return self.kwargs["responses"][qname] - - -class YXDOMAIN(dns.exception.DNSException): - """The DNS query name is too long after DNAME substitution.""" - - -ErrorTuple = Tuple[ - Optional[str], - bool, - int, - Union[Exception, str], - Optional[dns.message.Message], -] - - -def _errors_to_text(errors: List[ErrorTuple]) -> List[str]: - """Turn a resolution errors trace into a list of text.""" - texts = [] - for err in errors: - texts.append("Server {} answered {}".format(err[0], err[3])) - return texts - - -class LifetimeTimeout(dns.exception.Timeout): - """The resolution lifetime expired.""" - - msg = "The resolution lifetime expired." - fmt = "%s after {timeout:.3f} seconds: {errors}" % msg[:-1] - supp_kwargs = {"timeout", "errors"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _fmt_kwargs(self, **kwargs): - srv_msgs = _errors_to_text(kwargs["errors"]) - return super()._fmt_kwargs( - timeout=kwargs["timeout"], errors="; ".join(srv_msgs) - ) - - -# We added more detail to resolution timeouts, but they are still -# subclasses of dns.exception.Timeout for backwards compatibility. We also -# keep dns.resolver.Timeout defined for backwards compatibility. -Timeout = LifetimeTimeout - - -class NoAnswer(dns.exception.DNSException): - """The DNS response does not contain an answer to the question.""" - - fmt = "The DNS response does not contain an answer to the question: {query}" - supp_kwargs = {"response"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _fmt_kwargs(self, **kwargs): - return super()._fmt_kwargs(query=kwargs["response"].question) - - def response(self): - return self.kwargs["response"] - - -class NoNameservers(dns.exception.DNSException): - """All nameservers failed to answer the query. - - errors: list of servers and respective errors - The type of errors is - [(server IP address, any object convertible to string)]. - Non-empty errors list will add explanatory message () - """ - - msg = "All nameservers failed to answer the query." - fmt = "%s {query}: {errors}" % msg[:-1] - supp_kwargs = {"request", "errors"} - - # We do this as otherwise mypy complains about unexpected keyword argument - # idna_exception - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def _fmt_kwargs(self, **kwargs): - srv_msgs = _errors_to_text(kwargs["errors"]) - return super()._fmt_kwargs( - query=kwargs["request"].question, errors="; ".join(srv_msgs) - ) - - -class NotAbsolute(dns.exception.DNSException): - """An absolute domain name is required but a relative name was provided.""" - - -class NoRootSOA(dns.exception.DNSException): - """There is no SOA RR at the DNS root name. This should never happen!""" - - -class NoMetaqueries(dns.exception.DNSException): - """DNS metaqueries are not allowed.""" - - -class NoResolverConfiguration(dns.exception.DNSException): - """Resolver configuration could not be read or specified no nameservers.""" - - -class Answer: - """DNS stub resolver answer. - - Instances of this class bundle up the result of a successful DNS - resolution. - - For convenience, the answer object implements much of the sequence - protocol, forwarding to its ``rrset`` attribute. E.g. - ``for a in answer`` is equivalent to ``for a in answer.rrset``. - ``answer[i]`` is equivalent to ``answer.rrset[i]``, and - ``answer[i:j]`` is equivalent to ``answer.rrset[i:j]``. - - Note that CNAMEs or DNAMEs in the response may mean that answer - RRset's name might not be the query name. - """ - - def __init__( - self, - qname: dns.name.Name, - rdtype: dns.rdatatype.RdataType, - rdclass: dns.rdataclass.RdataClass, - response: dns.message.QueryMessage, - nameserver: Optional[str] = None, - port: Optional[int] = None, - ) -> None: - self.qname = qname - self.rdtype = rdtype - self.rdclass = rdclass - self.response = response - self.nameserver = nameserver - self.port = port - self.chaining_result = response.resolve_chaining() - # Copy some attributes out of chaining_result for backwards - # compatibility and convenience. - self.canonical_name = self.chaining_result.canonical_name - self.rrset = self.chaining_result.answer - self.expiration = time.time() + self.chaining_result.minimum_ttl - - def __getattr__(self, attr): # pragma: no cover - if attr == "name": - return self.rrset.name - elif attr == "ttl": - return self.rrset.ttl - elif attr == "covers": - return self.rrset.covers - elif attr == "rdclass": - return self.rrset.rdclass - elif attr == "rdtype": - return self.rrset.rdtype - else: - raise AttributeError(attr) - - def __len__(self) -> int: - return self.rrset and len(self.rrset) or 0 - - def __iter__(self): - return self.rrset and iter(self.rrset) or iter(tuple()) - - def __getitem__(self, i): - if self.rrset is None: - raise IndexError - return self.rrset[i] - - def __delitem__(self, i): - if self.rrset is None: - raise IndexError - del self.rrset[i] - - -class Answers(dict): - """A dict of DNS stub resolver answers, indexed by type.""" - - -class HostAnswers(Answers): - """A dict of DNS stub resolver answers to a host name lookup, indexed by - type. - """ - - @classmethod - def make( - cls, - v6: Optional[Answer] = None, - v4: Optional[Answer] = None, - add_empty: bool = True, - ) -> "HostAnswers": - answers = HostAnswers() - if v6 is not None and (add_empty or v6.rrset): - answers[dns.rdatatype.AAAA] = v6 - if v4 is not None and (add_empty or v4.rrset): - answers[dns.rdatatype.A] = v4 - return answers - - # Returns pairs of (address, family) from this result, potentiallys - # filtering by address family. - def addresses_and_families( - self, family: int = socket.AF_UNSPEC - ) -> Iterator[Tuple[str, int]]: - if family == socket.AF_UNSPEC: - yield from self.addresses_and_families(socket.AF_INET6) - yield from self.addresses_and_families(socket.AF_INET) - return - elif family == socket.AF_INET6: - answer = self.get(dns.rdatatype.AAAA) - elif family == socket.AF_INET: - answer = self.get(dns.rdatatype.A) - else: - raise NotImplementedError(f"unknown address family {family}") - if answer: - for rdata in answer: - yield (rdata.address, family) - - # Returns addresses from this result, potentially filtering by - # address family. - def addresses(self, family: int = socket.AF_UNSPEC) -> Iterator[str]: - return (pair[0] for pair in self.addresses_and_families(family)) - - # Returns the canonical name from this result. - def canonical_name(self) -> dns.name.Name: - answer = self.get(dns.rdatatype.AAAA, self.get(dns.rdatatype.A)) - return answer.canonical_name - - -class CacheStatistics: - """Cache Statistics""" - - def __init__(self, hits: int = 0, misses: int = 0) -> None: - self.hits = hits - self.misses = misses - - def reset(self) -> None: - self.hits = 0 - self.misses = 0 - - def clone(self) -> "CacheStatistics": - return CacheStatistics(self.hits, self.misses) - - -class CacheBase: - def __init__(self) -> None: - self.lock = threading.Lock() - self.statistics = CacheStatistics() - - def reset_statistics(self) -> None: - """Reset all statistics to zero.""" - with self.lock: - self.statistics.reset() - - def hits(self) -> int: - """How many hits has the cache had?""" - with self.lock: - return self.statistics.hits - - def misses(self) -> int: - """How many misses has the cache had?""" - with self.lock: - return self.statistics.misses - - def get_statistics_snapshot(self) -> CacheStatistics: - """Return a consistent snapshot of all the statistics. - - If running with multiple threads, it's better to take a - snapshot than to call statistics methods such as hits() and - misses() individually. - """ - with self.lock: - return self.statistics.clone() - - -CacheKey = Tuple[dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass] - - -class Cache(CacheBase): - """Simple thread-safe DNS answer cache.""" - - def __init__(self, cleaning_interval: float = 300.0) -> None: - """*cleaning_interval*, a ``float`` is the number of seconds between - periodic cleanings. - """ - - super().__init__() - self.data: Dict[CacheKey, Answer] = {} - self.cleaning_interval = cleaning_interval - self.next_cleaning: float = time.time() + self.cleaning_interval - - def _maybe_clean(self) -> None: - """Clean the cache if it's time to do so.""" - - now = time.time() - if self.next_cleaning <= now: - keys_to_delete = [] - for k, v in self.data.items(): - if v.expiration <= now: - keys_to_delete.append(k) - for k in keys_to_delete: - del self.data[k] - now = time.time() - self.next_cleaning = now + self.cleaning_interval - - def get(self, key: CacheKey) -> Optional[Answer]: - """Get the answer associated with *key*. - - Returns None if no answer is cached for the key. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - Returns a ``dns.resolver.Answer`` or ``None``. - """ - - with self.lock: - self._maybe_clean() - v = self.data.get(key) - if v is None or v.expiration <= time.time(): - self.statistics.misses += 1 - return None - self.statistics.hits += 1 - return v - - def put(self, key: CacheKey, value: Answer) -> None: - """Associate key and value in the cache. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - *value*, a ``dns.resolver.Answer``, the answer. - """ - - with self.lock: - self._maybe_clean() - self.data[key] = value - - def flush(self, key: Optional[CacheKey] = None) -> None: - """Flush the cache. - - If *key* is not ``None``, only that item is flushed. Otherwise the entire cache - is flushed. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - """ - - with self.lock: - if key is not None: - if key in self.data: - del self.data[key] - else: - self.data = {} - self.next_cleaning = time.time() + self.cleaning_interval - - -class LRUCacheNode: - """LRUCache node.""" - - def __init__(self, key, value): - self.key = key - self.value = value - self.hits = 0 - self.prev = self - self.next = self - - def link_after(self, node: "LRUCacheNode") -> None: - self.prev = node - self.next = node.next - node.next.prev = self - node.next = self - - def unlink(self) -> None: - self.next.prev = self.prev - self.prev.next = self.next - - -class LRUCache(CacheBase): - """Thread-safe, bounded, least-recently-used DNS answer cache. - - This cache is better than the simple cache (above) if you're - running a web crawler or other process that does a lot of - resolutions. The LRUCache has a maximum number of nodes, and when - it is full, the least-recently used node is removed to make space - for a new one. - """ - - def __init__(self, max_size: int = 100000) -> None: - """*max_size*, an ``int``, is the maximum number of nodes to cache; - it must be greater than 0. - """ - - super().__init__() - self.data: Dict[CacheKey, LRUCacheNode] = {} - self.set_max_size(max_size) - self.sentinel: LRUCacheNode = LRUCacheNode(None, None) - self.sentinel.prev = self.sentinel - self.sentinel.next = self.sentinel - - def set_max_size(self, max_size: int) -> None: - if max_size < 1: - max_size = 1 - self.max_size = max_size - - def get(self, key: CacheKey) -> Optional[Answer]: - """Get the answer associated with *key*. - - Returns None if no answer is cached for the key. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - Returns a ``dns.resolver.Answer`` or ``None``. - """ - - with self.lock: - node = self.data.get(key) - if node is None: - self.statistics.misses += 1 - return None - # Unlink because we're either going to move the node to the front - # of the LRU list or we're going to free it. - node.unlink() - if node.value.expiration <= time.time(): - del self.data[node.key] - self.statistics.misses += 1 - return None - node.link_after(self.sentinel) - self.statistics.hits += 1 - node.hits += 1 - return node.value - - def get_hits_for_key(self, key: CacheKey) -> int: - """Return the number of cache hits associated with the specified key.""" - with self.lock: - node = self.data.get(key) - if node is None or node.value.expiration <= time.time(): - return 0 - else: - return node.hits - - def put(self, key: CacheKey, value: Answer) -> None: - """Associate key and value in the cache. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - - *value*, a ``dns.resolver.Answer``, the answer. - """ - - with self.lock: - node = self.data.get(key) - if node is not None: - node.unlink() - del self.data[node.key] - while len(self.data) >= self.max_size: - gnode = self.sentinel.prev - gnode.unlink() - del self.data[gnode.key] - node = LRUCacheNode(key, value) - node.link_after(self.sentinel) - self.data[key] = node - - def flush(self, key: Optional[CacheKey] = None) -> None: - """Flush the cache. - - If *key* is not ``None``, only that item is flushed. Otherwise the entire cache - is flushed. - - *key*, a ``(dns.name.Name, dns.rdatatype.RdataType, dns.rdataclass.RdataClass)`` - tuple whose values are the query name, rdtype, and rdclass respectively. - """ - - with self.lock: - if key is not None: - node = self.data.get(key) - if node is not None: - node.unlink() - del self.data[node.key] - else: - gnode = self.sentinel.next - while gnode != self.sentinel: - next = gnode.next - gnode.unlink() - gnode = next - self.data = {} - - -class _Resolution: - """Helper class for dns.resolver.Resolver.resolve(). - - All of the "business logic" of resolution is encapsulated in this - class, allowing us to have multiple resolve() implementations - using different I/O schemes without copying all of the - complicated logic. - - This class is a "friend" to dns.resolver.Resolver and manipulates - resolver data structures directly. - """ - - def __init__( - self, - resolver: "BaseResolver", - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - rdclass: Union[dns.rdataclass.RdataClass, str], - tcp: bool, - raise_on_no_answer: bool, - search: Optional[bool], - ) -> None: - if isinstance(qname, str): - qname = dns.name.from_text(qname, None) - rdtype = dns.rdatatype.RdataType.make(rdtype) - if dns.rdatatype.is_metatype(rdtype): - raise NoMetaqueries - rdclass = dns.rdataclass.RdataClass.make(rdclass) - if dns.rdataclass.is_metaclass(rdclass): - raise NoMetaqueries - self.resolver = resolver - self.qnames_to_try = resolver._get_qnames_to_try(qname, search) - self.qnames = self.qnames_to_try[:] - self.rdtype = rdtype - self.rdclass = rdclass - self.tcp = tcp - self.raise_on_no_answer = raise_on_no_answer - self.nxdomain_responses: Dict[dns.name.Name, dns.message.QueryMessage] = {} - # Initialize other things to help analysis tools - self.qname = dns.name.empty - self.nameservers: List[dns.nameserver.Nameserver] = [] - self.current_nameservers: List[dns.nameserver.Nameserver] = [] - self.errors: List[ErrorTuple] = [] - self.nameserver: Optional[dns.nameserver.Nameserver] = None - self.tcp_attempt = False - self.retry_with_tcp = False - self.request: Optional[dns.message.QueryMessage] = None - self.backoff = 0.0 - - def next_request( - self, - ) -> Tuple[Optional[dns.message.QueryMessage], Optional[Answer]]: - """Get the next request to send, and check the cache. - - Returns a (request, answer) tuple. At most one of request or - answer will not be None. - """ - - # We return a tuple instead of Union[Message,Answer] as it lets - # the caller avoid isinstance(). - - while len(self.qnames) > 0: - self.qname = self.qnames.pop(0) - - # Do we know the answer? - if self.resolver.cache: - answer = self.resolver.cache.get( - (self.qname, self.rdtype, self.rdclass) - ) - if answer is not None: - if answer.rrset is None and self.raise_on_no_answer: - raise NoAnswer(response=answer.response) - else: - return (None, answer) - answer = self.resolver.cache.get( - (self.qname, dns.rdatatype.ANY, self.rdclass) - ) - if answer is not None and answer.response.rcode() == dns.rcode.NXDOMAIN: - # cached NXDOMAIN; record it and continue to next - # name. - self.nxdomain_responses[self.qname] = answer.response - continue - - # Build the request - request = dns.message.make_query(self.qname, self.rdtype, self.rdclass) - if self.resolver.keyname is not None: - request.use_tsig( - self.resolver.keyring, - self.resolver.keyname, - algorithm=self.resolver.keyalgorithm, - ) - request.use_edns( - self.resolver.edns, - self.resolver.ednsflags, - self.resolver.payload, - options=self.resolver.ednsoptions, - ) - if self.resolver.flags is not None: - request.flags = self.resolver.flags - - self.nameservers = self.resolver._enrich_nameservers( - self.resolver._nameservers, - self.resolver.nameserver_ports, - self.resolver.port, - ) - if self.resolver.rotate: - random.shuffle(self.nameservers) - self.current_nameservers = self.nameservers[:] - self.errors = [] - self.nameserver = None - self.tcp_attempt = False - self.retry_with_tcp = False - self.request = request - self.backoff = 0.10 - - return (request, None) - - # - # We've tried everything and only gotten NXDOMAINs. (We know - # it's only NXDOMAINs as anything else would have returned - # before now.) - # - raise NXDOMAIN(qnames=self.qnames_to_try, responses=self.nxdomain_responses) - - def next_nameserver(self) -> Tuple[dns.nameserver.Nameserver, bool, float]: - if self.retry_with_tcp: - assert self.nameserver is not None - assert not self.nameserver.is_always_max_size() - self.tcp_attempt = True - self.retry_with_tcp = False - return (self.nameserver, True, 0) - - backoff = 0.0 - if not self.current_nameservers: - if len(self.nameservers) == 0: - # Out of things to try! - raise NoNameservers(request=self.request, errors=self.errors) - self.current_nameservers = self.nameservers[:] - backoff = self.backoff - self.backoff = min(self.backoff * 2, 2) - - self.nameserver = self.current_nameservers.pop(0) - self.tcp_attempt = self.tcp or self.nameserver.is_always_max_size() - return (self.nameserver, self.tcp_attempt, backoff) - - def query_result( - self, response: Optional[dns.message.Message], ex: Optional[Exception] - ) -> Tuple[Optional[Answer], bool]: - # - # returns an (answer: Answer, end_loop: bool) tuple. - # - assert self.nameserver is not None - if ex: - # Exception during I/O or from_wire() - assert response is None - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - ex, - response, - ) - ) - if ( - isinstance(ex, dns.exception.FormError) - or isinstance(ex, EOFError) - or isinstance(ex, OSError) - or isinstance(ex, NotImplementedError) - ): - # This nameserver is no good, take it out of the mix. - self.nameservers.remove(self.nameserver) - elif isinstance(ex, dns.message.Truncated): - if self.tcp_attempt: - # Truncation with TCP is no good! - self.nameservers.remove(self.nameserver) - else: - self.retry_with_tcp = True - return (None, False) - # We got an answer! - assert response is not None - assert isinstance(response, dns.message.QueryMessage) - rcode = response.rcode() - if rcode == dns.rcode.NOERROR: - try: - answer = Answer( - self.qname, - self.rdtype, - self.rdclass, - response, - self.nameserver.answer_nameserver(), - self.nameserver.answer_port(), - ) - except Exception as e: - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - e, - response, - ) - ) - # The nameserver is no good, take it out of the mix. - self.nameservers.remove(self.nameserver) - return (None, False) - if self.resolver.cache: - self.resolver.cache.put((self.qname, self.rdtype, self.rdclass), answer) - if answer.rrset is None and self.raise_on_no_answer: - raise NoAnswer(response=answer.response) - return (answer, True) - elif rcode == dns.rcode.NXDOMAIN: - # Further validate the response by making an Answer, even - # if we aren't going to cache it. - try: - answer = Answer( - self.qname, dns.rdatatype.ANY, dns.rdataclass.IN, response - ) - except Exception as e: - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - e, - response, - ) - ) - # The nameserver is no good, take it out of the mix. - self.nameservers.remove(self.nameserver) - return (None, False) - self.nxdomain_responses[self.qname] = response - if self.resolver.cache: - self.resolver.cache.put( - (self.qname, dns.rdatatype.ANY, self.rdclass), answer - ) - # Make next_nameserver() return None, so caller breaks its - # inner loop and calls next_request(). - return (None, True) - elif rcode == dns.rcode.YXDOMAIN: - yex = YXDOMAIN() - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - yex, - response, - ) - ) - raise yex - else: - # - # We got a response, but we're not happy with the - # rcode in it. - # - if rcode != dns.rcode.SERVFAIL or not self.resolver.retry_servfail: - self.nameservers.remove(self.nameserver) - self.errors.append( - ( - str(self.nameserver), - self.tcp_attempt, - self.nameserver.answer_port(), - dns.rcode.to_text(rcode), - response, - ) - ) - return (None, False) - - -class BaseResolver: - """DNS stub resolver.""" - - # We initialize in reset() - # - # pylint: disable=attribute-defined-outside-init - - domain: dns.name.Name - nameserver_ports: Dict[str, int] - port: int - search: List[dns.name.Name] - use_search_by_default: bool - timeout: float - lifetime: float - keyring: Optional[Any] - keyname: Optional[Union[dns.name.Name, str]] - keyalgorithm: Union[dns.name.Name, str] - edns: int - ednsflags: int - ednsoptions: Optional[List[dns.edns.Option]] - payload: int - cache: Any - flags: Optional[int] - retry_servfail: bool - rotate: bool - ndots: Optional[int] - _nameservers: Sequence[Union[str, dns.nameserver.Nameserver]] - - def __init__( - self, filename: str = "/etc/resolv.conf", configure: bool = True - ) -> None: - """*filename*, a ``str`` or file object, specifying a file - in standard /etc/resolv.conf format. This parameter is meaningful - only when *configure* is true and the platform is POSIX. - - *configure*, a ``bool``. If True (the default), the resolver - instance is configured in the normal fashion for the operating - system the resolver is running on. (I.e. by reading a - /etc/resolv.conf file on POSIX systems and from the registry - on Windows systems.) - """ - - self.reset() - if configure: - if sys.platform == "win32": - self.read_registry() - elif filename: - self.read_resolv_conf(filename) - - def reset(self) -> None: - """Reset all resolver configuration to the defaults.""" - - self.domain = dns.name.Name(dns.name.from_text(socket.gethostname())[1:]) - if len(self.domain) == 0: - self.domain = dns.name.root - self._nameservers = [] - self.nameserver_ports = {} - self.port = 53 - self.search = [] - self.use_search_by_default = False - self.timeout = 2.0 - self.lifetime = 5.0 - self.keyring = None - self.keyname = None - self.keyalgorithm = dns.tsig.default_algorithm - self.edns = -1 - self.ednsflags = 0 - self.ednsoptions = None - self.payload = 0 - self.cache = None - self.flags = None - self.retry_servfail = False - self.rotate = False - self.ndots = None - - def read_resolv_conf(self, f: Any) -> None: - """Process *f* as a file in the /etc/resolv.conf format. If f is - a ``str``, it is used as the name of the file to open; otherwise it - is treated as the file itself. - - Interprets the following items: - - - nameserver - name server IP address - - - domain - local domain name - - - search - search list for host-name lookup - - - options - supported options are rotate, timeout, edns0, and ndots - - """ - - nameservers = [] - if isinstance(f, str): - try: - cm: contextlib.AbstractContextManager = open(f) - except OSError: - # /etc/resolv.conf doesn't exist, can't be read, etc. - raise NoResolverConfiguration(f"cannot open {f}") - else: - cm = contextlib.nullcontext(f) - with cm as f: - for l in f: - if len(l) == 0 or l[0] == "#" or l[0] == ";": - continue - tokens = l.split() - - # Any line containing less than 2 tokens is malformed - if len(tokens) < 2: - continue - - if tokens[0] == "nameserver": - nameservers.append(tokens[1]) - elif tokens[0] == "domain": - self.domain = dns.name.from_text(tokens[1]) - # domain and search are exclusive - self.search = [] - elif tokens[0] == "search": - # the last search wins - self.search = [] - for suffix in tokens[1:]: - self.search.append(dns.name.from_text(suffix)) - # We don't set domain as it is not used if - # len(self.search) > 0 - elif tokens[0] == "options": - for opt in tokens[1:]: - if opt == "rotate": - self.rotate = True - elif opt == "edns0": - self.use_edns() - elif "timeout" in opt: - try: - self.timeout = int(opt.split(":")[1]) - except (ValueError, IndexError): - pass - elif "ndots" in opt: - try: - self.ndots = int(opt.split(":")[1]) - except (ValueError, IndexError): - pass - if len(nameservers) == 0: - raise NoResolverConfiguration("no nameservers") - # Assigning directly instead of appending means we invoke the - # setter logic, with additonal checking and enrichment. - self.nameservers = nameservers - - def read_registry(self) -> None: - """Extract resolver configuration from the Windows registry.""" - try: - info = dns.win32util.get_dns_info() # type: ignore - if info.domain is not None: - self.domain = info.domain - self.nameservers = info.nameservers - self.search = info.search - except AttributeError: - raise NotImplementedError - - def _compute_timeout( - self, - start: float, - lifetime: Optional[float] = None, - errors: Optional[List[ErrorTuple]] = None, - ) -> float: - lifetime = self.lifetime if lifetime is None else lifetime - now = time.time() - duration = now - start - if errors is None: - errors = [] - if duration < 0: - if duration < -1: - # Time going backwards is bad. Just give up. - raise LifetimeTimeout(timeout=duration, errors=errors) - else: - # Time went backwards, but only a little. This can - # happen, e.g. under vmware with older linux kernels. - # Pretend it didn't happen. - duration = 0 - if duration >= lifetime: - raise LifetimeTimeout(timeout=duration, errors=errors) - return min(lifetime - duration, self.timeout) - - def _get_qnames_to_try( - self, qname: dns.name.Name, search: Optional[bool] - ) -> List[dns.name.Name]: - # This is a separate method so we can unit test the search - # rules without requiring the Internet. - if search is None: - search = self.use_search_by_default - qnames_to_try = [] - if qname.is_absolute(): - qnames_to_try.append(qname) - else: - abs_qname = qname.concatenate(dns.name.root) - if search: - if len(self.search) > 0: - # There is a search list, so use it exclusively - search_list = self.search[:] - elif self.domain != dns.name.root and self.domain is not None: - # We have some notion of a domain that isn't the root, so - # use it as the search list. - search_list = [self.domain] - else: - search_list = [] - # Figure out the effective ndots (default is 1) - if self.ndots is None: - ndots = 1 - else: - ndots = self.ndots - for suffix in search_list: - qnames_to_try.append(qname + suffix) - if len(qname) > ndots: - # The name has at least ndots dots, so we should try an - # absolute query first. - qnames_to_try.insert(0, abs_qname) - else: - # The name has less than ndots dots, so we should search - # first, then try the absolute name. - qnames_to_try.append(abs_qname) - else: - qnames_to_try.append(abs_qname) - return qnames_to_try - - def use_tsig( - self, - keyring: Any, - keyname: Optional[Union[dns.name.Name, str]] = None, - algorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, - ) -> None: - """Add a TSIG signature to each query. - - The parameters are passed to ``dns.message.Message.use_tsig()``; - see its documentation for details. - """ - - self.keyring = keyring - self.keyname = keyname - self.keyalgorithm = algorithm - - def use_edns( - self, - edns: Optional[Union[int, bool]] = 0, - ednsflags: int = 0, - payload: int = dns.message.DEFAULT_EDNS_PAYLOAD, - options: Optional[List[dns.edns.Option]] = None, - ) -> None: - """Configure EDNS behavior. - - *edns*, an ``int``, is the EDNS level to use. Specifying - ``None``, ``False``, or ``-1`` means "do not use EDNS", and in this case - the other parameters are ignored. Specifying ``True`` is - equivalent to specifying 0, i.e. "use EDNS0". - - *ednsflags*, an ``int``, the EDNS flag values. - - *payload*, an ``int``, is the EDNS sender's payload field, which is the - maximum size of UDP datagram the sender can handle. I.e. how big - a response to this message can be. - - *options*, a list of ``dns.edns.Option`` objects or ``None``, the EDNS - options. - """ - - if edns is None or edns is False: - edns = -1 - elif edns is True: - edns = 0 - self.edns = edns - self.ednsflags = ednsflags - self.payload = payload - self.ednsoptions = options - - def set_flags(self, flags: int) -> None: - """Overrides the default flags with your own. - - *flags*, an ``int``, the message flags to use. - """ - - self.flags = flags - - @classmethod - def _enrich_nameservers( - cls, - nameservers: Sequence[Union[str, dns.nameserver.Nameserver]], - nameserver_ports: Dict[str, int], - default_port: int, - ) -> List[dns.nameserver.Nameserver]: - enriched_nameservers = [] - if isinstance(nameservers, list): - for nameserver in nameservers: - enriched_nameserver: dns.nameserver.Nameserver - if isinstance(nameserver, dns.nameserver.Nameserver): - enriched_nameserver = nameserver - elif dns.inet.is_address(nameserver): - port = nameserver_ports.get(nameserver, default_port) - enriched_nameserver = dns.nameserver.Do53Nameserver( - nameserver, port - ) - else: - try: - if urlparse(nameserver).scheme != "https": - raise NotImplementedError - except Exception: - raise ValueError( - f"nameserver {nameserver} is not a " - "dns.nameserver.Nameserver instance or text form, " - "IP address, nor a valid https URL" - ) - enriched_nameserver = dns.nameserver.DoHNameserver(nameserver) - enriched_nameservers.append(enriched_nameserver) - else: - raise ValueError( - "nameservers must be a list or tuple (not a {})".format( - type(nameservers) - ) - ) - return enriched_nameservers - - @property - def nameservers( - self, - ) -> Sequence[Union[str, dns.nameserver.Nameserver]]: - return self._nameservers - - @nameservers.setter - def nameservers( - self, nameservers: Sequence[Union[str, dns.nameserver.Nameserver]] - ) -> None: - """ - *nameservers*, a ``list`` of nameservers, where a nameserver is either - a string interpretable as a nameserver, or a ``dns.nameserver.Nameserver`` - instance. - - Raises ``ValueError`` if *nameservers* is not a list of nameservers. - """ - # We just call _enrich_nameservers() for checking - self._enrich_nameservers(nameservers, self.nameserver_ports, self.port) - self._nameservers = nameservers - - -class Resolver(BaseResolver): - """DNS stub resolver.""" - - def resolve( - self, - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - ) -> Answer: # pylint: disable=arguments-differ - """Query nameservers to find the answer to the question. - - The *qname*, *rdtype*, and *rdclass* parameters may be objects - of the appropriate type, or strings that can be converted into objects - of the appropriate type. - - *qname*, a ``dns.name.Name`` or ``str``, the query name. - - *rdtype*, an ``int`` or ``str``, the query type. - - *rdclass*, an ``int`` or ``str``, the query class. - - *tcp*, a ``bool``. If ``True``, use TCP to make the query. - - *source*, a ``str`` or ``None``. If not ``None``, bind to this IP - address when making queries. - - *raise_on_no_answer*, a ``bool``. If ``True``, raise - ``dns.resolver.NoAnswer`` if there's no answer to the question. - - *source_port*, an ``int``, the port from which to send the message. - - *lifetime*, a ``float``, how many seconds a query should run - before timing out. - - *search*, a ``bool`` or ``None``, determines whether the - search list configured in the system's resolver configuration - are used for relative names, and whether the resolver's domain - may be added to relative names. The default is ``None``, - which causes the value of the resolver's - ``use_search_by_default`` attribute to be used. - - Raises ``dns.resolver.LifetimeTimeout`` if no answers could be found - in the specified lifetime. - - Raises ``dns.resolver.NXDOMAIN`` if the query name does not exist. - - Raises ``dns.resolver.YXDOMAIN`` if the query name is too long after - DNAME substitution. - - Raises ``dns.resolver.NoAnswer`` if *raise_on_no_answer* is - ``True`` and the query name exists but has no RRset of the - desired type and class. - - Raises ``dns.resolver.NoNameservers`` if no non-broken - nameservers are available to answer the question. - - Returns a ``dns.resolver.Answer`` instance. - - """ - - resolution = _Resolution( - self, qname, rdtype, rdclass, tcp, raise_on_no_answer, search - ) - start = time.time() - while True: - (request, answer) = resolution.next_request() - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - # cache hit! - return answer - assert request is not None # needed for type checking - done = False - while not done: - (nameserver, tcp, backoff) = resolution.next_nameserver() - if backoff: - time.sleep(backoff) - timeout = self._compute_timeout(start, lifetime, resolution.errors) - try: - response = nameserver.query( - request, - timeout=timeout, - source=source, - source_port=source_port, - max_size=tcp, - ) - except Exception as ex: - (_, done) = resolution.query_result(None, ex) - continue - (answer, done) = resolution.query_result(response, None) - # Note we need to say "if answer is not None" and not just - # "if answer" because answer implements __len__, and python - # will call that. We want to return if we have an answer - # object, including in cases where its length is 0. - if answer is not None: - return answer - - def query( - self, - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - ) -> Answer: # pragma: no cover - """Query nameservers to find the answer to the question. - - This method calls resolve() with ``search=True``, and is - provided for backwards compatibility with prior versions of - dnspython. See the documentation for the resolve() method for - further details. - """ - warnings.warn( - "please use dns.resolver.Resolver.resolve() instead", - DeprecationWarning, - stacklevel=2, - ) - return self.resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - True, - ) - - def resolve_address(self, ipaddr: str, *args: Any, **kwargs: Any) -> Answer: - """Use a resolver to run a reverse query for PTR records. - - This utilizes the resolve() method to perform a PTR lookup on the - specified IP address. - - *ipaddr*, a ``str``, the IPv4 or IPv6 address you want to get - the PTR record for. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs["rdtype"] = dns.rdatatype.PTR - modified_kwargs["rdclass"] = dns.rdataclass.IN - return self.resolve( - dns.reversename.from_address(ipaddr), *args, **modified_kwargs - ) - - def resolve_name( - self, - name: Union[dns.name.Name, str], - family: int = socket.AF_UNSPEC, - **kwargs: Any, - ) -> HostAnswers: - """Use a resolver to query for address records. - - This utilizes the resolve() method to perform A and/or AAAA lookups on - the specified name. - - *qname*, a ``dns.name.Name`` or ``str``, the name to resolve. - - *family*, an ``int``, the address family. If socket.AF_UNSPEC - (the default), both A and AAAA records will be retrieved. - - All other arguments that can be passed to the resolve() function - except for rdtype and rdclass are also supported by this - function. - """ - # We make a modified kwargs for type checking happiness, as otherwise - # we get a legit warning about possibly having rdtype and rdclass - # in the kwargs more than once. - modified_kwargs: Dict[str, Any] = {} - modified_kwargs.update(kwargs) - modified_kwargs.pop("rdtype", None) - modified_kwargs["rdclass"] = dns.rdataclass.IN - - if family == socket.AF_INET: - v4 = self.resolve(name, dns.rdatatype.A, **modified_kwargs) - return HostAnswers.make(v4=v4) - elif family == socket.AF_INET6: - v6 = self.resolve(name, dns.rdatatype.AAAA, **modified_kwargs) - return HostAnswers.make(v6=v6) - elif family != socket.AF_UNSPEC: - raise NotImplementedError(f"unknown address family {family}") - - raise_on_no_answer = modified_kwargs.pop("raise_on_no_answer", True) - lifetime = modified_kwargs.pop("lifetime", None) - start = time.time() - v6 = self.resolve( - name, - dns.rdatatype.AAAA, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - # Note that setting name ensures we query the same name - # for A as we did for AAAA. (This is just in case search lists - # are active by default in the resolver configuration and - # we might be talking to a server that says NXDOMAIN when it - # wants to say NOERROR no data. - name = v6.qname - v4 = self.resolve( - name, - dns.rdatatype.A, - raise_on_no_answer=False, - lifetime=self._compute_timeout(start, lifetime), - **modified_kwargs, - ) - answers = HostAnswers.make(v6=v6, v4=v4, add_empty=not raise_on_no_answer) - if not answers: - raise NoAnswer(response=v6.response) - return answers - - # pylint: disable=redefined-outer-name - - def canonical_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - The canonical name is the name the resolver uses for queries - after all CNAME and DNAME renamings have been applied. - - *name*, a ``dns.name.Name`` or ``str``, the query name. - - This method can raise any exception that ``resolve()`` can - raise, other than ``dns.resolver.NoAnswer`` and - ``dns.resolver.NXDOMAIN``. - - Returns a ``dns.name.Name``. - """ - try: - answer = self.resolve(name, raise_on_no_answer=False) - canonical_name = answer.canonical_name - except dns.resolver.NXDOMAIN as e: - canonical_name = e.canonical_name - return canonical_name - - # pylint: enable=redefined-outer-name - - def try_ddr(self, lifetime: float = 5.0) -> None: - """Try to update the resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - *lifetime*, a float, is the maximum time to spend attempting DDR. The default - is 5 seconds. - - If the SVCB query is successful and results in a non-empty list of nameservers, - then the resolver's nameservers are set to the returned servers in priority - order. - - The current implementation does not use any address hints from the SVCB record, - nor does it resolve addresses for the SCVB target name, rather it assumes that - the bootstrap nameserver will always be one of the addresses and uses it. - A future revision to the code may offer fuller support. The code verifies that - the bootstrap nameserver is in the Subject Alternative Name field of the - TLS certficate. - """ - try: - expiration = time.time() + lifetime - answer = self.resolve( - dns._ddr._local_resolver_name, "SVCB", lifetime=lifetime - ) - timeout = dns.query._remaining(expiration) - nameservers = dns._ddr._get_nameservers_sync(answer, timeout) - if len(nameservers) > 0: - self.nameservers = nameservers - except Exception: - pass - - -#: The default resolver. -default_resolver: Optional[Resolver] = None - - -def get_default_resolver() -> Resolver: - """Get the default resolver, initializing it if necessary.""" - if default_resolver is None: - reset_default_resolver() - assert default_resolver is not None - return default_resolver - - -def reset_default_resolver() -> None: - """Re-initialize default resolver. - - Note that the resolver configuration (i.e. /etc/resolv.conf on UNIX - systems) will be re-read immediately. - """ - - global default_resolver - default_resolver = Resolver() - - -def resolve( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, -) -> Answer: # pragma: no cover - """Query nameservers to find the answer to the question. - - This is a convenience function that uses the default resolver - object to make the query. - - See ``dns.resolver.Resolver.resolve`` for more information on the - parameters. - """ - - return get_default_resolver().resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - ) - - -def query( - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, -) -> Answer: # pragma: no cover - """Query nameservers to find the answer to the question. - - This method calls resolve() with ``search=True``, and is - provided for backwards compatibility with prior versions of - dnspython. See the documentation for the resolve() method for - further details. - """ - warnings.warn( - "please use dns.resolver.resolve() instead", DeprecationWarning, stacklevel=2 - ) - return resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - True, - ) - - -def resolve_address(ipaddr: str, *args: Any, **kwargs: Any) -> Answer: - """Use a resolver to run a reverse query for PTR records. - - See ``dns.resolver.Resolver.resolve_address`` for more information on the - parameters. - """ - - return get_default_resolver().resolve_address(ipaddr, *args, **kwargs) - - -def resolve_name( - name: Union[dns.name.Name, str], family: int = socket.AF_UNSPEC, **kwargs: Any -) -> HostAnswers: - """Use a resolver to query for address records. - - See ``dns.resolver.Resolver.resolve_name`` for more information on the - parameters. - """ - - return get_default_resolver().resolve_name(name, family, **kwargs) - - -def canonical_name(name: Union[dns.name.Name, str]) -> dns.name.Name: - """Determine the canonical name of *name*. - - See ``dns.resolver.Resolver.canonical_name`` for more information on the - parameters and possible exceptions. - """ - - return get_default_resolver().canonical_name(name) - - -def try_ddr(lifetime: float = 5.0) -> None: - """Try to update the default resolver's nameservers using Discovery of Designated - Resolvers (DDR). If successful, the resolver will subsequently use - DNS-over-HTTPS or DNS-over-TLS for future queries. - - See :py:func:`dns.resolver.Resolver.try_ddr` for more information. - """ - return get_default_resolver().try_ddr(lifetime) - - -def zone_for_name( - name: Union[dns.name.Name, str], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - tcp: bool = False, - resolver: Optional[Resolver] = None, - lifetime: Optional[float] = None, -) -> dns.name.Name: - """Find the name of the zone which contains the specified name. - - *name*, an absolute ``dns.name.Name`` or ``str``, the query name. - - *rdclass*, an ``int``, the query class. - - *tcp*, a ``bool``. If ``True``, use TCP to make the query. - - *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. - If ``None``, the default, then the default resolver is used. - - *lifetime*, a ``float``, the total time to allow for the queries needed - to determine the zone. If ``None``, the default, then only the individual - query limits of the resolver apply. - - Raises ``dns.resolver.NoRootSOA`` if there is no SOA RR at the DNS - root. (This is only likely to happen if you're using non-default - root servers in your network and they are misconfigured.) - - Raises ``dns.resolver.LifetimeTimeout`` if the answer could not be - found in the allotted lifetime. - - Returns a ``dns.name.Name``. - """ - - if isinstance(name, str): - name = dns.name.from_text(name, dns.name.root) - if resolver is None: - resolver = get_default_resolver() - if not name.is_absolute(): - raise NotAbsolute(name) - start = time.time() - expiration: Optional[float] - if lifetime is not None: - expiration = start + lifetime - else: - expiration = None - while 1: - try: - rlifetime: Optional[float] - if expiration is not None: - rlifetime = expiration - time.time() - if rlifetime <= 0: - rlifetime = 0 - else: - rlifetime = None - answer = resolver.resolve( - name, dns.rdatatype.SOA, rdclass, tcp, lifetime=rlifetime - ) - assert answer.rrset is not None - if answer.rrset.name == name: - return name - # otherwise we were CNAMEd or DNAMEd and need to look higher - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer) as e: - if isinstance(e, dns.resolver.NXDOMAIN): - response = e.responses().get(name) - else: - response = e.response() # pylint: disable=no-value-for-parameter - if response: - for rrs in response.authority: - if rrs.rdtype == dns.rdatatype.SOA and rrs.rdclass == rdclass: - (nr, _, _) = rrs.name.fullcompare(name) - if nr == dns.name.NAMERELN_SUPERDOMAIN: - # We're doing a proper superdomain check as - # if the name were equal we ought to have gotten - # it in the answer section! We are ignoring the - # possibility that the authority is insane and - # is including multiple SOA RRs for different - # authorities. - return rrs.name - # we couldn't extract anything useful from the response (e.g. it's - # a type 3 NXDOMAIN) - try: - name = name.parent() - except dns.name.NoParent: - raise NoRootSOA - - -def make_resolver_at( - where: Union[dns.name.Name, str], - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> Resolver: - """Make a stub resolver using the specified destination as the full resolver. - - *where*, a ``dns.name.Name`` or ``str`` the domain name or IP address of the - full resolver. - - *port*, an ``int``, the port to use. If not specified, the default is 53. - - *family*, an ``int``, the address family to use. This parameter is used if - *where* is not an address. The default is ``socket.AF_UNSPEC`` in which case - the first address returned by ``resolve_name()`` will be used, otherwise the - first address of the specified family will be used. - - *resolver*, a ``dns.resolver.Resolver`` or ``None``, the resolver to use for - resolution of hostnames. If not specified, the default resolver will be used. - - Returns a ``dns.resolver.Resolver`` or raises an exception. - """ - if resolver is None: - resolver = get_default_resolver() - nameservers: List[Union[str, dns.nameserver.Nameserver]] = [] - if isinstance(where, str) and dns.inet.is_address(where): - nameservers.append(dns.nameserver.Do53Nameserver(where, port)) - else: - for address in resolver.resolve_name(where, family).addresses(): - nameservers.append(dns.nameserver.Do53Nameserver(address, port)) - res = dns.resolver.Resolver(configure=False) - res.nameservers = nameservers - return res - - -def resolve_at( - where: Union[dns.name.Name, str], - qname: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.A, - rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - tcp: bool = False, - source: Optional[str] = None, - raise_on_no_answer: bool = True, - source_port: int = 0, - lifetime: Optional[float] = None, - search: Optional[bool] = None, - port: int = 53, - family: int = socket.AF_UNSPEC, - resolver: Optional[Resolver] = None, -) -> Answer: - """Query nameservers to find the answer to the question. - - This is a convenience function that calls ``dns.resolver.make_resolver_at()`` to - make a resolver, and then uses it to resolve the query. - - See ``dns.resolver.Resolver.resolve`` for more information on the resolution - parameters, and ``dns.resolver.make_resolver_at`` for information about the resolver - parameters *where*, *port*, *family*, and *resolver*. - - If making more than one query, it is more efficient to call - ``dns.resolver.make_resolver_at()`` and then use that resolver for the queries - instead of calling ``resolve_at()`` multiple times. - """ - return make_resolver_at(where, port, family, resolver).resolve( - qname, - rdtype, - rdclass, - tcp, - source, - raise_on_no_answer, - source_port, - lifetime, - search, - ) - - -# -# Support for overriding the system resolver for all python code in the -# running process. -# - -_protocols_for_socktype = { - socket.SOCK_DGRAM: [socket.SOL_UDP], - socket.SOCK_STREAM: [socket.SOL_TCP], -} - -_resolver = None -_original_getaddrinfo = socket.getaddrinfo -_original_getnameinfo = socket.getnameinfo -_original_getfqdn = socket.getfqdn -_original_gethostbyname = socket.gethostbyname -_original_gethostbyname_ex = socket.gethostbyname_ex -_original_gethostbyaddr = socket.gethostbyaddr - - -def _getaddrinfo( - host=None, service=None, family=socket.AF_UNSPEC, socktype=0, proto=0, flags=0 -): - if flags & socket.AI_NUMERICHOST != 0: - # Short circuit directly into the system's getaddrinfo(). We're - # not adding any value in this case, and this avoids infinite loops - # because dns.query.* needs to call getaddrinfo() for IPv6 scoping - # reasons. We will also do this short circuit below if we - # discover that the host is an address literal. - return _original_getaddrinfo(host, service, family, socktype, proto, flags) - if flags & (socket.AI_ADDRCONFIG | socket.AI_V4MAPPED) != 0: - # Not implemented. We raise a gaierror as opposed to a - # NotImplementedError as it helps callers handle errors more - # appropriately. [Issue #316] - # - # We raise EAI_FAIL as opposed to EAI_SYSTEM because there is - # no EAI_SYSTEM on Windows [Issue #416]. We didn't go for - # EAI_BADFLAGS as the flags aren't bad, we just don't - # implement them. - raise socket.gaierror( - socket.EAI_FAIL, "Non-recoverable failure in name resolution" - ) - if host is None and service is None: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - addrs = [] - canonical_name = None # pylint: disable=redefined-outer-name - # Is host None or an address literal? If so, use the system's - # getaddrinfo(). - if host is None: - return _original_getaddrinfo(host, service, family, socktype, proto, flags) - try: - # We don't care about the result of af_for_address(), we're just - # calling it so it raises an exception if host is not an IPv4 or - # IPv6 address. - dns.inet.af_for_address(host) - return _original_getaddrinfo(host, service, family, socktype, proto, flags) - except Exception: - pass - # Something needs resolution! - try: - answers = _resolver.resolve_name(host, family) - addrs = answers.addresses_and_families() - canonical_name = answers.canonical_name().to_text(True) - except dns.resolver.NXDOMAIN: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - except Exception: - # We raise EAI_AGAIN here as the failure may be temporary - # (e.g. a timeout) and EAI_SYSTEM isn't defined on Windows. - # [Issue #416] - raise socket.gaierror(socket.EAI_AGAIN, "Temporary failure in name resolution") - port = None - try: - # Is it a port literal? - if service is None: - port = 0 - else: - port = int(service) - except Exception: - if flags & socket.AI_NUMERICSERV == 0: - try: - port = socket.getservbyname(service) - except Exception: - pass - if port is None: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - tuples = [] - if socktype == 0: - socktypes = [socket.SOCK_DGRAM, socket.SOCK_STREAM] - else: - socktypes = [socktype] - if flags & socket.AI_CANONNAME != 0: - cname = canonical_name - else: - cname = "" - for addr, af in addrs: - for socktype in socktypes: - for proto in _protocols_for_socktype[socktype]: - addr_tuple = dns.inet.low_level_address_tuple((addr, port), af) - tuples.append((af, socktype, proto, cname, addr_tuple)) - if len(tuples) == 0: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - return tuples - - -def _getnameinfo(sockaddr, flags=0): - host = sockaddr[0] - port = sockaddr[1] - if len(sockaddr) == 4: - scope = sockaddr[3] - family = socket.AF_INET6 - else: - scope = None - family = socket.AF_INET - tuples = _getaddrinfo(host, port, family, socket.SOCK_STREAM, socket.SOL_TCP, 0) - if len(tuples) > 1: - raise socket.error("sockaddr resolved to multiple addresses") - addr = tuples[0][4][0] - if flags & socket.NI_DGRAM: - pname = "udp" - else: - pname = "tcp" - qname = dns.reversename.from_address(addr) - if flags & socket.NI_NUMERICHOST == 0: - try: - answer = _resolver.resolve(qname, "PTR") - hostname = answer.rrset[0].target.to_text(True) - except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): - if flags & socket.NI_NAMEREQD: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - hostname = addr - if scope is not None: - hostname += "%" + str(scope) - else: - hostname = addr - if scope is not None: - hostname += "%" + str(scope) - if flags & socket.NI_NUMERICSERV: - service = str(port) - else: - service = socket.getservbyport(port, pname) - return (hostname, service) - - -def _getfqdn(name=None): - if name is None: - name = socket.gethostname() - try: - (name, _, _) = _gethostbyaddr(name) - # Python's version checks aliases too, but our gethostbyname - # ignores them, so we do so here as well. - except Exception: - pass - return name - - -def _gethostbyname(name): - return _gethostbyname_ex(name)[2][0] - - -def _gethostbyname_ex(name): - aliases = [] - addresses = [] - tuples = _getaddrinfo( - name, 0, socket.AF_INET, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME - ) - canonical = tuples[0][3] - for item in tuples: - addresses.append(item[4][0]) - # XXX we just ignore aliases - return (canonical, aliases, addresses) - - -def _gethostbyaddr(ip): - try: - dns.ipv6.inet_aton(ip) - sockaddr = (ip, 80, 0, 0) - family = socket.AF_INET6 - except Exception: - try: - dns.ipv4.inet_aton(ip) - except Exception: - raise socket.gaierror(socket.EAI_NONAME, "Name or service not known") - sockaddr = (ip, 80) - family = socket.AF_INET - (name, _) = _getnameinfo(sockaddr, socket.NI_NAMEREQD) - aliases = [] - addresses = [] - tuples = _getaddrinfo( - name, 0, family, socket.SOCK_STREAM, socket.SOL_TCP, socket.AI_CANONNAME - ) - canonical = tuples[0][3] - # We only want to include an address from the tuples if it's the - # same as the one we asked about. We do this comparison in binary - # to avoid any differences in text representations. - bin_ip = dns.inet.inet_pton(family, ip) - for item in tuples: - addr = item[4][0] - bin_addr = dns.inet.inet_pton(family, addr) - if bin_ip == bin_addr: - addresses.append(addr) - # XXX we just ignore aliases - return (canonical, aliases, addresses) - - -def override_system_resolver(resolver: Optional[Resolver] = None) -> None: - """Override the system resolver routines in the socket module with - versions which use dnspython's resolver. - - This can be useful in testing situations where you want to control - the resolution behavior of python code without having to change - the system's resolver settings (e.g. /etc/resolv.conf). - - The resolver to use may be specified; if it's not, the default - resolver will be used. - - resolver, a ``dns.resolver.Resolver`` or ``None``, the resolver to use. - """ - - if resolver is None: - resolver = get_default_resolver() - global _resolver - _resolver = resolver - socket.getaddrinfo = _getaddrinfo - socket.getnameinfo = _getnameinfo - socket.getfqdn = _getfqdn - socket.gethostbyname = _gethostbyname - socket.gethostbyname_ex = _gethostbyname_ex - socket.gethostbyaddr = _gethostbyaddr - - -def restore_system_resolver() -> None: - """Undo the effects of prior override_system_resolver().""" - - global _resolver - _resolver = None - socket.getaddrinfo = _original_getaddrinfo - socket.getnameinfo = _original_getnameinfo - socket.getfqdn = _original_getfqdn - socket.gethostbyname = _original_gethostbyname - socket.gethostbyname_ex = _original_gethostbyname_ex - socket.gethostbyaddr = _original_gethostbyaddr diff --git a/.venv/lib/python3.12/site-packages/dns/reversename.py b/.venv/lib/python3.12/site-packages/dns/reversename.py deleted file mode 100644 index 8236c71..0000000 --- a/.venv/lib/python3.12/site-packages/dns/reversename.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2006-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Reverse Map Names.""" - -import binascii - -import dns.ipv4 -import dns.ipv6 -import dns.name - -ipv4_reverse_domain = dns.name.from_text("in-addr.arpa.") -ipv6_reverse_domain = dns.name.from_text("ip6.arpa.") - - -def from_address( - text: str, - v4_origin: dns.name.Name = ipv4_reverse_domain, - v6_origin: dns.name.Name = ipv6_reverse_domain, -) -> dns.name.Name: - """Convert an IPv4 or IPv6 address in textual form into a Name object whose - value is the reverse-map domain name of the address. - - *text*, a ``str``, is an IPv4 or IPv6 address in textual form - (e.g. '127.0.0.1', '::1') - - *v4_origin*, a ``dns.name.Name`` to append to the labels corresponding to - the address if the address is an IPv4 address, instead of the default - (in-addr.arpa.) - - *v6_origin*, a ``dns.name.Name`` to append to the labels corresponding to - the address if the address is an IPv6 address, instead of the default - (ip6.arpa.) - - Raises ``dns.exception.SyntaxError`` if the address is badly formed. - - Returns a ``dns.name.Name``. - """ - - try: - v6 = dns.ipv6.inet_aton(text) - if dns.ipv6.is_mapped(v6): - parts = ["%d" % byte for byte in v6[12:]] - origin = v4_origin - else: - parts = [x for x in str(binascii.hexlify(v6).decode())] - origin = v6_origin - except Exception: - parts = ["%d" % byte for byte in dns.ipv4.inet_aton(text)] - origin = v4_origin - return dns.name.from_text(".".join(reversed(parts)), origin=origin) - - -def to_address( - name: dns.name.Name, - v4_origin: dns.name.Name = ipv4_reverse_domain, - v6_origin: dns.name.Name = ipv6_reverse_domain, -) -> str: - """Convert a reverse map domain name into textual address form. - - *name*, a ``dns.name.Name``, an IPv4 or IPv6 address in reverse-map name - form. - - *v4_origin*, a ``dns.name.Name`` representing the top-level domain for - IPv4 addresses, instead of the default (in-addr.arpa.) - - *v6_origin*, a ``dns.name.Name`` representing the top-level domain for - IPv4 addresses, instead of the default (ip6.arpa.) - - Raises ``dns.exception.SyntaxError`` if the name does not have a - reverse-map form. - - Returns a ``str``. - """ - - if name.is_subdomain(v4_origin): - name = name.relativize(v4_origin) - text = b".".join(reversed(name.labels)) - # run through inet_ntoa() to check syntax and make pretty. - return dns.ipv4.inet_ntoa(dns.ipv4.inet_aton(text)) - elif name.is_subdomain(v6_origin): - name = name.relativize(v6_origin) - labels = list(reversed(name.labels)) - parts = [] - for i in range(0, len(labels), 4): - parts.append(b"".join(labels[i : i + 4])) - text = b":".join(parts) - # run through inet_ntoa() to check syntax and make pretty. - return dns.ipv6.inet_ntoa(dns.ipv6.inet_aton(text)) - else: - raise dns.exception.SyntaxError("unknown reverse-map address family") diff --git a/.venv/lib/python3.12/site-packages/dns/rrset.py b/.venv/lib/python3.12/site-packages/dns/rrset.py deleted file mode 100644 index 6f39b10..0000000 --- a/.venv/lib/python3.12/site-packages/dns/rrset.py +++ /dev/null @@ -1,285 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS RRsets (an RRset is a named rdataset)""" - -from typing import Any, Collection, Dict, Optional, Union, cast - -import dns.name -import dns.rdataclass -import dns.rdataset -import dns.renderer - - -class RRset(dns.rdataset.Rdataset): - """A DNS RRset (named rdataset). - - RRset inherits from Rdataset, and RRsets can be treated as - Rdatasets in most cases. There are, however, a few notable - exceptions. RRsets have different to_wire() and to_text() method - arguments, reflecting the fact that RRsets always have an owner - name. - """ - - __slots__ = ["name", "deleting"] - - def __init__( - self, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - deleting: Optional[dns.rdataclass.RdataClass] = None, - ): - """Create a new RRset.""" - - super().__init__(rdclass, rdtype, covers) - self.name = name - self.deleting = deleting - - def _clone(self): - obj = super()._clone() - obj.name = self.name - obj.deleting = self.deleting - return obj - - def __repr__(self): - if self.covers == 0: - ctext = "" - else: - ctext = "(" + dns.rdatatype.to_text(self.covers) + ")" - if self.deleting is not None: - dtext = " delete=" + dns.rdataclass.to_text(self.deleting) - else: - dtext = "" - return ( - "" - ) - - def __str__(self): - return self.to_text() - - def __eq__(self, other): - if isinstance(other, RRset): - if self.name != other.name: - return False - elif not isinstance(other, dns.rdataset.Rdataset): - return False - return super().__eq__(other) - - def match(self, *args: Any, **kwargs: Any) -> bool: # type: ignore[override] - """Does this rrset match the specified attributes? - - Behaves as :py:func:`full_match()` if the first argument is a - ``dns.name.Name``, and as :py:func:`dns.rdataset.Rdataset.match()` - otherwise. - - (This behavior fixes a design mistake where the signature of this - method became incompatible with that of its superclass. The fix - makes RRsets matchable as Rdatasets while preserving backwards - compatibility.) - """ - if isinstance(args[0], dns.name.Name): - return self.full_match(*args, **kwargs) # type: ignore[arg-type] - else: - return super().match(*args, **kwargs) # type: ignore[arg-type] - - def full_match( - self, - name: dns.name.Name, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - deleting: Optional[dns.rdataclass.RdataClass] = None, - ) -> bool: - """Returns ``True`` if this rrset matches the specified name, class, - type, covers, and deletion state. - """ - if not super().match(rdclass, rdtype, covers): - return False - if self.name != name or self.deleting != deleting: - return False - return True - - # pylint: disable=arguments-differ - - def to_text( # type: ignore[override] - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - **kw: Dict[str, Any], - ) -> str: - """Convert the RRset into DNS zone file format. - - See ``dns.name.Name.choose_relativity`` for more information - on how *origin* and *relativize* determine the way names - are emitted. - - Any additional keyword arguments are passed on to the rdata - ``to_text()`` method. - - *origin*, a ``dns.name.Name`` or ``None``, the origin for relative - names. - - *relativize*, a ``bool``. If ``True``, names will be relativized - to *origin*. - """ - - return super().to_text( - self.name, origin, relativize, self.deleting, **kw # type: ignore - ) - - def to_wire( # type: ignore[override] - self, - file: Any, - compress: Optional[dns.name.CompressType] = None, # type: ignore - origin: Optional[dns.name.Name] = None, - **kw: Dict[str, Any], - ) -> int: - """Convert the RRset to wire format. - - All keyword arguments are passed to ``dns.rdataset.to_wire()``; see - that function for details. - - Returns an ``int``, the number of records emitted. - """ - - return super().to_wire( - self.name, file, compress, origin, self.deleting, **kw # type:ignore - ) - - # pylint: enable=arguments-differ - - def to_rdataset(self) -> dns.rdataset.Rdataset: - """Convert an RRset into an Rdataset. - - Returns a ``dns.rdataset.Rdataset``. - """ - return dns.rdataset.from_rdata_list(self.ttl, list(self)) - - -def from_text_list( - name: Union[dns.name.Name, str], - ttl: int, - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - text_rdatas: Collection[str], - idna_codec: Optional[dns.name.IDNACodec] = None, - origin: Optional[dns.name.Name] = None, - relativize: bool = True, - relativize_to: Optional[dns.name.Name] = None, -) -> RRset: - """Create an RRset with the specified name, TTL, class, and type, and with - the specified list of rdatas in text format. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use; if ``None``, the default IDNA 2003 - encoder/decoder is used. - - *origin*, a ``dns.name.Name`` (or ``None``), the - origin to use for relative names. - - *relativize*, a ``bool``. If true, name will be relativized. - - *relativize_to*, a ``dns.name.Name`` (or ``None``), the origin to use - when relativizing names. If not set, the *origin* value will be used. - - Returns a ``dns.rrset.RRset`` object. - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None, idna_codec=idna_codec) - rdclass = dns.rdataclass.RdataClass.make(rdclass) - rdtype = dns.rdatatype.RdataType.make(rdtype) - r = RRset(name, rdclass, rdtype) - r.update_ttl(ttl) - for t in text_rdatas: - rd = dns.rdata.from_text( - r.rdclass, r.rdtype, t, origin, relativize, relativize_to, idna_codec - ) - r.add(rd) - return r - - -def from_text( - name: Union[dns.name.Name, str], - ttl: int, - rdclass: Union[dns.rdataclass.RdataClass, str], - rdtype: Union[dns.rdatatype.RdataType, str], - *text_rdatas: Any, -) -> RRset: - """Create an RRset with the specified name, TTL, class, and type and with - the specified rdatas in text format. - - Returns a ``dns.rrset.RRset`` object. - """ - - return from_text_list( - name, ttl, rdclass, rdtype, cast(Collection[str], text_rdatas) - ) - - -def from_rdata_list( - name: Union[dns.name.Name, str], - ttl: int, - rdatas: Collection[dns.rdata.Rdata], - idna_codec: Optional[dns.name.IDNACodec] = None, -) -> RRset: - """Create an RRset with the specified name and TTL, and with - the specified list of rdata objects. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder to use; if ``None``, the default IDNA 2003 - encoder/decoder is used. - - Returns a ``dns.rrset.RRset`` object. - - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None, idna_codec=idna_codec) - - if len(rdatas) == 0: - raise ValueError("rdata list must not be empty") - r = None - for rd in rdatas: - if r is None: - r = RRset(name, rd.rdclass, rd.rdtype) - r.update_ttl(ttl) - r.add(rd) - assert r is not None - return r - - -def from_rdata(name: Union[dns.name.Name, str], ttl: int, *rdatas: Any) -> RRset: - """Create an RRset with the specified name and TTL, and with - the specified rdata objects. - - Returns a ``dns.rrset.RRset`` object. - """ - - return from_rdata_list(name, ttl, cast(Collection[dns.rdata.Rdata], rdatas)) diff --git a/.venv/lib/python3.12/site-packages/dns/serial.py b/.venv/lib/python3.12/site-packages/dns/serial.py deleted file mode 100644 index 3417299..0000000 --- a/.venv/lib/python3.12/site-packages/dns/serial.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""Serial Number Arthimetic from RFC 1982""" - - -class Serial: - def __init__(self, value: int, bits: int = 32): - self.value = value % 2**bits - self.bits = bits - - def __repr__(self): - return f"dns.serial.Serial({self.value}, {self.bits})" - - def __eq__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - return self.value == other.value - - def __ne__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - return self.value != other.value - - def __lt__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - if self.value < other.value and other.value - self.value < 2 ** (self.bits - 1): - return True - elif self.value > other.value and self.value - other.value > 2 ** ( - self.bits - 1 - ): - return True - else: - return False - - def __le__(self, other): - return self == other or self < other - - def __gt__(self, other): - if isinstance(other, int): - other = Serial(other, self.bits) - elif not isinstance(other, Serial) or other.bits != self.bits: - return NotImplemented - if self.value < other.value and other.value - self.value > 2 ** (self.bits - 1): - return True - elif self.value > other.value and self.value - other.value < 2 ** ( - self.bits - 1 - ): - return True - else: - return False - - def __ge__(self, other): - return self == other or self > other - - def __add__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v += delta - v = v % 2**self.bits - return Serial(v, self.bits) - - def __iadd__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v += delta - v = v % 2**self.bits - self.value = v - return self - - def __sub__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v -= delta - v = v % 2**self.bits - return Serial(v, self.bits) - - def __isub__(self, other): - v = self.value - if isinstance(other, Serial): - delta = other.value - elif isinstance(other, int): - delta = other - else: - raise ValueError - if abs(delta) > (2 ** (self.bits - 1) - 1): - raise ValueError - v -= delta - v = v % 2**self.bits - self.value = v - return self diff --git a/.venv/lib/python3.12/site-packages/dns/set.py b/.venv/lib/python3.12/site-packages/dns/set.py deleted file mode 100644 index f0fb0d5..0000000 --- a/.venv/lib/python3.12/site-packages/dns/set.py +++ /dev/null @@ -1,307 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -import itertools - - -class Set: - """A simple set class. - - This class was originally used to deal with sets being missing in - ancient versions of python, but dnspython will continue to use it - as these sets are based on lists and are thus indexable, and this - ability is widely used in dnspython applications. - """ - - __slots__ = ["items"] - - def __init__(self, items=None): - """Initialize the set. - - *items*, an iterable or ``None``, the initial set of items. - """ - - self.items = dict() - if items is not None: - for item in items: - # This is safe for how we use set, but if other code - # subclasses it could be a legitimate issue. - self.add(item) # lgtm[py/init-calls-subclass] - - def __repr__(self): - return "dns.set.Set(%s)" % repr(list(self.items.keys())) - - def add(self, item): - """Add an item to the set.""" - - if item not in self.items: - self.items[item] = None - - def remove(self, item): - """Remove an item from the set.""" - - try: - del self.items[item] - except KeyError: - raise ValueError - - def discard(self, item): - """Remove an item from the set if present.""" - - self.items.pop(item, None) - - def pop(self): - """Remove an arbitrary item from the set.""" - (k, _) = self.items.popitem() - return k - - def _clone(self) -> "Set": - """Make a (shallow) copy of the set. - - There is a 'clone protocol' that subclasses of this class - should use. To make a copy, first call your super's _clone() - method, and use the object returned as the new instance. Then - make shallow copies of the attributes defined in the subclass. - - This protocol allows us to write the set algorithms that - return new instances (e.g. union) once, and keep using them in - subclasses. - """ - - if hasattr(self, "_clone_class"): - cls = self._clone_class # type: ignore - else: - cls = self.__class__ - obj = cls.__new__(cls) - obj.items = dict() - obj.items.update(self.items) - return obj - - def __copy__(self): - """Make a (shallow) copy of the set.""" - - return self._clone() - - def copy(self): - """Make a (shallow) copy of the set.""" - - return self._clone() - - def union_update(self, other): - """Update the set, adding any elements from other which are not - already in the set. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - return - for item in other.items: - self.add(item) - - def intersection_update(self, other): - """Update the set, removing any elements from other which are not - in both sets. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - return - # we make a copy of the list so that we can remove items from - # the list without breaking the iterator. - for item in list(self.items): - if item not in other.items: - del self.items[item] - - def difference_update(self, other): - """Update the set, removing any elements from other which are in - the set. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - self.items.clear() - else: - for item in other.items: - self.discard(item) - - def symmetric_difference_update(self, other): - """Update the set, retaining only elements unique to both sets.""" - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - if self is other: # lgtm[py/comparison-using-is] - self.items.clear() - else: - overlap = self.intersection(other) - self.union_update(other) - self.difference_update(overlap) - - def union(self, other): - """Return a new set which is the union of ``self`` and ``other``. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.union_update(other) - return obj - - def intersection(self, other): - """Return a new set which is the intersection of ``self`` and - ``other``. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.intersection_update(other) - return obj - - def difference(self, other): - """Return a new set which ``self`` - ``other``, i.e. the items - in ``self`` which are not also in ``other``. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.difference_update(other) - return obj - - def symmetric_difference(self, other): - """Return a new set which (``self`` - ``other``) | (``other`` - - ``self), ie: the items in either ``self`` or ``other`` which - are not contained in their intersection. - - Returns the same Set type as this set. - """ - - obj = self._clone() - obj.symmetric_difference_update(other) - return obj - - def __or__(self, other): - return self.union(other) - - def __and__(self, other): - return self.intersection(other) - - def __add__(self, other): - return self.union(other) - - def __sub__(self, other): - return self.difference(other) - - def __xor__(self, other): - return self.symmetric_difference(other) - - def __ior__(self, other): - self.union_update(other) - return self - - def __iand__(self, other): - self.intersection_update(other) - return self - - def __iadd__(self, other): - self.union_update(other) - return self - - def __isub__(self, other): - self.difference_update(other) - return self - - def __ixor__(self, other): - self.symmetric_difference_update(other) - return self - - def update(self, other): - """Update the set, adding any elements from other which are not - already in the set. - - *other*, the collection of items with which to update the set, which - may be any iterable type. - """ - - for item in other: - self.add(item) - - def clear(self): - """Make the set empty.""" - self.items.clear() - - def __eq__(self, other): - return self.items == other.items - - def __ne__(self, other): - return not self.__eq__(other) - - def __len__(self): - return len(self.items) - - def __iter__(self): - return iter(self.items) - - def __getitem__(self, i): - if isinstance(i, slice): - return list(itertools.islice(self.items, i.start, i.stop, i.step)) - else: - return next(itertools.islice(self.items, i, i + 1)) - - def __delitem__(self, i): - if isinstance(i, slice): - for elt in list(self[i]): - del self.items[elt] - else: - del self.items[self[i]] - - def issubset(self, other): - """Is this set a subset of *other*? - - Returns a ``bool``. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - for item in self.items: - if item not in other.items: - return False - return True - - def issuperset(self, other): - """Is this set a superset of *other*? - - Returns a ``bool``. - """ - - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - for item in other.items: - if item not in self.items: - return False - return True - - def isdisjoint(self, other): - if not isinstance(other, Set): - raise ValueError("other must be a Set instance") - for item in other.items: - if item in self.items: - return False - return True diff --git a/.venv/lib/python3.12/site-packages/dns/tokenizer.py b/.venv/lib/python3.12/site-packages/dns/tokenizer.py deleted file mode 100644 index 454cac4..0000000 --- a/.venv/lib/python3.12/site-packages/dns/tokenizer.py +++ /dev/null @@ -1,708 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""Tokenize DNS zone file format""" - -import io -import sys -from typing import Any, List, Optional, Tuple - -import dns.exception -import dns.name -import dns.ttl - -_DELIMITERS = {" ", "\t", "\n", ";", "(", ")", '"'} -_QUOTING_DELIMITERS = {'"'} - -EOF = 0 -EOL = 1 -WHITESPACE = 2 -IDENTIFIER = 3 -QUOTED_STRING = 4 -COMMENT = 5 -DELIMITER = 6 - - -class UngetBufferFull(dns.exception.DNSException): - """An attempt was made to unget a token when the unget buffer was full.""" - - -class Token: - """A DNS zone file format token. - - ttype: The token type - value: The token value - has_escape: Does the token value contain escapes? - """ - - def __init__( - self, - ttype: int, - value: Any = "", - has_escape: bool = False, - comment: Optional[str] = None, - ): - """Initialize a token instance.""" - - self.ttype = ttype - self.value = value - self.has_escape = has_escape - self.comment = comment - - def is_eof(self) -> bool: - return self.ttype == EOF - - def is_eol(self) -> bool: - return self.ttype == EOL - - def is_whitespace(self) -> bool: - return self.ttype == WHITESPACE - - def is_identifier(self) -> bool: - return self.ttype == IDENTIFIER - - def is_quoted_string(self) -> bool: - return self.ttype == QUOTED_STRING - - def is_comment(self) -> bool: - return self.ttype == COMMENT - - def is_delimiter(self) -> bool: # pragma: no cover (we don't return delimiters yet) - return self.ttype == DELIMITER - - def is_eol_or_eof(self) -> bool: - return self.ttype == EOL or self.ttype == EOF - - def __eq__(self, other): - if not isinstance(other, Token): - return False - return self.ttype == other.ttype and self.value == other.value - - def __ne__(self, other): - if not isinstance(other, Token): - return True - return self.ttype != other.ttype or self.value != other.value - - def __str__(self): - return '%d "%s"' % (self.ttype, self.value) - - def unescape(self) -> "Token": - if not self.has_escape: - return self - unescaped = "" - l = len(self.value) - i = 0 - while i < l: - c = self.value[i] - i += 1 - if c == "\\": - if i >= l: # pragma: no cover (can't happen via get()) - raise dns.exception.UnexpectedEnd - c = self.value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = self.value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = self.value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - codepoint = int(c) * 100 + int(c2) * 10 + int(c3) - if codepoint > 255: - raise dns.exception.SyntaxError - c = chr(codepoint) - unescaped += c - return Token(self.ttype, unescaped) - - def unescape_to_bytes(self) -> "Token": - # We used to use unescape() for TXT-like records, but this - # caused problems as we'd process DNS escapes into Unicode code - # points instead of byte values, and then a to_text() of the - # processed data would not equal the original input. For - # example, \226 in the TXT record would have a to_text() of - # \195\162 because we applied UTF-8 encoding to Unicode code - # point 226. - # - # We now apply escapes while converting directly to bytes, - # avoiding this double encoding. - # - # This code also handles cases where the unicode input has - # non-ASCII code-points in it by converting it to UTF-8. TXT - # records aren't defined for Unicode, but this is the best we - # can do to preserve meaning. For example, - # - # foo\u200bbar - # - # (where \u200b is Unicode code point 0x200b) will be treated - # as if the input had been the UTF-8 encoding of that string, - # namely: - # - # foo\226\128\139bar - # - unescaped = b"" - l = len(self.value) - i = 0 - while i < l: - c = self.value[i] - i += 1 - if c == "\\": - if i >= l: # pragma: no cover (can't happen via get()) - raise dns.exception.UnexpectedEnd - c = self.value[i] - i += 1 - if c.isdigit(): - if i >= l: - raise dns.exception.UnexpectedEnd - c2 = self.value[i] - i += 1 - if i >= l: - raise dns.exception.UnexpectedEnd - c3 = self.value[i] - i += 1 - if not (c2.isdigit() and c3.isdigit()): - raise dns.exception.SyntaxError - codepoint = int(c) * 100 + int(c2) * 10 + int(c3) - if codepoint > 255: - raise dns.exception.SyntaxError - unescaped += b"%c" % (codepoint) - else: - # Note that as mentioned above, if c is a Unicode - # code point outside of the ASCII range, then this - # += is converting that code point to its UTF-8 - # encoding and appending multiple bytes to - # unescaped. - unescaped += c.encode() - else: - unescaped += c.encode() - return Token(self.ttype, bytes(unescaped)) - - -class Tokenizer: - """A DNS zone file format tokenizer. - - A token object is basically a (type, value) tuple. The valid - types are EOF, EOL, WHITESPACE, IDENTIFIER, QUOTED_STRING, - COMMENT, and DELIMITER. - - file: The file to tokenize - - ungotten_char: The most recently ungotten character, or None. - - ungotten_token: The most recently ungotten token, or None. - - multiline: The current multiline level. This value is increased - by one every time a '(' delimiter is read, and decreased by one every time - a ')' delimiter is read. - - quoting: This variable is true if the tokenizer is currently - reading a quoted string. - - eof: This variable is true if the tokenizer has encountered EOF. - - delimiters: The current delimiter dictionary. - - line_number: The current line number - - filename: A filename that will be returned by the where() method. - - idna_codec: A dns.name.IDNACodec, specifies the IDNA - encoder/decoder. If None, the default IDNA 2003 - encoder/decoder is used. - """ - - def __init__( - self, - f: Any = sys.stdin, - filename: Optional[str] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, - ): - """Initialize a tokenizer instance. - - f: The file to tokenize. The default is sys.stdin. - This parameter may also be a string, in which case the tokenizer - will take its input from the contents of the string. - - filename: the name of the filename that the where() method - will return. - - idna_codec: A dns.name.IDNACodec, specifies the IDNA - encoder/decoder. If None, the default IDNA 2003 - encoder/decoder is used. - """ - - if isinstance(f, str): - f = io.StringIO(f) - if filename is None: - filename = "" - elif isinstance(f, bytes): - f = io.StringIO(f.decode()) - if filename is None: - filename = "" - else: - if filename is None: - if f is sys.stdin: - filename = "" - else: - filename = "" - self.file = f - self.ungotten_char: Optional[str] = None - self.ungotten_token: Optional[Token] = None - self.multiline = 0 - self.quoting = False - self.eof = False - self.delimiters = _DELIMITERS - self.line_number = 1 - assert filename is not None - self.filename = filename - if idna_codec is None: - self.idna_codec: dns.name.IDNACodec = dns.name.IDNA_2003 - else: - self.idna_codec = idna_codec - - def _get_char(self) -> str: - """Read a character from input.""" - - if self.ungotten_char is None: - if self.eof: - c = "" - else: - c = self.file.read(1) - if c == "": - self.eof = True - elif c == "\n": - self.line_number += 1 - else: - c = self.ungotten_char - self.ungotten_char = None - return c - - def where(self) -> Tuple[str, int]: - """Return the current location in the input. - - Returns a (string, int) tuple. The first item is the filename of - the input, the second is the current line number. - """ - - return (self.filename, self.line_number) - - def _unget_char(self, c: str) -> None: - """Unget a character. - - The unget buffer for characters is only one character large; it is - an error to try to unget a character when the unget buffer is not - empty. - - c: the character to unget - raises UngetBufferFull: there is already an ungotten char - """ - - if self.ungotten_char is not None: - # this should never happen! - raise UngetBufferFull # pragma: no cover - self.ungotten_char = c - - def skip_whitespace(self) -> int: - """Consume input until a non-whitespace character is encountered. - - The non-whitespace character is then ungotten, and the number of - whitespace characters consumed is returned. - - If the tokenizer is in multiline mode, then newlines are whitespace. - - Returns the number of characters skipped. - """ - - skipped = 0 - while True: - c = self._get_char() - if c != " " and c != "\t": - if (c != "\n") or not self.multiline: - self._unget_char(c) - return skipped - skipped += 1 - - def get(self, want_leading: bool = False, want_comment: bool = False) -> Token: - """Get the next token. - - want_leading: If True, return a WHITESPACE token if the - first character read is whitespace. The default is False. - - want_comment: If True, return a COMMENT token if the - first token read is a comment. The default is False. - - Raises dns.exception.UnexpectedEnd: input ended prematurely - - Raises dns.exception.SyntaxError: input was badly formed - - Returns a Token. - """ - - if self.ungotten_token is not None: - utoken = self.ungotten_token - self.ungotten_token = None - if utoken.is_whitespace(): - if want_leading: - return utoken - elif utoken.is_comment(): - if want_comment: - return utoken - else: - return utoken - skipped = self.skip_whitespace() - if want_leading and skipped > 0: - return Token(WHITESPACE, " ") - token = "" - ttype = IDENTIFIER - has_escape = False - while True: - c = self._get_char() - if c == "" or c in self.delimiters: - if c == "" and self.quoting: - raise dns.exception.UnexpectedEnd - if token == "" and ttype != QUOTED_STRING: - if c == "(": - self.multiline += 1 - self.skip_whitespace() - continue - elif c == ")": - if self.multiline <= 0: - raise dns.exception.SyntaxError - self.multiline -= 1 - self.skip_whitespace() - continue - elif c == '"': - if not self.quoting: - self.quoting = True - self.delimiters = _QUOTING_DELIMITERS - ttype = QUOTED_STRING - continue - else: - self.quoting = False - self.delimiters = _DELIMITERS - self.skip_whitespace() - continue - elif c == "\n": - return Token(EOL, "\n") - elif c == ";": - while 1: - c = self._get_char() - if c == "\n" or c == "": - break - token += c - if want_comment: - self._unget_char(c) - return Token(COMMENT, token) - elif c == "": - if self.multiline: - raise dns.exception.SyntaxError( - "unbalanced parentheses" - ) - return Token(EOF, comment=token) - elif self.multiline: - self.skip_whitespace() - token = "" - continue - else: - return Token(EOL, "\n", comment=token) - else: - # This code exists in case we ever want a - # delimiter to be returned. It never produces - # a token currently. - token = c - ttype = DELIMITER - else: - self._unget_char(c) - break - elif self.quoting and c == "\n": - raise dns.exception.SyntaxError("newline in quoted string") - elif c == "\\": - # - # It's an escape. Put it and the next character into - # the token; it will be checked later for goodness. - # - token += c - has_escape = True - c = self._get_char() - if c == "" or (c == "\n" and not self.quoting): - raise dns.exception.UnexpectedEnd - token += c - if token == "" and ttype != QUOTED_STRING: - if self.multiline: - raise dns.exception.SyntaxError("unbalanced parentheses") - ttype = EOF - return Token(ttype, token, has_escape) - - def unget(self, token: Token) -> None: - """Unget a token. - - The unget buffer for tokens is only one token large; it is - an error to try to unget a token when the unget buffer is not - empty. - - token: the token to unget - - Raises UngetBufferFull: there is already an ungotten token - """ - - if self.ungotten_token is not None: - raise UngetBufferFull - self.ungotten_token = token - - def next(self): - """Return the next item in an iteration. - - Returns a Token. - """ - - token = self.get() - if token.is_eof(): - raise StopIteration - return token - - __next__ = next - - def __iter__(self): - return self - - # Helpers - - def get_int(self, base: int = 10) -> int: - """Read the next token and interpret it as an unsigned integer. - - Raises dns.exception.SyntaxError if not an unsigned integer. - - Returns an int. - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - if not token.value.isdigit(): - raise dns.exception.SyntaxError("expecting an integer") - return int(token.value, base) - - def get_uint8(self) -> int: - """Read the next token and interpret it as an 8-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not an 8-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int() - if value < 0 or value > 255: - raise dns.exception.SyntaxError( - "%d is not an unsigned 8-bit integer" % value - ) - return value - - def get_uint16(self, base: int = 10) -> int: - """Read the next token and interpret it as a 16-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not a 16-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int(base=base) - if value < 0 or value > 65535: - if base == 8: - raise dns.exception.SyntaxError( - "%o is not an octal unsigned 16-bit integer" % value - ) - else: - raise dns.exception.SyntaxError( - "%d is not an unsigned 16-bit integer" % value - ) - return value - - def get_uint32(self, base: int = 10) -> int: - """Read the next token and interpret it as a 32-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not a 32-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int(base=base) - if value < 0 or value > 4294967295: - raise dns.exception.SyntaxError( - "%d is not an unsigned 32-bit integer" % value - ) - return value - - def get_uint48(self, base: int = 10) -> int: - """Read the next token and interpret it as a 48-bit unsigned - integer. - - Raises dns.exception.SyntaxError if not a 48-bit unsigned integer. - - Returns an int. - """ - - value = self.get_int(base=base) - if value < 0 or value > 281474976710655: - raise dns.exception.SyntaxError( - "%d is not an unsigned 48-bit integer" % value - ) - return value - - def get_string(self, max_length: Optional[int] = None) -> str: - """Read the next token and interpret it as a string. - - Raises dns.exception.SyntaxError if not a string. - Raises dns.exception.SyntaxError if token value length - exceeds max_length (if specified). - - Returns a string. - """ - - token = self.get().unescape() - if not (token.is_identifier() or token.is_quoted_string()): - raise dns.exception.SyntaxError("expecting a string") - if max_length and len(token.value) > max_length: - raise dns.exception.SyntaxError("string too long") - return token.value - - def get_identifier(self) -> str: - """Read the next token, which should be an identifier. - - Raises dns.exception.SyntaxError if not an identifier. - - Returns a string. - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - return token.value - - def get_remaining(self, max_tokens: Optional[int] = None) -> List[Token]: - """Return the remaining tokens on the line, until an EOL or EOF is seen. - - max_tokens: If not None, stop after this number of tokens. - - Returns a list of tokens. - """ - - tokens = [] - while True: - token = self.get() - if token.is_eol_or_eof(): - self.unget(token) - break - tokens.append(token) - if len(tokens) == max_tokens: - break - return tokens - - def concatenate_remaining_identifiers(self, allow_empty: bool = False) -> str: - """Read the remaining tokens on the line, which should be identifiers. - - Raises dns.exception.SyntaxError if there are no remaining tokens, - unless `allow_empty=True` is given. - - Raises dns.exception.SyntaxError if a token is seen that is not an - identifier. - - Returns a string containing a concatenation of the remaining - identifiers. - """ - s = "" - while True: - token = self.get().unescape() - if token.is_eol_or_eof(): - self.unget(token) - break - if not token.is_identifier(): - raise dns.exception.SyntaxError - s += token.value - if not (allow_empty or s): - raise dns.exception.SyntaxError("expecting another identifier") - return s - - def as_name( - self, - token: Token, - origin: Optional[dns.name.Name] = None, - relativize: bool = False, - relativize_to: Optional[dns.name.Name] = None, - ) -> dns.name.Name: - """Try to interpret the token as a DNS name. - - Raises dns.exception.SyntaxError if not a name. - - Returns a dns.name.Name. - """ - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - name = dns.name.from_text(token.value, origin, self.idna_codec) - return name.choose_relativity(relativize_to or origin, relativize) - - def get_name( - self, - origin: Optional[dns.name.Name] = None, - relativize: bool = False, - relativize_to: Optional[dns.name.Name] = None, - ) -> dns.name.Name: - """Read the next token and interpret it as a DNS name. - - Raises dns.exception.SyntaxError if not a name. - - Returns a dns.name.Name. - """ - - token = self.get() - return self.as_name(token, origin, relativize, relativize_to) - - def get_eol_as_token(self) -> Token: - """Read the next token and raise an exception if it isn't EOL or - EOF. - - Returns a string. - """ - - token = self.get() - if not token.is_eol_or_eof(): - raise dns.exception.SyntaxError( - 'expected EOL or EOF, got %d "%s"' % (token.ttype, token.value) - ) - return token - - def get_eol(self) -> str: - return self.get_eol_as_token().value - - def get_ttl(self) -> int: - """Read the next token and interpret it as a DNS TTL. - - Raises dns.exception.SyntaxError or dns.ttl.BadTTL if not an - identifier or badly formed. - - Returns an int. - """ - - token = self.get().unescape() - if not token.is_identifier(): - raise dns.exception.SyntaxError("expecting an identifier") - return dns.ttl.from_text(token.value) diff --git a/.venv/lib/python3.12/site-packages/dns/transaction.py b/.venv/lib/python3.12/site-packages/dns/transaction.py deleted file mode 100644 index 84e54f7..0000000 --- a/.venv/lib/python3.12/site-packages/dns/transaction.py +++ /dev/null @@ -1,651 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import collections -from typing import Any, Callable, Iterator, List, Optional, Tuple, Union - -import dns.exception -import dns.name -import dns.node -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rrset -import dns.serial -import dns.ttl - - -class TransactionManager: - def reader(self) -> "Transaction": - """Begin a read-only transaction.""" - raise NotImplementedError # pragma: no cover - - def writer(self, replacement: bool = False) -> "Transaction": - """Begin a writable transaction. - - *replacement*, a ``bool``. If `True`, the content of the - transaction completely replaces any prior content. If False, - the default, then the content of the transaction updates the - existing content. - """ - raise NotImplementedError # pragma: no cover - - def origin_information( - self, - ) -> Tuple[Optional[dns.name.Name], bool, Optional[dns.name.Name]]: - """Returns a tuple - - (absolute_origin, relativize, effective_origin) - - giving the absolute name of the default origin for any - relative domain names, the "effective origin", and whether - names should be relativized. The "effective origin" is the - absolute origin if relativize is False, and the empty name if - relativize is true. (The effective origin is provided even - though it can be computed from the absolute_origin and - relativize setting because it avoids a lot of code - duplication.) - - If the returned names are `None`, then no origin information is - available. - - This information is used by code working with transactions to - allow it to coordinate relativization. The transaction code - itself takes what it gets (i.e. does not change name - relativity). - - """ - raise NotImplementedError # pragma: no cover - - def get_class(self) -> dns.rdataclass.RdataClass: - """The class of the transaction manager.""" - raise NotImplementedError # pragma: no cover - - def from_wire_origin(self) -> Optional[dns.name.Name]: - """Origin to use in from_wire() calls.""" - (absolute_origin, relativize, _) = self.origin_information() - if relativize: - return absolute_origin - else: - return None - - -class DeleteNotExact(dns.exception.DNSException): - """Existing data did not match data specified by an exact delete.""" - - -class ReadOnly(dns.exception.DNSException): - """Tried to write to a read-only transaction.""" - - -class AlreadyEnded(dns.exception.DNSException): - """Tried to use an already-ended transaction.""" - - -def _ensure_immutable_rdataset(rdataset): - if rdataset is None or isinstance(rdataset, dns.rdataset.ImmutableRdataset): - return rdataset - return dns.rdataset.ImmutableRdataset(rdataset) - - -def _ensure_immutable_node(node): - if node is None or node.is_immutable(): - return node - return dns.node.ImmutableNode(node) - - -CheckPutRdatasetType = Callable[ - ["Transaction", dns.name.Name, dns.rdataset.Rdataset], None -] -CheckDeleteRdatasetType = Callable[ - ["Transaction", dns.name.Name, dns.rdatatype.RdataType, dns.rdatatype.RdataType], - None, -] -CheckDeleteNameType = Callable[["Transaction", dns.name.Name], None] - - -class Transaction: - def __init__( - self, - manager: TransactionManager, - replacement: bool = False, - read_only: bool = False, - ): - self.manager = manager - self.replacement = replacement - self.read_only = read_only - self._ended = False - self._check_put_rdataset: List[CheckPutRdatasetType] = [] - self._check_delete_rdataset: List[CheckDeleteRdatasetType] = [] - self._check_delete_name: List[CheckDeleteNameType] = [] - - # - # This is the high level API - # - # Note that we currently use non-immutable types in the return type signature to - # avoid covariance problems, e.g. if the caller has a List[Rdataset], mypy will be - # unhappy if we return an ImmutableRdataset. - - def get( - self, - name: Optional[Union[dns.name.Name, str]], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> dns.rdataset.Rdataset: - """Return the rdataset associated with *name*, *rdtype*, and *covers*, - or `None` if not found. - - Note that the returned rdataset is immutable. - """ - self._check_ended() - if isinstance(name, str): - name = dns.name.from_text(name, None) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - rdataset = self._get_rdataset(name, rdtype, covers) - return _ensure_immutable_rdataset(rdataset) - - def get_node(self, name: dns.name.Name) -> Optional[dns.node.Node]: - """Return the node at *name*, if any. - - Returns an immutable node or ``None``. - """ - return _ensure_immutable_node(self._get_node(name)) - - def _check_read_only(self) -> None: - if self.read_only: - raise ReadOnly - - def add(self, *args: Any) -> None: - """Add records. - - The arguments may be: - - - rrset - - - name, rdataset... - - - name, ttl, rdata... - """ - self._check_ended() - self._check_read_only() - self._add(False, args) - - def replace(self, *args: Any) -> None: - """Replace the existing rdataset at the name with the specified - rdataset, or add the specified rdataset if there was no existing - rdataset. - - The arguments may be: - - - rrset - - - name, rdataset... - - - name, ttl, rdata... - - Note that if you want to replace the entire node, you should do - a delete of the name followed by one or more calls to add() or - replace(). - """ - self._check_ended() - self._check_read_only() - self._add(True, args) - - def delete(self, *args: Any) -> None: - """Delete records. - - It is not an error if some of the records are not in the existing - set. - - The arguments may be: - - - rrset - - - name - - - name, rdatatype, [covers] - - - name, rdataset... - - - name, rdata... - """ - self._check_ended() - self._check_read_only() - self._delete(False, args) - - def delete_exact(self, *args: Any) -> None: - """Delete records. - - The arguments may be: - - - rrset - - - name - - - name, rdatatype, [covers] - - - name, rdataset... - - - name, rdata... - - Raises dns.transaction.DeleteNotExact if some of the records - are not in the existing set. - - """ - self._check_ended() - self._check_read_only() - self._delete(True, args) - - def name_exists(self, name: Union[dns.name.Name, str]) -> bool: - """Does the specified name exist?""" - self._check_ended() - if isinstance(name, str): - name = dns.name.from_text(name, None) - return self._name_exists(name) - - def update_serial( - self, - value: int = 1, - relative: bool = True, - name: dns.name.Name = dns.name.empty, - ) -> None: - """Update the serial number. - - *value*, an `int`, is an increment if *relative* is `True`, or the - actual value to set if *relative* is `False`. - - Raises `KeyError` if there is no SOA rdataset at *name*. - - Raises `ValueError` if *value* is negative or if the increment is - so large that it would cause the new serial to be less than the - prior value. - """ - self._check_ended() - if value < 0: - raise ValueError("negative update_serial() value") - if isinstance(name, str): - name = dns.name.from_text(name, None) - rdataset = self._get_rdataset(name, dns.rdatatype.SOA, dns.rdatatype.NONE) - if rdataset is None or len(rdataset) == 0: - raise KeyError - if relative: - serial = dns.serial.Serial(rdataset[0].serial) + value - else: - serial = dns.serial.Serial(value) - serial = serial.value # convert back to int - if serial == 0: - serial = 1 - rdata = rdataset[0].replace(serial=serial) - new_rdataset = dns.rdataset.from_rdata(rdataset.ttl, rdata) - self.replace(name, new_rdataset) - - def __iter__(self): - self._check_ended() - return self._iterate_rdatasets() - - def changed(self) -> bool: - """Has this transaction changed anything? - - For read-only transactions, the result is always `False`. - - For writable transactions, the result is `True` if at some time - during the life of the transaction, the content was changed. - """ - self._check_ended() - return self._changed() - - def commit(self) -> None: - """Commit the transaction. - - Normally transactions are used as context managers and commit - or rollback automatically, but it may be done explicitly if needed. - A ``dns.transaction.Ended`` exception will be raised if you try - to use a transaction after it has been committed or rolled back. - - Raises an exception if the commit fails (in which case the transaction - is also rolled back. - """ - self._end(True) - - def rollback(self) -> None: - """Rollback the transaction. - - Normally transactions are used as context managers and commit - or rollback automatically, but it may be done explicitly if needed. - A ``dns.transaction.AlreadyEnded`` exception will be raised if you try - to use a transaction after it has been committed or rolled back. - - Rollback cannot otherwise fail. - """ - self._end(False) - - def check_put_rdataset(self, check: CheckPutRdatasetType) -> None: - """Call *check* before putting (storing) an rdataset. - - The function is called with the transaction, the name, and the rdataset. - - The check function may safely make non-mutating transaction method - calls, but behavior is undefined if mutating transaction methods are - called. The check function should raise an exception if it objects to - the put, and otherwise should return ``None``. - """ - self._check_put_rdataset.append(check) - - def check_delete_rdataset(self, check: CheckDeleteRdatasetType) -> None: - """Call *check* before deleting an rdataset. - - The function is called with the transaction, the name, the rdatatype, - and the covered rdatatype. - - The check function may safely make non-mutating transaction method - calls, but behavior is undefined if mutating transaction methods are - called. The check function should raise an exception if it objects to - the put, and otherwise should return ``None``. - """ - self._check_delete_rdataset.append(check) - - def check_delete_name(self, check: CheckDeleteNameType) -> None: - """Call *check* before putting (storing) an rdataset. - - The function is called with the transaction and the name. - - The check function may safely make non-mutating transaction method - calls, but behavior is undefined if mutating transaction methods are - called. The check function should raise an exception if it objects to - the put, and otherwise should return ``None``. - """ - self._check_delete_name.append(check) - - def iterate_rdatasets( - self, - ) -> Iterator[Tuple[dns.name.Name, dns.rdataset.Rdataset]]: - """Iterate all the rdatasets in the transaction, returning - (`dns.name.Name`, `dns.rdataset.Rdataset`) tuples. - - Note that as is usual with python iterators, adding or removing items - while iterating will invalidate the iterator and may raise `RuntimeError` - or fail to iterate over all entries.""" - self._check_ended() - return self._iterate_rdatasets() - - def iterate_names(self) -> Iterator[dns.name.Name]: - """Iterate all the names in the transaction. - - Note that as is usual with python iterators, adding or removing names - while iterating will invalidate the iterator and may raise `RuntimeError` - or fail to iterate over all entries.""" - self._check_ended() - return self._iterate_names() - - # - # Helper methods - # - - def _raise_if_not_empty(self, method, args): - if len(args) != 0: - raise TypeError(f"extra parameters to {method}") - - def _rdataset_from_args(self, method, deleting, args): - try: - arg = args.popleft() - if isinstance(arg, dns.rrset.RRset): - rdataset = arg.to_rdataset() - elif isinstance(arg, dns.rdataset.Rdataset): - rdataset = arg - else: - if deleting: - ttl = 0 - else: - if isinstance(arg, int): - ttl = arg - if ttl > dns.ttl.MAX_TTL: - raise ValueError(f"{method}: TTL value too big") - else: - raise TypeError(f"{method}: expected a TTL") - arg = args.popleft() - if isinstance(arg, dns.rdata.Rdata): - rdataset = dns.rdataset.from_rdata(ttl, arg) - else: - raise TypeError(f"{method}: expected an Rdata") - return rdataset - except IndexError: - if deleting: - return None - else: - # reraise - raise TypeError(f"{method}: expected more arguments") - - def _add(self, replace, args): - try: - args = collections.deque(args) - if replace: - method = "replace()" - else: - method = "add()" - arg = args.popleft() - if isinstance(arg, str): - arg = dns.name.from_text(arg, None) - if isinstance(arg, dns.name.Name): - name = arg - rdataset = self._rdataset_from_args(method, False, args) - elif isinstance(arg, dns.rrset.RRset): - rrset = arg - name = rrset.name - # rrsets are also rdatasets, but they don't print the - # same and can't be stored in nodes, so convert. - rdataset = rrset.to_rdataset() - else: - raise TypeError( - f"{method} requires a name or RRset as the first argument" - ) - if rdataset.rdclass != self.manager.get_class(): - raise ValueError(f"{method} has objects of wrong RdataClass") - if rdataset.rdtype == dns.rdatatype.SOA: - (_, _, origin) = self._origin_information() - if name != origin: - raise ValueError(f"{method} has non-origin SOA") - self._raise_if_not_empty(method, args) - if not replace: - existing = self._get_rdataset(name, rdataset.rdtype, rdataset.covers) - if existing is not None: - if isinstance(existing, dns.rdataset.ImmutableRdataset): - trds = dns.rdataset.Rdataset( - existing.rdclass, existing.rdtype, existing.covers - ) - trds.update(existing) - existing = trds - rdataset = existing.union(rdataset) - self._checked_put_rdataset(name, rdataset) - except IndexError: - raise TypeError(f"not enough parameters to {method}") - - def _delete(self, exact, args): - try: - args = collections.deque(args) - if exact: - method = "delete_exact()" - else: - method = "delete()" - arg = args.popleft() - if isinstance(arg, str): - arg = dns.name.from_text(arg, None) - if isinstance(arg, dns.name.Name): - name = arg - if len(args) > 0 and ( - isinstance(args[0], int) or isinstance(args[0], str) - ): - # deleting by type and (optionally) covers - rdtype = dns.rdatatype.RdataType.make(args.popleft()) - if len(args) > 0: - covers = dns.rdatatype.RdataType.make(args.popleft()) - else: - covers = dns.rdatatype.NONE - self._raise_if_not_empty(method, args) - existing = self._get_rdataset(name, rdtype, covers) - if existing is None: - if exact: - raise DeleteNotExact(f"{method}: missing rdataset") - else: - self._delete_rdataset(name, rdtype, covers) - return - else: - rdataset = self._rdataset_from_args(method, True, args) - elif isinstance(arg, dns.rrset.RRset): - rdataset = arg # rrsets are also rdatasets - name = rdataset.name - else: - raise TypeError( - f"{method} requires a name or RRset as the first argument" - ) - self._raise_if_not_empty(method, args) - if rdataset: - if rdataset.rdclass != self.manager.get_class(): - raise ValueError(f"{method} has objects of wrong RdataClass") - existing = self._get_rdataset(name, rdataset.rdtype, rdataset.covers) - if existing is not None: - if exact: - intersection = existing.intersection(rdataset) - if intersection != rdataset: - raise DeleteNotExact(f"{method}: missing rdatas") - rdataset = existing.difference(rdataset) - if len(rdataset) == 0: - self._checked_delete_rdataset( - name, rdataset.rdtype, rdataset.covers - ) - else: - self._checked_put_rdataset(name, rdataset) - elif exact: - raise DeleteNotExact(f"{method}: missing rdataset") - else: - if exact and not self._name_exists(name): - raise DeleteNotExact(f"{method}: name not known") - self._checked_delete_name(name) - except IndexError: - raise TypeError(f"not enough parameters to {method}") - - def _check_ended(self): - if self._ended: - raise AlreadyEnded - - def _end(self, commit): - self._check_ended() - if self._ended: - raise AlreadyEnded - try: - self._end_transaction(commit) - finally: - self._ended = True - - def _checked_put_rdataset(self, name, rdataset): - for check in self._check_put_rdataset: - check(self, name, rdataset) - self._put_rdataset(name, rdataset) - - def _checked_delete_rdataset(self, name, rdtype, covers): - for check in self._check_delete_rdataset: - check(self, name, rdtype, covers) - self._delete_rdataset(name, rdtype, covers) - - def _checked_delete_name(self, name): - for check in self._check_delete_name: - check(self, name) - self._delete_name(name) - - # - # Transactions are context managers. - # - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if not self._ended: - if exc_type is None: - self.commit() - else: - self.rollback() - return False - - # - # This is the low level API, which must be implemented by subclasses - # of Transaction. - # - - def _get_rdataset(self, name, rdtype, covers): - """Return the rdataset associated with *name*, *rdtype*, and *covers*, - or `None` if not found. - """ - raise NotImplementedError # pragma: no cover - - def _put_rdataset(self, name, rdataset): - """Store the rdataset.""" - raise NotImplementedError # pragma: no cover - - def _delete_name(self, name): - """Delete all data associated with *name*. - - It is not an error if the name does not exist. - """ - raise NotImplementedError # pragma: no cover - - def _delete_rdataset(self, name, rdtype, covers): - """Delete all data associated with *name*, *rdtype*, and *covers*. - - It is not an error if the rdataset does not exist. - """ - raise NotImplementedError # pragma: no cover - - def _name_exists(self, name): - """Does name exist? - - Returns a bool. - """ - raise NotImplementedError # pragma: no cover - - def _changed(self): - """Has this transaction changed anything?""" - raise NotImplementedError # pragma: no cover - - def _end_transaction(self, commit): - """End the transaction. - - *commit*, a bool. If ``True``, commit the transaction, otherwise - roll it back. - - If committing and the commit fails, then roll back and raise an - exception. - """ - raise NotImplementedError # pragma: no cover - - def _set_origin(self, origin): - """Set the origin. - - This method is called when reading a possibly relativized - source, and an origin setting operation occurs (e.g. $ORIGIN - in a zone file). - """ - raise NotImplementedError # pragma: no cover - - def _iterate_rdatasets(self): - """Return an iterator that yields (name, rdataset) tuples.""" - raise NotImplementedError # pragma: no cover - - def _iterate_names(self): - """Return an iterator that yields a name.""" - raise NotImplementedError # pragma: no cover - - def _get_node(self, name): - """Return the node at *name*, if any. - - Returns a node or ``None``. - """ - raise NotImplementedError # pragma: no cover - - # - # Low-level API with a default implementation, in case a subclass needs - # to override. - # - - def _origin_information(self): - # This is only used by _add() - return self.manager.origin_information() diff --git a/.venv/lib/python3.12/site-packages/dns/tsig.py b/.venv/lib/python3.12/site-packages/dns/tsig.py deleted file mode 100644 index 780852e..0000000 --- a/.venv/lib/python3.12/site-packages/dns/tsig.py +++ /dev/null @@ -1,352 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2001-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS TSIG support.""" - -import base64 -import hashlib -import hmac -import struct - -import dns.exception -import dns.name -import dns.rcode -import dns.rdataclass - - -class BadTime(dns.exception.DNSException): - """The current time is not within the TSIG's validity time.""" - - -class BadSignature(dns.exception.DNSException): - """The TSIG signature fails to verify.""" - - -class BadKey(dns.exception.DNSException): - """The TSIG record owner name does not match the key.""" - - -class BadAlgorithm(dns.exception.DNSException): - """The TSIG algorithm does not match the key.""" - - -class PeerError(dns.exception.DNSException): - """Base class for all TSIG errors generated by the remote peer""" - - -class PeerBadKey(PeerError): - """The peer didn't know the key we used""" - - -class PeerBadSignature(PeerError): - """The peer didn't like the signature we sent""" - - -class PeerBadTime(PeerError): - """The peer didn't like the time we sent""" - - -class PeerBadTruncation(PeerError): - """The peer didn't like amount of truncation in the TSIG we sent""" - - -# TSIG Algorithms - -HMAC_MD5 = dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT") -HMAC_SHA1 = dns.name.from_text("hmac-sha1") -HMAC_SHA224 = dns.name.from_text("hmac-sha224") -HMAC_SHA256 = dns.name.from_text("hmac-sha256") -HMAC_SHA256_128 = dns.name.from_text("hmac-sha256-128") -HMAC_SHA384 = dns.name.from_text("hmac-sha384") -HMAC_SHA384_192 = dns.name.from_text("hmac-sha384-192") -HMAC_SHA512 = dns.name.from_text("hmac-sha512") -HMAC_SHA512_256 = dns.name.from_text("hmac-sha512-256") -GSS_TSIG = dns.name.from_text("gss-tsig") - -default_algorithm = HMAC_SHA256 - -mac_sizes = { - HMAC_SHA1: 20, - HMAC_SHA224: 28, - HMAC_SHA256: 32, - HMAC_SHA256_128: 16, - HMAC_SHA384: 48, - HMAC_SHA384_192: 24, - HMAC_SHA512: 64, - HMAC_SHA512_256: 32, - HMAC_MD5: 16, - GSS_TSIG: 128, # This is what we assume to be the worst case! -} - - -class GSSTSig: - """ - GSS-TSIG TSIG implementation. This uses the GSS-API context established - in the TKEY message handshake to sign messages using GSS-API message - integrity codes, per the RFC. - - In order to avoid a direct GSSAPI dependency, the keyring holds a ref - to the GSSAPI object required, rather than the key itself. - """ - - def __init__(self, gssapi_context): - self.gssapi_context = gssapi_context - self.data = b"" - self.name = "gss-tsig" - - def update(self, data): - self.data += data - - def sign(self): - # defer to the GSSAPI function to sign - return self.gssapi_context.get_signature(self.data) - - def verify(self, expected): - try: - # defer to the GSSAPI function to verify - return self.gssapi_context.verify_signature(self.data, expected) - except Exception: - # note the usage of a bare exception - raise BadSignature - - -class GSSTSigAdapter: - def __init__(self, keyring): - self.keyring = keyring - - def __call__(self, message, keyname): - if keyname in self.keyring: - key = self.keyring[keyname] - if isinstance(key, Key) and key.algorithm == GSS_TSIG: - if message: - GSSTSigAdapter.parse_tkey_and_step(key, message, keyname) - return key - else: - return None - - @classmethod - def parse_tkey_and_step(cls, key, message, keyname): - # if the message is a TKEY type, absorb the key material - # into the context using step(); this is used to allow the - # client to complete the GSSAPI negotiation before attempting - # to verify the signed response to a TKEY message exchange - try: - rrset = message.find_rrset( - message.answer, keyname, dns.rdataclass.ANY, dns.rdatatype.TKEY - ) - if rrset: - token = rrset[0].key - gssapi_context = key.secret - return gssapi_context.step(token) - except KeyError: - pass - - -class HMACTSig: - """ - HMAC TSIG implementation. This uses the HMAC python module to handle the - sign/verify operations. - """ - - _hashes = { - HMAC_SHA1: hashlib.sha1, - HMAC_SHA224: hashlib.sha224, - HMAC_SHA256: hashlib.sha256, - HMAC_SHA256_128: (hashlib.sha256, 128), - HMAC_SHA384: hashlib.sha384, - HMAC_SHA384_192: (hashlib.sha384, 192), - HMAC_SHA512: hashlib.sha512, - HMAC_SHA512_256: (hashlib.sha512, 256), - HMAC_MD5: hashlib.md5, - } - - def __init__(self, key, algorithm): - try: - hashinfo = self._hashes[algorithm] - except KeyError: - raise NotImplementedError(f"TSIG algorithm {algorithm} is not supported") - - # create the HMAC context - if isinstance(hashinfo, tuple): - self.hmac_context = hmac.new(key, digestmod=hashinfo[0]) - self.size = hashinfo[1] - else: - self.hmac_context = hmac.new(key, digestmod=hashinfo) - self.size = None - self.name = self.hmac_context.name - if self.size: - self.name += f"-{self.size}" - - def update(self, data): - return self.hmac_context.update(data) - - def sign(self): - # defer to the HMAC digest() function for that digestmod - digest = self.hmac_context.digest() - if self.size: - digest = digest[: (self.size // 8)] - return digest - - def verify(self, expected): - # re-digest and compare the results - mac = self.sign() - if not hmac.compare_digest(mac, expected): - raise BadSignature - - -def _digest(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=None): - """Return a context containing the TSIG rdata for the input parameters - @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object - @raises ValueError: I{other_data} is too long - @raises NotImplementedError: I{algorithm} is not supported - """ - - first = not (ctx and multi) - if first: - ctx = get_context(key) - if request_mac: - ctx.update(struct.pack("!H", len(request_mac))) - ctx.update(request_mac) - ctx.update(struct.pack("!H", rdata.original_id)) - ctx.update(wire[2:]) - if first: - ctx.update(key.name.to_digestable()) - ctx.update(struct.pack("!H", dns.rdataclass.ANY)) - ctx.update(struct.pack("!I", 0)) - if time is None: - time = rdata.time_signed - upper_time = (time >> 32) & 0xFFFF - lower_time = time & 0xFFFFFFFF - time_encoded = struct.pack("!HIH", upper_time, lower_time, rdata.fudge) - other_len = len(rdata.other) - if other_len > 65535: - raise ValueError("TSIG Other Data is > 65535 bytes") - if first: - ctx.update(key.algorithm.to_digestable() + time_encoded) - ctx.update(struct.pack("!HH", rdata.error, other_len) + rdata.other) - else: - ctx.update(time_encoded) - return ctx - - -def _maybe_start_digest(key, mac, multi): - """If this is the first message in a multi-message sequence, - start a new context. - @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object - """ - if multi: - ctx = get_context(key) - ctx.update(struct.pack("!H", len(mac))) - ctx.update(mac) - return ctx - else: - return None - - -def sign(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=False): - """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata - for the input parameters, the HMAC MAC calculated by applying the - TSIG signature algorithm, and the TSIG digest context. - @rtype: (string, dns.tsig.HMACTSig or dns.tsig.GSSTSig object) - @raises ValueError: I{other_data} is too long - @raises NotImplementedError: I{algorithm} is not supported - """ - - ctx = _digest(wire, key, rdata, time, request_mac, ctx, multi) - mac = ctx.sign() - tsig = rdata.replace(time_signed=time, mac=mac) - - return (tsig, _maybe_start_digest(key, mac, multi)) - - -def validate( - wire, key, owner, rdata, now, request_mac, tsig_start, ctx=None, multi=False -): - """Validate the specified TSIG rdata against the other input parameters. - - @raises FormError: The TSIG is badly formed. - @raises BadTime: There is too much time skew between the client and the - server. - @raises BadSignature: The TSIG signature did not validate - @rtype: dns.tsig.HMACTSig or dns.tsig.GSSTSig object""" - - (adcount,) = struct.unpack("!H", wire[10:12]) - if adcount == 0: - raise dns.exception.FormError - adcount -= 1 - new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start] - if rdata.error != 0: - if rdata.error == dns.rcode.BADSIG: - raise PeerBadSignature - elif rdata.error == dns.rcode.BADKEY: - raise PeerBadKey - elif rdata.error == dns.rcode.BADTIME: - raise PeerBadTime - elif rdata.error == dns.rcode.BADTRUNC: - raise PeerBadTruncation - else: - raise PeerError("unknown TSIG error code %d" % rdata.error) - if abs(rdata.time_signed - now) > rdata.fudge: - raise BadTime - if key.name != owner: - raise BadKey - if key.algorithm != rdata.algorithm: - raise BadAlgorithm - ctx = _digest(new_wire, key, rdata, None, request_mac, ctx, multi) - ctx.verify(rdata.mac) - return _maybe_start_digest(key, rdata.mac, multi) - - -def get_context(key): - """Returns an HMAC context for the specified key. - - @rtype: HMAC context - @raises NotImplementedError: I{algorithm} is not supported - """ - - if key.algorithm == GSS_TSIG: - return GSSTSig(key.secret) - else: - return HMACTSig(key.secret, key.algorithm) - - -class Key: - def __init__(self, name, secret, algorithm=default_algorithm): - if isinstance(name, str): - name = dns.name.from_text(name) - self.name = name - if isinstance(secret, str): - secret = base64.decodebytes(secret.encode()) - self.secret = secret - if isinstance(algorithm, str): - algorithm = dns.name.from_text(algorithm) - self.algorithm = algorithm - - def __eq__(self, other): - return ( - isinstance(other, Key) - and self.name == other.name - and self.secret == other.secret - and self.algorithm == other.algorithm - ) - - def __repr__(self): - r = f" Dict[dns.name.Name, dns.tsig.Key]: - """Convert a dictionary containing (textual DNS name, base64 secret) - pairs into a binary keyring which has (dns.name.Name, bytes) pairs, or - a dictionary containing (textual DNS name, (algorithm, base64 secret)) - pairs into a binary keyring which has (dns.name.Name, dns.tsig.Key) pairs. - @rtype: dict""" - - keyring = {} - for name, value in textring.items(): - kname = dns.name.from_text(name) - if isinstance(value, str): - keyring[kname] = dns.tsig.Key(kname, value).secret - else: - (algorithm, secret) = value - keyring[kname] = dns.tsig.Key(kname, secret, algorithm) - return keyring - - -def to_text(keyring: Dict[dns.name.Name, Any]) -> Dict[str, Any]: - """Convert a dictionary containing (dns.name.Name, dns.tsig.Key) pairs - into a text keyring which has (textual DNS name, (textual algorithm, - base64 secret)) pairs, or a dictionary containing (dns.name.Name, bytes) - pairs into a text keyring which has (textual DNS name, base64 secret) pairs. - @rtype: dict""" - - textring = {} - - def b64encode(secret): - return base64.encodebytes(secret).decode().rstrip() - - for name, key in keyring.items(): - tname = name.to_text() - if isinstance(key, bytes): - textring[tname] = b64encode(key) - else: - if isinstance(key.secret, bytes): - text_secret = b64encode(key.secret) - else: - text_secret = str(key.secret) - - textring[tname] = (key.algorithm.to_text(), text_secret) - return textring diff --git a/.venv/lib/python3.12/site-packages/dns/ttl.py b/.venv/lib/python3.12/site-packages/dns/ttl.py deleted file mode 100644 index 264b033..0000000 --- a/.venv/lib/python3.12/site-packages/dns/ttl.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS TTL conversion.""" - -from typing import Union - -import dns.exception - -# Technically TTLs are supposed to be between 0 and 2**31 - 1, with values -# greater than that interpreted as 0, but we do not impose this policy here -# as values > 2**31 - 1 occur in real world data. -# -# We leave it to applications to impose tighter bounds if desired. -MAX_TTL = 2**32 - 1 - - -class BadTTL(dns.exception.SyntaxError): - """DNS TTL value is not well-formed.""" - - -def from_text(text: str) -> int: - """Convert the text form of a TTL to an integer. - - The BIND 8 units syntax for TTLs (e.g. '1w6d4h3m10s') is supported. - - *text*, a ``str``, the textual TTL. - - Raises ``dns.ttl.BadTTL`` if the TTL is not well-formed. - - Returns an ``int``. - """ - - if text.isdigit(): - total = int(text) - elif len(text) == 0: - raise BadTTL - else: - total = 0 - current = 0 - need_digit = True - for c in text: - if c.isdigit(): - current *= 10 - current += int(c) - need_digit = False - else: - if need_digit: - raise BadTTL - c = c.lower() - if c == "w": - total += current * 604800 - elif c == "d": - total += current * 86400 - elif c == "h": - total += current * 3600 - elif c == "m": - total += current * 60 - elif c == "s": - total += current - else: - raise BadTTL("unknown unit '%s'" % c) - current = 0 - need_digit = True - if not current == 0: - raise BadTTL("trailing integer") - if total < 0 or total > MAX_TTL: - raise BadTTL("TTL should be between 0 and 2**32 - 1 (inclusive)") - return total - - -def make(value: Union[int, str]) -> int: - if isinstance(value, int): - return value - elif isinstance(value, str): - return dns.ttl.from_text(value) - else: - raise ValueError("cannot convert value to TTL") diff --git a/.venv/lib/python3.12/site-packages/dns/update.py b/.venv/lib/python3.12/site-packages/dns/update.py deleted file mode 100644 index bf1157a..0000000 --- a/.venv/lib/python3.12/site-packages/dns/update.py +++ /dev/null @@ -1,386 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Dynamic Update Support""" - -from typing import Any, List, Optional, Union - -import dns.message -import dns.name -import dns.opcode -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.tsig - - -class UpdateSection(dns.enum.IntEnum): - """Update sections""" - - ZONE = 0 - PREREQ = 1 - UPDATE = 2 - ADDITIONAL = 3 - - @classmethod - def _maximum(cls): - return 3 - - -class UpdateMessage(dns.message.Message): # lgtm[py/missing-equals] - # ignore the mypy error here as we mean to use a different enum - _section_enum = UpdateSection # type: ignore - - def __init__( - self, - zone: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - keyring: Optional[Any] = None, - keyname: Optional[dns.name.Name] = None, - keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, - id: Optional[int] = None, - ): - """Initialize a new DNS Update object. - - See the documentation of the Message class for a complete - description of the keyring dictionary. - - *zone*, a ``dns.name.Name``, ``str``, or ``None``, the zone - which is being updated. ``None`` should only be used by dnspython's - message constructors, as a zone is required for the convenience - methods like ``add()``, ``replace()``, etc. - - *rdclass*, an ``int`` or ``str``, the class of the zone. - - The *keyring*, *keyname*, and *keyalgorithm* parameters are passed to - ``use_tsig()``; see its documentation for details. - """ - super().__init__(id=id) - self.flags |= dns.opcode.to_flags(dns.opcode.UPDATE) - if isinstance(zone, str): - zone = dns.name.from_text(zone) - self.origin = zone - rdclass = dns.rdataclass.RdataClass.make(rdclass) - self.zone_rdclass = rdclass - if self.origin: - self.find_rrset( - self.zone, - self.origin, - rdclass, - dns.rdatatype.SOA, - create=True, - force_unique=True, - ) - if keyring is not None: - self.use_tsig(keyring, keyname, algorithm=keyalgorithm) - - @property - def zone(self) -> List[dns.rrset.RRset]: - """The zone section.""" - return self.sections[0] - - @zone.setter - def zone(self, v): - self.sections[0] = v - - @property - def prerequisite(self) -> List[dns.rrset.RRset]: - """The prerequisite section.""" - return self.sections[1] - - @prerequisite.setter - def prerequisite(self, v): - self.sections[1] = v - - @property - def update(self) -> List[dns.rrset.RRset]: - """The update section.""" - return self.sections[2] - - @update.setter - def update(self, v): - self.sections[2] = v - - def _add_rr(self, name, ttl, rd, deleting=None, section=None): - """Add a single RR to the update section.""" - - if section is None: - section = self.update - covers = rd.covers() - rrset = self.find_rrset( - section, name, self.zone_rdclass, rd.rdtype, covers, deleting, True, True - ) - rrset.add(rd, ttl) - - def _add(self, replace, section, name, *args): - """Add records. - - *replace* is the replacement mode. If ``False``, - RRs are added to an existing RRset; if ``True``, the RRset - is replaced with the specified contents. The second - argument is the section to add to. The third argument - is always a name. The other arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if isinstance(args[0], dns.rdataset.Rdataset): - for rds in args: - if replace: - self.delete(name, rds.rdtype) - for rd in rds: - self._add_rr(name, rds.ttl, rd, section=section) - else: - args = list(args) - ttl = int(args.pop(0)) - if isinstance(args[0], dns.rdata.Rdata): - if replace: - self.delete(name, args[0].rdtype) - for rd in args: - self._add_rr(name, ttl, rd, section=section) - else: - rdtype = dns.rdatatype.RdataType.make(args.pop(0)) - if replace: - self.delete(name, rdtype) - for s in args: - rd = dns.rdata.from_text(self.zone_rdclass, rdtype, s, self.origin) - self._add_rr(name, ttl, rd, section=section) - - def add(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Add records. - - The first argument is always a name. The other - arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - """ - - self._add(False, self.update, name, *args) - - def delete(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Delete records. - - The first argument is always a name. The other - arguments can be: - - - *empty* - - - rdataset... - - - rdata... - - - rdtype, [string...] - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if len(args) == 0: - self.find_rrset( - self.update, - name, - dns.rdataclass.ANY, - dns.rdatatype.ANY, - dns.rdatatype.NONE, - dns.rdataclass.ANY, - True, - True, - ) - elif isinstance(args[0], dns.rdataset.Rdataset): - for rds in args: - for rd in rds: - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - else: - largs = list(args) - if isinstance(largs[0], dns.rdata.Rdata): - for rd in largs: - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - else: - rdtype = dns.rdatatype.RdataType.make(largs.pop(0)) - if len(largs) == 0: - self.find_rrset( - self.update, - name, - self.zone_rdclass, - rdtype, - dns.rdatatype.NONE, - dns.rdataclass.ANY, - True, - True, - ) - else: - for s in largs: - rd = dns.rdata.from_text( - self.zone_rdclass, - rdtype, - s, # type: ignore[arg-type] - self.origin, - ) - self._add_rr(name, 0, rd, dns.rdataclass.NONE) - - def replace(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Replace records. - - The first argument is always a name. The other - arguments can be: - - - rdataset... - - - ttl, rdata... - - - ttl, rdtype, string... - - Note that if you want to replace the entire node, you should do - a delete of the name followed by one or more calls to add. - """ - - self._add(True, self.update, name, *args) - - def present(self, name: Union[dns.name.Name, str], *args: Any) -> None: - """Require that an owner name (and optionally an rdata type, - or specific rdataset) exists as a prerequisite to the - execution of the update. - - The first argument is always a name. - The other arguments can be: - - - rdataset... - - - rdata... - - - rdtype, string... - """ - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if len(args) == 0: - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.ANY, - dns.rdatatype.ANY, - dns.rdatatype.NONE, - None, - True, - True, - ) - elif ( - isinstance(args[0], dns.rdataset.Rdataset) - or isinstance(args[0], dns.rdata.Rdata) - or len(args) > 1 - ): - if not isinstance(args[0], dns.rdataset.Rdataset): - # Add a 0 TTL - largs = list(args) - largs.insert(0, 0) # type: ignore[arg-type] - self._add(False, self.prerequisite, name, *largs) - else: - self._add(False, self.prerequisite, name, *args) - else: - rdtype = dns.rdatatype.RdataType.make(args[0]) - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.ANY, - rdtype, - dns.rdatatype.NONE, - None, - True, - True, - ) - - def absent( - self, - name: Union[dns.name.Name, str], - rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None, - ) -> None: - """Require that an owner name (and optionally an rdata type) does - not exist as a prerequisite to the execution of the update.""" - - if isinstance(name, str): - name = dns.name.from_text(name, None) - if rdtype is None: - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.NONE, - dns.rdatatype.ANY, - dns.rdatatype.NONE, - None, - True, - True, - ) - else: - rdtype = dns.rdatatype.RdataType.make(rdtype) - self.find_rrset( - self.prerequisite, - name, - dns.rdataclass.NONE, - rdtype, - dns.rdatatype.NONE, - None, - True, - True, - ) - - def _get_one_rr_per_rrset(self, value): - # Updates are always one_rr_per_rrset - return True - - def _parse_rr_header(self, section, name, rdclass, rdtype): - deleting = None - empty = False - if section == UpdateSection.ZONE: - if ( - dns.rdataclass.is_metaclass(rdclass) - or rdtype != dns.rdatatype.SOA - or self.zone - ): - raise dns.exception.FormError - else: - if not self.zone: - raise dns.exception.FormError - if rdclass in (dns.rdataclass.ANY, dns.rdataclass.NONE): - deleting = rdclass - rdclass = self.zone[0].rdclass - empty = ( - deleting == dns.rdataclass.ANY or section == UpdateSection.PREREQ - ) - return (rdclass, rdtype, deleting, empty) - - -# backwards compatibility -Update = UpdateMessage - -### BEGIN generated UpdateSection constants - -ZONE = UpdateSection.ZONE -PREREQ = UpdateSection.PREREQ -UPDATE = UpdateSection.UPDATE -ADDITIONAL = UpdateSection.ADDITIONAL - -### END generated UpdateSection constants diff --git a/.venv/lib/python3.12/site-packages/dns/version.py b/.venv/lib/python3.12/site-packages/dns/version.py deleted file mode 100644 index 251f258..0000000 --- a/.venv/lib/python3.12/site-packages/dns/version.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""dnspython release version information.""" - -#: MAJOR -MAJOR = 2 -#: MINOR -MINOR = 6 -#: MICRO -MICRO = 1 -#: RELEASELEVEL -RELEASELEVEL = 0x0F -#: SERIAL -SERIAL = 0 - -if RELEASELEVEL == 0x0F: # pragma: no cover lgtm[py/unreachable-statement] - #: version - version = "%d.%d.%d" % (MAJOR, MINOR, MICRO) # lgtm[py/unreachable-statement] -elif RELEASELEVEL == 0x00: # pragma: no cover lgtm[py/unreachable-statement] - version = "%d.%d.%ddev%d" % ( - MAJOR, - MINOR, - MICRO, - SERIAL, - ) # lgtm[py/unreachable-statement] -elif RELEASELEVEL == 0x0C: # pragma: no cover lgtm[py/unreachable-statement] - version = "%d.%d.%drc%d" % ( - MAJOR, - MINOR, - MICRO, - SERIAL, - ) # lgtm[py/unreachable-statement] -else: # pragma: no cover lgtm[py/unreachable-statement] - version = "%d.%d.%d%x%d" % ( - MAJOR, - MINOR, - MICRO, - RELEASELEVEL, - SERIAL, - ) # lgtm[py/unreachable-statement] - -#: hexversion -hexversion = MAJOR << 24 | MINOR << 16 | MICRO << 8 | RELEASELEVEL << 4 | SERIAL diff --git a/.venv/lib/python3.12/site-packages/dns/versioned.py b/.venv/lib/python3.12/site-packages/dns/versioned.py deleted file mode 100644 index fd78e67..0000000 --- a/.venv/lib/python3.12/site-packages/dns/versioned.py +++ /dev/null @@ -1,318 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""DNS Versioned Zones.""" - -import collections -import threading -from typing import Callable, Deque, Optional, Set, Union - -import dns.exception -import dns.immutable -import dns.name -import dns.node -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rdtypes.ANY.SOA -import dns.zone - - -class UseTransaction(dns.exception.DNSException): - """To alter a versioned zone, use a transaction.""" - - -# Backwards compatibility -Node = dns.zone.VersionedNode -ImmutableNode = dns.zone.ImmutableVersionedNode -Version = dns.zone.Version -WritableVersion = dns.zone.WritableVersion -ImmutableVersion = dns.zone.ImmutableVersion -Transaction = dns.zone.Transaction - - -class Zone(dns.zone.Zone): # lgtm[py/missing-equals] - __slots__ = [ - "_versions", - "_versions_lock", - "_write_txn", - "_write_waiters", - "_write_event", - "_pruning_policy", - "_readers", - ] - - node_factory = Node - - def __init__( - self, - origin: Optional[Union[dns.name.Name, str]], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - pruning_policy: Optional[Callable[["Zone", Version], Optional[bool]]] = None, - ): - """Initialize a versioned zone object. - - *origin* is the origin of the zone. It may be a ``dns.name.Name``, - a ``str``, or ``None``. If ``None``, then the zone's origin will - be set by the first ``$ORIGIN`` line in a zone file. - - *rdclass*, an ``int``, the zone's rdata class; the default is class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - - *pruning policy*, a function taking a ``Zone`` and a ``Version`` and returning - a ``bool``, or ``None``. Should the version be pruned? If ``None``, - the default policy, which retains one version is used. - """ - super().__init__(origin, rdclass, relativize) - self._versions: Deque[Version] = collections.deque() - self._version_lock = threading.Lock() - if pruning_policy is None: - self._pruning_policy = self._default_pruning_policy - else: - self._pruning_policy = pruning_policy - self._write_txn: Optional[Transaction] = None - self._write_event: Optional[threading.Event] = None - self._write_waiters: Deque[threading.Event] = collections.deque() - self._readers: Set[Transaction] = set() - self._commit_version_unlocked( - None, WritableVersion(self, replacement=True), origin - ) - - def reader( - self, id: Optional[int] = None, serial: Optional[int] = None - ) -> Transaction: # pylint: disable=arguments-differ - if id is not None and serial is not None: - raise ValueError("cannot specify both id and serial") - with self._version_lock: - if id is not None: - version = None - for v in reversed(self._versions): - if v.id == id: - version = v - break - if version is None: - raise KeyError("version not found") - elif serial is not None: - if self.relativize: - oname = dns.name.empty - else: - assert self.origin is not None - oname = self.origin - version = None - for v in reversed(self._versions): - n = v.nodes.get(oname) - if n: - rds = n.get_rdataset(self.rdclass, dns.rdatatype.SOA) - if rds and rds[0].serial == serial: - version = v - break - if version is None: - raise KeyError("serial not found") - else: - version = self._versions[-1] - txn = Transaction(self, False, version) - self._readers.add(txn) - return txn - - def writer(self, replacement: bool = False) -> Transaction: - event = None - while True: - with self._version_lock: - # Checking event == self._write_event ensures that either - # no one was waiting before we got lucky and found no write - # txn, or we were the one who was waiting and got woken up. - # This prevents "taking cuts" when creating a write txn. - if self._write_txn is None and event == self._write_event: - # Creating the transaction defers version setup - # (i.e. copying the nodes dictionary) until we - # give up the lock, so that we hold the lock as - # short a time as possible. This is why we call - # _setup_version() below. - self._write_txn = Transaction( - self, replacement, make_immutable=True - ) - # give up our exclusive right to make a Transaction - self._write_event = None - break - # Someone else is writing already, so we will have to - # wait, but we want to do the actual wait outside the - # lock. - event = threading.Event() - self._write_waiters.append(event) - # wait (note we gave up the lock!) - # - # We only wake one sleeper at a time, so it's important - # that no event waiter can exit this method (e.g. via - # cancellation) without returning a transaction or waking - # someone else up. - # - # This is not a problem with Threading module threads as - # they cannot be canceled, but could be an issue with trio - # tasks when we do the async version of writer(). - # I.e. we'd need to do something like: - # - # try: - # event.wait() - # except trio.Cancelled: - # with self._version_lock: - # self._maybe_wakeup_one_waiter_unlocked() - # raise - # - event.wait() - # Do the deferred version setup. - self._write_txn._setup_version() - return self._write_txn - - def _maybe_wakeup_one_waiter_unlocked(self): - if len(self._write_waiters) > 0: - self._write_event = self._write_waiters.popleft() - self._write_event.set() - - # pylint: disable=unused-argument - def _default_pruning_policy(self, zone, version): - return True - - # pylint: enable=unused-argument - - def _prune_versions_unlocked(self): - assert len(self._versions) > 0 - # Don't ever prune a version greater than or equal to one that - # a reader has open. This pins versions in memory while the - # reader is open, and importantly lets the reader open a txn on - # a successor version (e.g. if generating an IXFR). - # - # Note our definition of least_kept also ensures we do not try to - # delete the greatest version. - if len(self._readers) > 0: - least_kept = min(txn.version.id for txn in self._readers) - else: - least_kept = self._versions[-1].id - while self._versions[0].id < least_kept and self._pruning_policy( - self, self._versions[0] - ): - self._versions.popleft() - - def set_max_versions(self, max_versions: Optional[int]) -> None: - """Set a pruning policy that retains up to the specified number - of versions - """ - if max_versions is not None and max_versions < 1: - raise ValueError("max versions must be at least 1") - if max_versions is None: - - def policy(zone, _): # pylint: disable=unused-argument - return False - - else: - - def policy(zone, _): - return len(zone._versions) > max_versions - - self.set_pruning_policy(policy) - - def set_pruning_policy( - self, policy: Optional[Callable[["Zone", Version], Optional[bool]]] - ) -> None: - """Set the pruning policy for the zone. - - The *policy* function takes a `Version` and returns `True` if - the version should be pruned, and `False` otherwise. `None` - may also be specified for policy, in which case the default policy - is used. - - Pruning checking proceeds from the least version and the first - time the function returns `False`, the checking stops. I.e. the - retained versions are always a consecutive sequence. - """ - if policy is None: - policy = self._default_pruning_policy - with self._version_lock: - self._pruning_policy = policy - self._prune_versions_unlocked() - - def _end_read(self, txn): - with self._version_lock: - self._readers.remove(txn) - self._prune_versions_unlocked() - - def _end_write_unlocked(self, txn): - assert self._write_txn == txn - self._write_txn = None - self._maybe_wakeup_one_waiter_unlocked() - - def _end_write(self, txn): - with self._version_lock: - self._end_write_unlocked(txn) - - def _commit_version_unlocked(self, txn, version, origin): - self._versions.append(version) - self._prune_versions_unlocked() - self.nodes = version.nodes - if self.origin is None: - self.origin = origin - # txn can be None in __init__ when we make the empty version. - if txn is not None: - self._end_write_unlocked(txn) - - def _commit_version(self, txn, version, origin): - with self._version_lock: - self._commit_version_unlocked(txn, version, origin) - - def _get_next_version_id(self): - if len(self._versions) > 0: - id = self._versions[-1].id + 1 - else: - id = 1 - return id - - def find_node( - self, name: Union[dns.name.Name, str], create: bool = False - ) -> dns.node.Node: - if create: - raise UseTransaction - return super().find_node(name) - - def delete_node(self, name: Union[dns.name.Name, str]) -> None: - raise UseTransaction - - def find_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - if create: - raise UseTransaction - rdataset = super().find_rdataset(name, rdtype, covers) - return dns.rdataset.ImmutableRdataset(rdataset) - - def get_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - if create: - raise UseTransaction - rdataset = super().get_rdataset(name, rdtype, covers) - if rdataset is not None: - return dns.rdataset.ImmutableRdataset(rdataset) - else: - return None - - def delete_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> None: - raise UseTransaction - - def replace_rdataset( - self, name: Union[dns.name.Name, str], replacement: dns.rdataset.Rdataset - ) -> None: - raise UseTransaction diff --git a/.venv/lib/python3.12/site-packages/dns/win32util.py b/.venv/lib/python3.12/site-packages/dns/win32util.py deleted file mode 100644 index aaa7e93..0000000 --- a/.venv/lib/python3.12/site-packages/dns/win32util.py +++ /dev/null @@ -1,252 +0,0 @@ -import sys - -import dns._features - -if sys.platform == "win32": - from typing import Any - - import dns.name - - _prefer_wmi = True - - import winreg # pylint: disable=import-error - - # Keep pylint quiet on non-windows. - try: - WindowsError is None # pylint: disable=used-before-assignment - except KeyError: - WindowsError = Exception - - if dns._features.have("wmi"): - import threading - - import pythoncom # pylint: disable=import-error - import wmi # pylint: disable=import-error - - _have_wmi = True - else: - _have_wmi = False - - def _config_domain(domain): - # Sometimes DHCP servers add a '.' prefix to the default domain, and - # Windows just stores such values in the registry (see #687). - # Check for this and fix it. - if domain.startswith("."): - domain = domain[1:] - return dns.name.from_text(domain) - - class DnsInfo: - def __init__(self): - self.domain = None - self.nameservers = [] - self.search = [] - - if _have_wmi: - - class _WMIGetter(threading.Thread): - def __init__(self): - super().__init__() - self.info = DnsInfo() - - def run(self): - pythoncom.CoInitialize() - try: - system = wmi.WMI() - for interface in system.Win32_NetworkAdapterConfiguration(): - if interface.IPEnabled and interface.DNSServerSearchOrder: - self.info.nameservers = list(interface.DNSServerSearchOrder) - if interface.DNSDomain: - self.info.domain = _config_domain(interface.DNSDomain) - if interface.DNSDomainSuffixSearchOrder: - self.info.search = [ - _config_domain(x) - for x in interface.DNSDomainSuffixSearchOrder - ] - break - finally: - pythoncom.CoUninitialize() - - def get(self): - # We always run in a separate thread to avoid any issues with - # the COM threading model. - self.start() - self.join() - return self.info - - else: - - class _WMIGetter: # type: ignore - pass - - class _RegistryGetter: - def __init__(self): - self.info = DnsInfo() - - def _determine_split_char(self, entry): - # - # The windows registry irritatingly changes the list element - # delimiter in between ' ' and ',' (and vice-versa) in various - # versions of windows. - # - if entry.find(" ") >= 0: - split_char = " " - elif entry.find(",") >= 0: - split_char = "," - else: - # probably a singleton; treat as a space-separated list. - split_char = " " - return split_char - - def _config_nameservers(self, nameservers): - split_char = self._determine_split_char(nameservers) - ns_list = nameservers.split(split_char) - for ns in ns_list: - if ns not in self.info.nameservers: - self.info.nameservers.append(ns) - - def _config_search(self, search): - split_char = self._determine_split_char(search) - search_list = search.split(split_char) - for s in search_list: - s = _config_domain(s) - if s not in self.info.search: - self.info.search.append(s) - - def _config_fromkey(self, key, always_try_domain): - try: - servers, _ = winreg.QueryValueEx(key, "NameServer") - except WindowsError: - servers = None - if servers: - self._config_nameservers(servers) - if servers or always_try_domain: - try: - dom, _ = winreg.QueryValueEx(key, "Domain") - if dom: - self.info.domain = _config_domain(dom) - except WindowsError: - pass - else: - try: - servers, _ = winreg.QueryValueEx(key, "DhcpNameServer") - except WindowsError: - servers = None - if servers: - self._config_nameservers(servers) - try: - dom, _ = winreg.QueryValueEx(key, "DhcpDomain") - if dom: - self.info.domain = _config_domain(dom) - except WindowsError: - pass - try: - search, _ = winreg.QueryValueEx(key, "SearchList") - except WindowsError: - search = None - if search is None: - try: - search, _ = winreg.QueryValueEx(key, "DhcpSearchList") - except WindowsError: - search = None - if search: - self._config_search(search) - - def _is_nic_enabled(self, lm, guid): - # Look in the Windows Registry to determine whether the network - # interface corresponding to the given guid is enabled. - # - # (Code contributed by Paul Marks, thanks!) - # - try: - # This hard-coded location seems to be consistent, at least - # from Windows 2000 through Vista. - connection_key = winreg.OpenKey( - lm, - r"SYSTEM\CurrentControlSet\Control\Network" - r"\{4D36E972-E325-11CE-BFC1-08002BE10318}" - r"\%s\Connection" % guid, - ) - - try: - # The PnpInstanceID points to a key inside Enum - (pnp_id, ttype) = winreg.QueryValueEx( - connection_key, "PnpInstanceID" - ) - - if ttype != winreg.REG_SZ: - raise ValueError # pragma: no cover - - device_key = winreg.OpenKey( - lm, r"SYSTEM\CurrentControlSet\Enum\%s" % pnp_id - ) - - try: - # Get ConfigFlags for this device - (flags, ttype) = winreg.QueryValueEx(device_key, "ConfigFlags") - - if ttype != winreg.REG_DWORD: - raise ValueError # pragma: no cover - - # Based on experimentation, bit 0x1 indicates that the - # device is disabled. - # - # XXXRTH I suspect we really want to & with 0x03 so - # that CONFIGFLAGS_REMOVED devices are also ignored, - # but we're shifting to WMI as ConfigFlags is not - # supposed to be used. - return not flags & 0x1 - - finally: - device_key.Close() - finally: - connection_key.Close() - except Exception: # pragma: no cover - return False - - def get(self): - """Extract resolver configuration from the Windows registry.""" - - lm = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) - try: - tcp_params = winreg.OpenKey( - lm, r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" - ) - try: - self._config_fromkey(tcp_params, True) - finally: - tcp_params.Close() - interfaces = winreg.OpenKey( - lm, - r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces", - ) - try: - i = 0 - while True: - try: - guid = winreg.EnumKey(interfaces, i) - i += 1 - key = winreg.OpenKey(interfaces, guid) - try: - if not self._is_nic_enabled(lm, guid): - continue - self._config_fromkey(key, False) - finally: - key.Close() - except EnvironmentError: - break - finally: - interfaces.Close() - finally: - lm.Close() - return self.info - - _getter_class: Any - if _have_wmi and _prefer_wmi: - _getter_class = _WMIGetter - else: - _getter_class = _RegistryGetter - - def get_dns_info(): - """Extract resolver configuration.""" - getter = _getter_class() - return getter.get() diff --git a/.venv/lib/python3.12/site-packages/dns/wire.py b/.venv/lib/python3.12/site-packages/dns/wire.py deleted file mode 100644 index 9f9b157..0000000 --- a/.venv/lib/python3.12/site-packages/dns/wire.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -import contextlib -import struct -from typing import Iterator, Optional, Tuple - -import dns.exception -import dns.name - - -class Parser: - def __init__(self, wire: bytes, current: int = 0): - self.wire = wire - self.current = 0 - self.end = len(self.wire) - if current: - self.seek(current) - self.furthest = current - - def remaining(self) -> int: - return self.end - self.current - - def get_bytes(self, size: int) -> bytes: - assert size >= 0 - if size > self.remaining(): - raise dns.exception.FormError - output = self.wire[self.current : self.current + size] - self.current += size - self.furthest = max(self.furthest, self.current) - return output - - def get_counted_bytes(self, length_size: int = 1) -> bytes: - length = int.from_bytes(self.get_bytes(length_size), "big") - return self.get_bytes(length) - - def get_remaining(self) -> bytes: - return self.get_bytes(self.remaining()) - - def get_uint8(self) -> int: - return struct.unpack("!B", self.get_bytes(1))[0] - - def get_uint16(self) -> int: - return struct.unpack("!H", self.get_bytes(2))[0] - - def get_uint32(self) -> int: - return struct.unpack("!I", self.get_bytes(4))[0] - - def get_uint48(self) -> int: - return int.from_bytes(self.get_bytes(6), "big") - - def get_struct(self, format: str) -> Tuple: - return struct.unpack(format, self.get_bytes(struct.calcsize(format))) - - def get_name(self, origin: Optional["dns.name.Name"] = None) -> "dns.name.Name": - name = dns.name.from_wire_parser(self) - if origin: - name = name.relativize(origin) - return name - - def seek(self, where: int) -> None: - # Note that seeking to the end is OK! (If you try to read - # after such a seek, you'll get an exception as expected.) - if where < 0 or where > self.end: - raise dns.exception.FormError - self.current = where - - @contextlib.contextmanager - def restrict_to(self, size: int) -> Iterator: - assert size >= 0 - if size > self.remaining(): - raise dns.exception.FormError - saved_end = self.end - try: - self.end = self.current + size - yield - # We make this check here and not in the finally as we - # don't want to raise if we're already raising for some - # other reason. - if self.current != self.end: - raise dns.exception.FormError - finally: - self.end = saved_end - - @contextlib.contextmanager - def restore_furthest(self) -> Iterator: - try: - yield None - finally: - self.current = self.furthest diff --git a/.venv/lib/python3.12/site-packages/dns/xfr.py b/.venv/lib/python3.12/site-packages/dns/xfr.py deleted file mode 100644 index dd247d3..0000000 --- a/.venv/lib/python3.12/site-packages/dns/xfr.py +++ /dev/null @@ -1,343 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2017 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -from typing import Any, List, Optional, Tuple, Union - -import dns.exception -import dns.message -import dns.name -import dns.rcode -import dns.rdataset -import dns.rdatatype -import dns.serial -import dns.transaction -import dns.tsig -import dns.zone - - -class TransferError(dns.exception.DNSException): - """A zone transfer response got a non-zero rcode.""" - - def __init__(self, rcode): - message = "Zone transfer error: %s" % dns.rcode.to_text(rcode) - super().__init__(message) - self.rcode = rcode - - -class SerialWentBackwards(dns.exception.FormError): - """The current serial number is less than the serial we know.""" - - -class UseTCP(dns.exception.DNSException): - """This IXFR cannot be completed with UDP.""" - - -class Inbound: - """ - State machine for zone transfers. - """ - - def __init__( - self, - txn_manager: dns.transaction.TransactionManager, - rdtype: dns.rdatatype.RdataType = dns.rdatatype.AXFR, - serial: Optional[int] = None, - is_udp: bool = False, - ): - """Initialize an inbound zone transfer. - - *txn_manager* is a :py:class:`dns.transaction.TransactionManager`. - - *rdtype* can be `dns.rdatatype.AXFR` or `dns.rdatatype.IXFR` - - *serial* is the base serial number for IXFRs, and is required in - that case. - - *is_udp*, a ``bool`` indidicates if UDP is being used for this - XFR. - """ - self.txn_manager = txn_manager - self.txn: Optional[dns.transaction.Transaction] = None - self.rdtype = rdtype - if rdtype == dns.rdatatype.IXFR: - if serial is None: - raise ValueError("a starting serial must be supplied for IXFRs") - elif is_udp: - raise ValueError("is_udp specified for AXFR") - self.serial = serial - self.is_udp = is_udp - (_, _, self.origin) = txn_manager.origin_information() - self.soa_rdataset: Optional[dns.rdataset.Rdataset] = None - self.done = False - self.expecting_SOA = False - self.delete_mode = False - - def process_message(self, message: dns.message.Message) -> bool: - """Process one message in the transfer. - - The message should have the same relativization as was specified when - the `dns.xfr.Inbound` was created. The message should also have been - created with `one_rr_per_rrset=True` because order matters. - - Returns `True` if the transfer is complete, and `False` otherwise. - """ - if self.txn is None: - replacement = self.rdtype == dns.rdatatype.AXFR - self.txn = self.txn_manager.writer(replacement) - rcode = message.rcode() - if rcode != dns.rcode.NOERROR: - raise TransferError(rcode) - # - # We don't require a question section, but if it is present is - # should be correct. - # - if len(message.question) > 0: - if message.question[0].name != self.origin: - raise dns.exception.FormError("wrong question name") - if message.question[0].rdtype != self.rdtype: - raise dns.exception.FormError("wrong question rdatatype") - answer_index = 0 - if self.soa_rdataset is None: - # - # This is the first message. We're expecting an SOA at - # the origin. - # - if not message.answer or message.answer[0].name != self.origin: - raise dns.exception.FormError("No answer or RRset not for zone origin") - rrset = message.answer[0] - rdataset = rrset - if rdataset.rdtype != dns.rdatatype.SOA: - raise dns.exception.FormError("first RRset is not an SOA") - answer_index = 1 - self.soa_rdataset = rdataset.copy() - if self.rdtype == dns.rdatatype.IXFR: - if self.soa_rdataset[0].serial == self.serial: - # - # We're already up-to-date. - # - self.done = True - elif dns.serial.Serial(self.soa_rdataset[0].serial) < self.serial: - # It went backwards! - raise SerialWentBackwards - else: - if self.is_udp and len(message.answer[answer_index:]) == 0: - # - # There are no more records, so this is the - # "truncated" response. Say to use TCP - # - raise UseTCP - # - # Note we're expecting another SOA so we can detect - # if this IXFR response is an AXFR-style response. - # - self.expecting_SOA = True - # - # Process the answer section (other than the initial SOA in - # the first message). - # - for rrset in message.answer[answer_index:]: - name = rrset.name - rdataset = rrset - if self.done: - raise dns.exception.FormError("answers after final SOA") - assert self.txn is not None # for mypy - if rdataset.rdtype == dns.rdatatype.SOA and name == self.origin: - # - # Every time we see an origin SOA delete_mode inverts - # - if self.rdtype == dns.rdatatype.IXFR: - self.delete_mode = not self.delete_mode - # - # If this SOA Rdataset is equal to the first we saw - # then we're finished. If this is an IXFR we also - # check that we're seeing the record in the expected - # part of the response. - # - if rdataset == self.soa_rdataset and ( - self.rdtype == dns.rdatatype.AXFR - or (self.rdtype == dns.rdatatype.IXFR and self.delete_mode) - ): - # - # This is the final SOA - # - if self.expecting_SOA: - # We got an empty IXFR sequence! - raise dns.exception.FormError("empty IXFR sequence") - if ( - self.rdtype == dns.rdatatype.IXFR - and self.serial != rdataset[0].serial - ): - raise dns.exception.FormError("unexpected end of IXFR sequence") - self.txn.replace(name, rdataset) - self.txn.commit() - self.txn = None - self.done = True - else: - # - # This is not the final SOA - # - self.expecting_SOA = False - if self.rdtype == dns.rdatatype.IXFR: - if self.delete_mode: - # This is the start of an IXFR deletion set - if rdataset[0].serial != self.serial: - raise dns.exception.FormError( - "IXFR base serial mismatch" - ) - else: - # This is the start of an IXFR addition set - self.serial = rdataset[0].serial - self.txn.replace(name, rdataset) - else: - # We saw a non-final SOA for the origin in an AXFR. - raise dns.exception.FormError("unexpected origin SOA in AXFR") - continue - if self.expecting_SOA: - # - # We made an IXFR request and are expecting another - # SOA RR, but saw something else, so this must be an - # AXFR response. - # - self.rdtype = dns.rdatatype.AXFR - self.expecting_SOA = False - self.delete_mode = False - self.txn.rollback() - self.txn = self.txn_manager.writer(True) - # - # Note we are falling through into the code below - # so whatever rdataset this was gets written. - # - # Add or remove the data - if self.delete_mode: - self.txn.delete_exact(name, rdataset) - else: - self.txn.add(name, rdataset) - if self.is_udp and not self.done: - # - # This is a UDP IXFR and we didn't get to done, and we didn't - # get the proper "truncated" response - # - raise dns.exception.FormError("unexpected end of UDP IXFR") - return self.done - - # - # Inbounds are context managers. - # - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.txn: - self.txn.rollback() - return False - - -def make_query( - txn_manager: dns.transaction.TransactionManager, - serial: Optional[int] = 0, - use_edns: Optional[Union[int, bool]] = None, - ednsflags: Optional[int] = None, - payload: Optional[int] = None, - request_payload: Optional[int] = None, - options: Optional[List[dns.edns.Option]] = None, - keyring: Any = None, - keyname: Optional[dns.name.Name] = None, - keyalgorithm: Union[dns.name.Name, str] = dns.tsig.default_algorithm, -) -> Tuple[dns.message.QueryMessage, Optional[int]]: - """Make an AXFR or IXFR query. - - *txn_manager* is a ``dns.transaction.TransactionManager``, typically a - ``dns.zone.Zone``. - - *serial* is an ``int`` or ``None``. If 0, then IXFR will be - attempted using the most recent serial number from the - *txn_manager*; it is the caller's responsibility to ensure there - are no write transactions active that could invalidate the - retrieved serial. If a serial cannot be determined, AXFR will be - forced. Other integer values are the starting serial to use. - ``None`` forces an AXFR. - - Please see the documentation for :py:func:`dns.message.make_query` and - :py:func:`dns.message.Message.use_tsig` for details on the other parameters - to this function. - - Returns a `(query, serial)` tuple. - """ - (zone_origin, _, origin) = txn_manager.origin_information() - if zone_origin is None: - raise ValueError("no zone origin") - if serial is None: - rdtype = dns.rdatatype.AXFR - elif not isinstance(serial, int): - raise ValueError("serial is not an integer") - elif serial == 0: - with txn_manager.reader() as txn: - rdataset = txn.get(origin, "SOA") - if rdataset: - serial = rdataset[0].serial - rdtype = dns.rdatatype.IXFR - else: - serial = None - rdtype = dns.rdatatype.AXFR - elif serial > 0 and serial < 4294967296: - rdtype = dns.rdatatype.IXFR - else: - raise ValueError("serial out-of-range") - rdclass = txn_manager.get_class() - q = dns.message.make_query( - zone_origin, - rdtype, - rdclass, - use_edns, - False, - ednsflags, - payload, - request_payload, - options, - ) - if serial is not None: - rdata = dns.rdata.from_text(rdclass, "SOA", f". . {serial} 0 0 0 0") - rrset = q.find_rrset( - q.authority, zone_origin, rdclass, dns.rdatatype.SOA, create=True - ) - rrset.add(rdata, 0) - if keyring is not None: - q.use_tsig(keyring, keyname, algorithm=keyalgorithm) - return (q, serial) - - -def extract_serial_from_query(query: dns.message.Message) -> Optional[int]: - """Extract the SOA serial number from query if it is an IXFR and return - it, otherwise return None. - - *query* is a dns.message.QueryMessage that is an IXFR or AXFR request. - - Raises if the query is not an IXFR or AXFR, or if an IXFR doesn't have - an appropriate SOA RRset in the authority section. - """ - if not isinstance(query, dns.message.QueryMessage): - raise ValueError("query not a QueryMessage") - question = query.question[0] - if question.rdtype == dns.rdatatype.AXFR: - return None - elif question.rdtype != dns.rdatatype.IXFR: - raise ValueError("query is not an AXFR or IXFR") - soa = query.find_rrset( - query.authority, question.name, question.rdclass, dns.rdatatype.SOA - ) - return soa[0].serial diff --git a/.venv/lib/python3.12/site-packages/dns/zone.py b/.venv/lib/python3.12/site-packages/dns/zone.py deleted file mode 100644 index 844919e..0000000 --- a/.venv/lib/python3.12/site-packages/dns/zone.py +++ /dev/null @@ -1,1434 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Zones.""" - -import contextlib -import io -import os -import struct -from typing import ( - Any, - Callable, - Iterable, - Iterator, - List, - MutableMapping, - Optional, - Set, - Tuple, - Union, -) - -import dns.exception -import dns.grange -import dns.immutable -import dns.name -import dns.node -import dns.rdata -import dns.rdataclass -import dns.rdataset -import dns.rdatatype -import dns.rdtypes.ANY.SOA -import dns.rdtypes.ANY.ZONEMD -import dns.rrset -import dns.tokenizer -import dns.transaction -import dns.ttl -import dns.zonefile -from dns.zonetypes import DigestHashAlgorithm, DigestScheme, _digest_hashers - - -class BadZone(dns.exception.DNSException): - """The DNS zone is malformed.""" - - -class NoSOA(BadZone): - """The DNS zone has no SOA RR at its origin.""" - - -class NoNS(BadZone): - """The DNS zone has no NS RRset at its origin.""" - - -class UnknownOrigin(BadZone): - """The DNS zone's origin is unknown.""" - - -class UnsupportedDigestScheme(dns.exception.DNSException): - """The zone digest's scheme is unsupported.""" - - -class UnsupportedDigestHashAlgorithm(dns.exception.DNSException): - """The zone digest's origin is unsupported.""" - - -class NoDigest(dns.exception.DNSException): - """The DNS zone has no ZONEMD RRset at its origin.""" - - -class DigestVerificationFailure(dns.exception.DNSException): - """The ZONEMD digest failed to verify.""" - - -def _validate_name( - name: dns.name.Name, - origin: Optional[dns.name.Name], - relativize: bool, -) -> dns.name.Name: - # This name validation code is shared by Zone and Version - if origin is None: - # This should probably never happen as other code (e.g. - # _rr_line) will notice the lack of an origin before us, but - # we check just in case! - raise KeyError("no zone origin is defined") - if name.is_absolute(): - if not name.is_subdomain(origin): - raise KeyError("name parameter must be a subdomain of the zone origin") - if relativize: - name = name.relativize(origin) - else: - # We have a relative name. Make sure that the derelativized name is - # not too long. - try: - abs_name = name.derelativize(origin) - except dns.name.NameTooLong: - # We map dns.name.NameTooLong to KeyError to be consistent with - # the other exceptions above. - raise KeyError("relative name too long for zone") - if not relativize: - # We have a relative name in a non-relative zone, so use the - # derelativized name. - name = abs_name - return name - - -class Zone(dns.transaction.TransactionManager): - """A DNS zone. - - A ``Zone`` is a mapping from names to nodes. The zone object may be - treated like a Python dictionary, e.g. ``zone[name]`` will retrieve - the node associated with that name. The *name* may be a - ``dns.name.Name object``, or it may be a string. In either case, - if the name is relative it is treated as relative to the origin of - the zone. - """ - - node_factory: Callable[[], dns.node.Node] = dns.node.Node - map_factory: Callable[[], MutableMapping[dns.name.Name, dns.node.Node]] = dict - writable_version_factory: Optional[Callable[[], "WritableVersion"]] = None - immutable_version_factory: Optional[Callable[[], "ImmutableVersion"]] = None - - __slots__ = ["rdclass", "origin", "nodes", "relativize"] - - def __init__( - self, - origin: Optional[Union[dns.name.Name, str]], - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - ): - """Initialize a zone object. - - *origin* is the origin of the zone. It may be a ``dns.name.Name``, - a ``str``, or ``None``. If ``None``, then the zone's origin will - be set by the first ``$ORIGIN`` line in a zone file. - - *rdclass*, an ``int``, the zone's rdata class; the default is class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - """ - - if origin is not None: - if isinstance(origin, str): - origin = dns.name.from_text(origin) - elif not isinstance(origin, dns.name.Name): - raise ValueError("origin parameter must be convertible to a DNS name") - if not origin.is_absolute(): - raise ValueError("origin parameter must be an absolute name") - self.origin = origin - self.rdclass = rdclass - self.nodes: MutableMapping[dns.name.Name, dns.node.Node] = self.map_factory() - self.relativize = relativize - - def __eq__(self, other): - """Two zones are equal if they have the same origin, class, and - nodes. - - Returns a ``bool``. - """ - - if not isinstance(other, Zone): - return False - if ( - self.rdclass != other.rdclass - or self.origin != other.origin - or self.nodes != other.nodes - ): - return False - return True - - def __ne__(self, other): - """Are two zones not equal? - - Returns a ``bool``. - """ - - return not self.__eq__(other) - - def _validate_name(self, name: Union[dns.name.Name, str]) -> dns.name.Name: - # Note that any changes in this method should have corresponding changes - # made in the Version _validate_name() method. - if isinstance(name, str): - name = dns.name.from_text(name, None) - elif not isinstance(name, dns.name.Name): - raise KeyError("name parameter must be convertible to a DNS name") - return _validate_name(name, self.origin, self.relativize) - - def __getitem__(self, key): - key = self._validate_name(key) - return self.nodes[key] - - def __setitem__(self, key, value): - key = self._validate_name(key) - self.nodes[key] = value - - def __delitem__(self, key): - key = self._validate_name(key) - del self.nodes[key] - - def __iter__(self): - return self.nodes.__iter__() - - def keys(self): - return self.nodes.keys() - - def values(self): - return self.nodes.values() - - def items(self): - return self.nodes.items() - - def get(self, key): - key = self._validate_name(key) - return self.nodes.get(key) - - def __contains__(self, key): - key = self._validate_name(key) - return key in self.nodes - - def find_node( - self, name: Union[dns.name.Name, str], create: bool = False - ) -> dns.node.Node: - """Find a node in the zone, possibly creating it. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.node.Node``. - """ - - name = self._validate_name(name) - node = self.nodes.get(name) - if node is None: - if not create: - raise KeyError - node = self.node_factory() - self.nodes[name] = node - return node - - def get_node( - self, name: Union[dns.name.Name, str], create: bool = False - ) -> Optional[dns.node.Node]: - """Get a node in the zone, possibly creating it. - - This method is like ``find_node()``, except it returns None instead - of raising an exception if the node does not exist and creation - has not been requested. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Returns a ``dns.node.Node`` or ``None``. - """ - - try: - node = self.find_node(name, create) - except KeyError: - node = None - return node - - def delete_node(self, name: Union[dns.name.Name, str]) -> None: - """Delete the specified node if it exists. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - It is not an error if the node does not exist. - """ - - name = self._validate_name(name) - if name in self.nodes: - del self.nodes[name] - - def find_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - """Look for an rdataset with the specified name and type in the zone, - and return an rdataset encapsulating it. - - The rdataset returned is not a copy; changes to it will change - the zone. - - KeyError is raised if the name or type are not found. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str`` the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.rdataset.Rdataset``. - """ - - name = self._validate_name(name) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - node = self.find_node(name, create) - return node.find_rdataset(self.rdclass, rdtype, covers, create) - - def get_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - """Look for an rdataset with the specified name and type in the zone. - - This method is like ``find_rdataset()``, except it returns None instead - of raising an exception if the rdataset does not exist and creation - has not been requested. - - The rdataset returned is not a copy; changes to it will change - the zone. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.rdataset.Rdataset`` or ``None``. - """ - - try: - rdataset = self.find_rdataset(name, rdtype, covers, create) - except KeyError: - rdataset = None - return rdataset - - def delete_rdataset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> None: - """Delete the rdataset matching *rdtype* and *covers*, if it - exists at the node specified by *name*. - - It is not an error if the node does not exist, or if there is no matching - rdataset at the node. - - If the node has no rdatasets after the deletion, it will itself be deleted. - - *name*: the name of the node to find. The value may be a ``dns.name.Name`` or a - ``str``. If absolute, the name must be a subdomain of the zone's origin. If - ``zone.relativize`` is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str`` or ``None``, the covered - type. Usually this value is ``dns.rdatatype.NONE``, but if the rdtype is - ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, then the covers value will be - the rdata type the SIG/RRSIG covers. The library treats the SIG and RRSIG types - as if they were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). This - makes RRSIGs much easier to work with than if RRSIGs covering different rdata - types were aggregated into a single RRSIG rdataset. - """ - - name = self._validate_name(name) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - node = self.get_node(name) - if node is not None: - node.delete_rdataset(self.rdclass, rdtype, covers) - if len(node) == 0: - self.delete_node(name) - - def replace_rdataset( - self, name: Union[dns.name.Name, str], replacement: dns.rdataset.Rdataset - ) -> None: - """Replace an rdataset at name. - - It is not an error if there is no rdataset matching I{replacement}. - - Ownership of the *replacement* object is transferred to the zone; - in other words, this method does not store a copy of *replacement* - at the node, it stores *replacement* itself. - - If the node does not exist, it is created. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *replacement*, a ``dns.rdataset.Rdataset``, the replacement rdataset. - """ - - if replacement.rdclass != self.rdclass: - raise ValueError("replacement.rdclass != zone.rdclass") - node = self.find_node(name, True) - node.replace_rdataset(replacement) - - def find_rrset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> dns.rrset.RRset: - """Look for an rdataset with the specified name and type in the zone, - and return an RRset encapsulating it. - - This method is less efficient than the similar - ``find_rdataset()`` because it creates an RRset instead of - returning the matching rdataset. It may be more convenient - for some uses since it returns an object which binds the owner - name to the rdataset. - - This method may not be used to create new nodes or rdatasets; - use ``find_rdataset`` instead. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdatatype.RdataType`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdatatype.RdataType`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Raises ``KeyError`` if the name is not known and create was - not specified, or if the name was not a subdomain of the origin. - - Returns a ``dns.rrset.RRset`` or ``None``. - """ - - vname = self._validate_name(name) - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - rdataset = self.nodes[vname].find_rdataset(self.rdclass, rdtype, covers) - rrset = dns.rrset.RRset(vname, self.rdclass, rdtype, covers) - rrset.update(rdataset) - return rrset - - def get_rrset( - self, - name: Union[dns.name.Name, str], - rdtype: Union[dns.rdatatype.RdataType, str], - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> Optional[dns.rrset.RRset]: - """Look for an rdataset with the specified name and type in the zone, - and return an RRset encapsulating it. - - This method is less efficient than the similar ``get_rdataset()`` - because it creates an RRset instead of returning the matching - rdataset. It may be more convenient for some uses since it - returns an object which binds the owner name to the rdataset. - - This method may not be used to create new nodes or rdatasets; - use ``get_rdataset()`` instead. - - *name*: the name of the node to find. - The value may be a ``dns.name.Name`` or a ``str``. If absolute, the - name must be a subdomain of the zone's origin. If ``zone.relativize`` - is ``True``, then the name will be relativized. - - *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - - *create*, a ``bool``. If true, the node will be created if it does - not exist. - - Returns a ``dns.rrset.RRset`` or ``None``. - """ - - try: - rrset = self.find_rrset(name, rdtype, covers) - except KeyError: - rrset = None - return rrset - - def iterate_rdatasets( - self, - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.ANY, - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> Iterator[Tuple[dns.name.Name, dns.rdataset.Rdataset]]: - """Return a generator which yields (name, rdataset) tuples for - all rdatasets in the zone which have the specified *rdtype* - and *covers*. If *rdtype* is ``dns.rdatatype.ANY``, the default, - then all rdatasets will be matched. - - *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - """ - - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - for name, node in self.items(): - for rds in node: - if rdtype == dns.rdatatype.ANY or ( - rds.rdtype == rdtype and rds.covers == covers - ): - yield (name, rds) - - def iterate_rdatas( - self, - rdtype: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.ANY, - covers: Union[dns.rdatatype.RdataType, str] = dns.rdatatype.NONE, - ) -> Iterator[Tuple[dns.name.Name, int, dns.rdata.Rdata]]: - """Return a generator which yields (name, ttl, rdata) tuples for - all rdatas in the zone which have the specified *rdtype* - and *covers*. If *rdtype* is ``dns.rdatatype.ANY``, the default, - then all rdatas will be matched. - - *rdtype*, a ``dns.rdataset.Rdataset`` or ``str``, the rdata type desired. - - *covers*, a ``dns.rdataset.Rdataset`` or ``str``, the covered type. - Usually this value is ``dns.rdatatype.NONE``, but if the - rdtype is ``dns.rdatatype.SIG`` or ``dns.rdatatype.RRSIG``, - then the covers value will be the rdata type the SIG/RRSIG - covers. The library treats the SIG and RRSIG types as if they - were a family of types, e.g. RRSIG(A), RRSIG(NS), RRSIG(SOA). - This makes RRSIGs much easier to work with than if RRSIGs - covering different rdata types were aggregated into a single - RRSIG rdataset. - """ - - rdtype = dns.rdatatype.RdataType.make(rdtype) - covers = dns.rdatatype.RdataType.make(covers) - for name, node in self.items(): - for rds in node: - if rdtype == dns.rdatatype.ANY or ( - rds.rdtype == rdtype and rds.covers == covers - ): - for rdata in rds: - yield (name, rds.ttl, rdata) - - def to_file( - self, - f: Any, - sorted: bool = True, - relativize: bool = True, - nl: Optional[str] = None, - want_comments: bool = False, - want_origin: bool = False, - ) -> None: - """Write a zone to a file. - - *f*, a file or `str`. If *f* is a string, it is treated - as the name of a file to open. - - *sorted*, a ``bool``. If True, the default, then the file - will be written with the names sorted in DNSSEC order from - least to greatest. Otherwise the names will be written in - whatever order they happen to have in the zone's dictionary. - - *relativize*, a ``bool``. If True, the default, then domain - names in the output will be relativized to the zone's origin - if possible. - - *nl*, a ``str`` or None. The end of line string. If not - ``None``, the output will use the platform's native - end-of-line marker (i.e. LF on POSIX, CRLF on Windows). - - *want_comments*, a ``bool``. If ``True``, emit end-of-line comments - as part of writing the file. If ``False``, the default, do not - emit them. - - *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at - the start of the file. If ``False``, the default, do not emit - one. - """ - - if isinstance(f, str): - cm: contextlib.AbstractContextManager = open(f, "wb") - else: - cm = contextlib.nullcontext(f) - with cm as f: - # must be in this way, f.encoding may contain None, or even - # attribute may not be there - file_enc = getattr(f, "encoding", None) - if file_enc is None: - file_enc = "utf-8" - - if nl is None: - # binary mode, '\n' is not enough - nl_b = os.linesep.encode(file_enc) - nl = "\n" - elif isinstance(nl, str): - nl_b = nl.encode(file_enc) - else: - nl_b = nl - nl = nl.decode() - - if want_origin: - assert self.origin is not None - l = "$ORIGIN " + self.origin.to_text() - l_b = l.encode(file_enc) - try: - f.write(l_b) - f.write(nl_b) - except TypeError: # textual mode - f.write(l) - f.write(nl) - - if sorted: - names = list(self.keys()) - names.sort() - else: - names = self.keys() - for n in names: - l = self[n].to_text( - n, - origin=self.origin, - relativize=relativize, - want_comments=want_comments, - ) - l_b = l.encode(file_enc) - - try: - f.write(l_b) - f.write(nl_b) - except TypeError: # textual mode - f.write(l) - f.write(nl) - - def to_text( - self, - sorted: bool = True, - relativize: bool = True, - nl: Optional[str] = None, - want_comments: bool = False, - want_origin: bool = False, - ) -> str: - """Return a zone's text as though it were written to a file. - - *sorted*, a ``bool``. If True, the default, then the file - will be written with the names sorted in DNSSEC order from - least to greatest. Otherwise the names will be written in - whatever order they happen to have in the zone's dictionary. - - *relativize*, a ``bool``. If True, the default, then domain - names in the output will be relativized to the zone's origin - if possible. - - *nl*, a ``str`` or None. The end of line string. If not - ``None``, the output will use the platform's native - end-of-line marker (i.e. LF on POSIX, CRLF on Windows). - - *want_comments*, a ``bool``. If ``True``, emit end-of-line comments - as part of writing the file. If ``False``, the default, do not - emit them. - - *want_origin*, a ``bool``. If ``True``, emit a $ORIGIN line at - the start of the output. If ``False``, the default, do not emit - one. - - Returns a ``str``. - """ - temp_buffer = io.StringIO() - self.to_file(temp_buffer, sorted, relativize, nl, want_comments, want_origin) - return_value = temp_buffer.getvalue() - temp_buffer.close() - return return_value - - def check_origin(self) -> None: - """Do some simple checking of the zone's origin. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - """ - if self.relativize: - name = dns.name.empty - else: - assert self.origin is not None - name = self.origin - if self.get_rdataset(name, dns.rdatatype.SOA) is None: - raise NoSOA - if self.get_rdataset(name, dns.rdatatype.NS) is None: - raise NoNS - - def get_soa( - self, txn: Optional[dns.transaction.Transaction] = None - ) -> dns.rdtypes.ANY.SOA.SOA: - """Get the zone SOA rdata. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Returns a ``dns.rdtypes.ANY.SOA.SOA`` Rdata. - """ - if self.relativize: - origin_name = dns.name.empty - else: - if self.origin is None: - # get_soa() has been called very early, and there must not be - # an SOA if there is no origin. - raise NoSOA - origin_name = self.origin - soa: Optional[dns.rdataset.Rdataset] - if txn: - soa = txn.get(origin_name, dns.rdatatype.SOA) - else: - soa = self.get_rdataset(origin_name, dns.rdatatype.SOA) - if soa is None: - raise NoSOA - return soa[0] - - def _compute_digest( - self, - hash_algorithm: DigestHashAlgorithm, - scheme: DigestScheme = DigestScheme.SIMPLE, - ) -> bytes: - hashinfo = _digest_hashers.get(hash_algorithm) - if not hashinfo: - raise UnsupportedDigestHashAlgorithm - if scheme != DigestScheme.SIMPLE: - raise UnsupportedDigestScheme - - if self.relativize: - origin_name = dns.name.empty - else: - assert self.origin is not None - origin_name = self.origin - hasher = hashinfo() - for name, node in sorted(self.items()): - rrnamebuf = name.to_digestable(self.origin) - for rdataset in sorted(node, key=lambda rds: (rds.rdtype, rds.covers)): - if name == origin_name and dns.rdatatype.ZONEMD in ( - rdataset.rdtype, - rdataset.covers, - ): - continue - rrfixed = struct.pack( - "!HHI", rdataset.rdtype, rdataset.rdclass, rdataset.ttl - ) - rdatas = [rdata.to_digestable(self.origin) for rdata in rdataset] - for rdata in sorted(rdatas): - rrlen = struct.pack("!H", len(rdata)) - hasher.update(rrnamebuf + rrfixed + rrlen + rdata) - return hasher.digest() - - def compute_digest( - self, - hash_algorithm: DigestHashAlgorithm, - scheme: DigestScheme = DigestScheme.SIMPLE, - ) -> dns.rdtypes.ANY.ZONEMD.ZONEMD: - serial = self.get_soa().serial - digest = self._compute_digest(hash_algorithm, scheme) - return dns.rdtypes.ANY.ZONEMD.ZONEMD( - self.rdclass, dns.rdatatype.ZONEMD, serial, scheme, hash_algorithm, digest - ) - - def verify_digest( - self, zonemd: Optional[dns.rdtypes.ANY.ZONEMD.ZONEMD] = None - ) -> None: - digests: Union[dns.rdataset.Rdataset, List[dns.rdtypes.ANY.ZONEMD.ZONEMD]] - if zonemd: - digests = [zonemd] - else: - assert self.origin is not None - rds = self.get_rdataset(self.origin, dns.rdatatype.ZONEMD) - if rds is None: - raise NoDigest - digests = rds - for digest in digests: - try: - computed = self._compute_digest(digest.hash_algorithm, digest.scheme) - if computed == digest.digest: - return - except Exception: - pass - raise DigestVerificationFailure - - # TransactionManager methods - - def reader(self) -> "Transaction": - return Transaction(self, False, Version(self, 1, self.nodes, self.origin)) - - def writer(self, replacement: bool = False) -> "Transaction": - txn = Transaction(self, replacement) - txn._setup_version() - return txn - - def origin_information( - self, - ) -> Tuple[Optional[dns.name.Name], bool, Optional[dns.name.Name]]: - effective: Optional[dns.name.Name] - if self.relativize: - effective = dns.name.empty - else: - effective = self.origin - return (self.origin, self.relativize, effective) - - def get_class(self): - return self.rdclass - - # Transaction methods - - def _end_read(self, txn): - pass - - def _end_write(self, txn): - pass - - def _commit_version(self, _, version, origin): - self.nodes = version.nodes - if self.origin is None: - self.origin = origin - - def _get_next_version_id(self): - # Versions are ephemeral and all have id 1 - return 1 - - -# These classes used to be in dns.versioned, but have moved here so we can use -# the copy-on-write transaction mechanism for both kinds of zones. In a -# regular zone, the version only exists during the transaction, and the nodes -# are regular dns.node.Nodes. - -# A node with a version id. - - -class VersionedNode(dns.node.Node): # lgtm[py/missing-equals] - __slots__ = ["id"] - - def __init__(self): - super().__init__() - # A proper id will get set by the Version - self.id = 0 - - -@dns.immutable.immutable -class ImmutableVersionedNode(VersionedNode): - def __init__(self, node): - super().__init__() - self.id = node.id - self.rdatasets = tuple( - [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] - ) - - def find_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> dns.rdataset.Rdataset: - if create: - raise TypeError("immutable") - return super().find_rdataset(rdclass, rdtype, covers, False) - - def get_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - create: bool = False, - ) -> Optional[dns.rdataset.Rdataset]: - if create: - raise TypeError("immutable") - return super().get_rdataset(rdclass, rdtype, covers, False) - - def delete_rdataset( - self, - rdclass: dns.rdataclass.RdataClass, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType = dns.rdatatype.NONE, - ) -> None: - raise TypeError("immutable") - - def replace_rdataset(self, replacement: dns.rdataset.Rdataset) -> None: - raise TypeError("immutable") - - def is_immutable(self) -> bool: - return True - - -class Version: - def __init__( - self, - zone: Zone, - id: int, - nodes: Optional[MutableMapping[dns.name.Name, dns.node.Node]] = None, - origin: Optional[dns.name.Name] = None, - ): - self.zone = zone - self.id = id - if nodes is not None: - self.nodes = nodes - else: - self.nodes = zone.map_factory() - self.origin = origin - - def _validate_name(self, name: dns.name.Name) -> dns.name.Name: - return _validate_name(name, self.origin, self.zone.relativize) - - def get_node(self, name: dns.name.Name) -> Optional[dns.node.Node]: - name = self._validate_name(name) - return self.nodes.get(name) - - def get_rdataset( - self, - name: dns.name.Name, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - ) -> Optional[dns.rdataset.Rdataset]: - node = self.get_node(name) - if node is None: - return None - return node.get_rdataset(self.zone.rdclass, rdtype, covers) - - def keys(self): - return self.nodes.keys() - - def items(self): - return self.nodes.items() - - -class WritableVersion(Version): - def __init__(self, zone: Zone, replacement: bool = False): - # The zone._versions_lock must be held by our caller in a versioned - # zone. - id = zone._get_next_version_id() - super().__init__(zone, id) - if not replacement: - # We copy the map, because that gives us a simple and thread-safe - # way of doing versions, and we have a garbage collector to help - # us. We only make new node objects if we actually change the - # node. - self.nodes.update(zone.nodes) - # We have to copy the zone origin as it may be None in the first - # version, and we don't want to mutate the zone until we commit. - self.origin = zone.origin - self.changed: Set[dns.name.Name] = set() - - def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node: - name = self._validate_name(name) - node = self.nodes.get(name) - if node is None or name not in self.changed: - new_node = self.zone.node_factory() - if hasattr(new_node, "id"): - # We keep doing this for backwards compatibility, as earlier - # code used new_node.id != self.id for the "do we need to CoW?" - # test. Now we use the changed set as this works with both - # regular zones and versioned zones. - # - # We ignore the mypy error as this is safe but it doesn't see it. - new_node.id = self.id # type: ignore - if node is not None: - # moo! copy on write! - new_node.rdatasets.extend(node.rdatasets) - self.nodes[name] = new_node - self.changed.add(name) - return new_node - else: - return node - - def delete_node(self, name: dns.name.Name) -> None: - name = self._validate_name(name) - if name in self.nodes: - del self.nodes[name] - self.changed.add(name) - - def put_rdataset( - self, name: dns.name.Name, rdataset: dns.rdataset.Rdataset - ) -> None: - node = self._maybe_cow(name) - node.replace_rdataset(rdataset) - - def delete_rdataset( - self, - name: dns.name.Name, - rdtype: dns.rdatatype.RdataType, - covers: dns.rdatatype.RdataType, - ) -> None: - node = self._maybe_cow(name) - node.delete_rdataset(self.zone.rdclass, rdtype, covers) - if len(node) == 0: - del self.nodes[name] - - -@dns.immutable.immutable -class ImmutableVersion(Version): - def __init__(self, version: WritableVersion): - # We tell super() that it's a replacement as we don't want it - # to copy the nodes, as we're about to do that with an - # immutable Dict. - super().__init__(version.zone, True) - # set the right id! - self.id = version.id - # keep the origin - self.origin = version.origin - # Make changed nodes immutable - for name in version.changed: - node = version.nodes.get(name) - # it might not exist if we deleted it in the version - if node: - version.nodes[name] = ImmutableVersionedNode(node) - # We're changing the type of the nodes dictionary here on purpose, so - # we ignore the mypy error. - self.nodes = dns.immutable.Dict( - version.nodes, True, self.zone.map_factory - ) # type: ignore - - -class Transaction(dns.transaction.Transaction): - def __init__(self, zone, replacement, version=None, make_immutable=False): - read_only = version is not None - super().__init__(zone, replacement, read_only) - self.version = version - self.make_immutable = make_immutable - - @property - def zone(self): - return self.manager - - def _setup_version(self): - assert self.version is None - factory = self.manager.writable_version_factory - if factory is None: - factory = WritableVersion - self.version = factory(self.zone, self.replacement) - - def _get_rdataset(self, name, rdtype, covers): - return self.version.get_rdataset(name, rdtype, covers) - - def _put_rdataset(self, name, rdataset): - assert not self.read_only - self.version.put_rdataset(name, rdataset) - - def _delete_name(self, name): - assert not self.read_only - self.version.delete_node(name) - - def _delete_rdataset(self, name, rdtype, covers): - assert not self.read_only - self.version.delete_rdataset(name, rdtype, covers) - - def _name_exists(self, name): - return self.version.get_node(name) is not None - - def _changed(self): - if self.read_only: - return False - else: - return len(self.version.changed) > 0 - - def _end_transaction(self, commit): - if self.read_only: - self.zone._end_read(self) - elif commit and len(self.version.changed) > 0: - if self.make_immutable: - factory = self.manager.immutable_version_factory - if factory is None: - factory = ImmutableVersion - version = factory(self.version) - else: - version = self.version - self.zone._commit_version(self, version, self.version.origin) - else: - # rollback - self.zone._end_write(self) - - def _set_origin(self, origin): - if self.version.origin is None: - self.version.origin = origin - - def _iterate_rdatasets(self): - for name, node in self.version.items(): - for rdataset in node: - yield (name, rdataset) - - def _iterate_names(self): - return self.version.keys() - - def _get_node(self, name): - return self.version.get_node(name) - - def _origin_information(self): - (absolute, relativize, effective) = self.manager.origin_information() - if absolute is None and self.version.origin is not None: - # No origin has been committed yet, but we've learned one as part of - # this txn. Use it. - absolute = self.version.origin - if relativize: - effective = dns.name.empty - else: - effective = absolute - return (absolute, relativize, effective) - - -def _from_text( - text: Any, - origin: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - zone_factory: Any = Zone, - filename: Optional[str] = None, - allow_include: bool = False, - check_origin: bool = True, - idna_codec: Optional[dns.name.IDNACodec] = None, - allow_directives: Union[bool, Iterable[str]] = True, -) -> Zone: - # See the comments for the public APIs from_text() and from_file() for - # details. - - # 'text' can also be a file, but we don't publish that fact - # since it's an implementation detail. The official file - # interface is from_file(). - - if filename is None: - filename = "" - zone = zone_factory(origin, rdclass, relativize=relativize) - with zone.writer(True) as txn: - tok = dns.tokenizer.Tokenizer(text, filename, idna_codec=idna_codec) - reader = dns.zonefile.Reader( - tok, - rdclass, - txn, - allow_include=allow_include, - allow_directives=allow_directives, - ) - try: - reader.read() - except dns.zonefile.UnknownOrigin: - # for backwards compatibility - raise dns.zone.UnknownOrigin - # Now that we're done reading, do some basic checking of the zone. - if check_origin: - zone.check_origin() - return zone - - -def from_text( - text: str, - origin: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - zone_factory: Any = Zone, - filename: Optional[str] = None, - allow_include: bool = False, - check_origin: bool = True, - idna_codec: Optional[dns.name.IDNACodec] = None, - allow_directives: Union[bool, Iterable[str]] = True, -) -> Zone: - """Build a zone object from a zone file format string. - - *text*, a ``str``, the zone file format input. - - *origin*, a ``dns.name.Name``, a ``str``, or ``None``. The origin - of the zone; if not specified, the first ``$ORIGIN`` statement in the - zone file will determine the origin of the zone. - - *rdclass*, a ``dns.rdataclass.RdataClass``, the zone's rdata class; the default is - class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - - *zone_factory*, the zone factory to use or ``None``. If ``None``, then - ``dns.zone.Zone`` will be used. The value may be any class or callable - that returns a subclass of ``dns.zone.Zone``. - - *filename*, a ``str`` or ``None``, the filename to emit when - describing where an error occurred; the default is ``''``. - - *allow_include*, a ``bool``. If ``True``, the default, then ``$INCLUDE`` - directives are permitted. If ``False``, then encoutering a ``$INCLUDE`` - will raise a ``SyntaxError`` exception. - - *check_origin*, a ``bool``. If ``True``, the default, then sanity - checks of the origin node will be made by calling the zone's - ``check_origin()`` method. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *allow_directives*, a ``bool`` or an iterable of `str`. If ``True``, the default, - then directives are permitted, and the *allow_include* parameter controls whether - ``$INCLUDE`` is permitted. If ``False`` or an empty iterable, then no directive - processing is done and any directive-like text will be treated as a regular owner - name. If a non-empty iterable, then only the listed directives (including the - ``$``) are allowed. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - - Returns a subclass of ``dns.zone.Zone``. - """ - return _from_text( - text, - origin, - rdclass, - relativize, - zone_factory, - filename, - allow_include, - check_origin, - idna_codec, - allow_directives, - ) - - -def from_file( - f: Any, - origin: Optional[Union[dns.name.Name, str]] = None, - rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, - relativize: bool = True, - zone_factory: Any = Zone, - filename: Optional[str] = None, - allow_include: bool = True, - check_origin: bool = True, - idna_codec: Optional[dns.name.IDNACodec] = None, - allow_directives: Union[bool, Iterable[str]] = True, -) -> Zone: - """Read a zone file and build a zone object. - - *f*, a file or ``str``. If *f* is a string, it is treated - as the name of a file to open. - - *origin*, a ``dns.name.Name``, a ``str``, or ``None``. The origin - of the zone; if not specified, the first ``$ORIGIN`` statement in the - zone file will determine the origin of the zone. - - *rdclass*, an ``int``, the zone's rdata class; the default is class IN. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - - *zone_factory*, the zone factory to use or ``None``. If ``None``, then - ``dns.zone.Zone`` will be used. The value may be any class or callable - that returns a subclass of ``dns.zone.Zone``. - - *filename*, a ``str`` or ``None``, the filename to emit when - describing where an error occurred; the default is ``''``. - - *allow_include*, a ``bool``. If ``True``, the default, then ``$INCLUDE`` - directives are permitted. If ``False``, then encoutering a ``$INCLUDE`` - will raise a ``SyntaxError`` exception. - - *check_origin*, a ``bool``. If ``True``, the default, then sanity - checks of the origin node will be made by calling the zone's - ``check_origin()`` method. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. - - *allow_directives*, a ``bool`` or an iterable of `str`. If ``True``, the default, - then directives are permitted, and the *allow_include* parameter controls whether - ``$INCLUDE`` is permitted. If ``False`` or an empty iterable, then no directive - processing is done and any directive-like text will be treated as a regular owner - name. If a non-empty iterable, then only the listed directives (including the - ``$``) are allowed. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - - Returns a subclass of ``dns.zone.Zone``. - """ - - if isinstance(f, str): - if filename is None: - filename = f - cm: contextlib.AbstractContextManager = open(f) - else: - cm = contextlib.nullcontext(f) - with cm as f: - return _from_text( - f, - origin, - rdclass, - relativize, - zone_factory, - filename, - allow_include, - check_origin, - idna_codec, - allow_directives, - ) - assert False # make mypy happy lgtm[py/unreachable-statement] - - -def from_xfr( - xfr: Any, - zone_factory: Any = Zone, - relativize: bool = True, - check_origin: bool = True, -) -> Zone: - """Convert the output of a zone transfer generator into a zone object. - - *xfr*, a generator of ``dns.message.Message`` objects, typically - ``dns.query.xfr()``. - - *relativize*, a ``bool``, determine's whether domain names are - relativized to the zone's origin. The default is ``True``. - It is essential that the relativize setting matches the one specified - to the generator. - - *check_origin*, a ``bool``. If ``True``, the default, then sanity - checks of the origin node will be made by calling the zone's - ``check_origin()`` method. - - Raises ``dns.zone.NoSOA`` if there is no SOA RRset. - - Raises ``dns.zone.NoNS`` if there is no NS RRset. - - Raises ``KeyError`` if there is no origin node. - - Raises ``ValueError`` if no messages are yielded by the generator. - - Returns a subclass of ``dns.zone.Zone``. - """ - - z = None - for r in xfr: - if z is None: - if relativize: - origin = r.origin - else: - origin = r.answer[0].name - rdclass = r.answer[0].rdclass - z = zone_factory(origin, rdclass, relativize=relativize) - for rrset in r.answer: - znode = z.nodes.get(rrset.name) - if not znode: - znode = z.node_factory() - z.nodes[rrset.name] = znode - zrds = znode.find_rdataset(rrset.rdclass, rrset.rdtype, rrset.covers, True) - zrds.update_ttl(rrset.ttl) - for rd in rrset: - zrds.add(rd) - if z is None: - raise ValueError("empty transfer") - if check_origin: - z.check_origin() - return z diff --git a/.venv/lib/python3.12/site-packages/dns/zonefile.py b/.venv/lib/python3.12/site-packages/dns/zonefile.py deleted file mode 100644 index af064e7..0000000 --- a/.venv/lib/python3.12/site-packages/dns/zonefile.py +++ /dev/null @@ -1,746 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -# Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation for any purpose with or without fee is hereby granted, -# provided that the above copyright notice and this permission notice -# appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -"""DNS Zones.""" - -import re -import sys -from typing import Any, Iterable, List, Optional, Set, Tuple, Union - -import dns.exception -import dns.grange -import dns.name -import dns.node -import dns.rdata -import dns.rdataclass -import dns.rdatatype -import dns.rdtypes.ANY.SOA -import dns.rrset -import dns.tokenizer -import dns.transaction -import dns.ttl - - -class UnknownOrigin(dns.exception.DNSException): - """Unknown origin""" - - -class CNAMEAndOtherData(dns.exception.DNSException): - """A node has a CNAME and other data""" - - -def _check_cname_and_other_data(txn, name, rdataset): - rdataset_kind = dns.node.NodeKind.classify_rdataset(rdataset) - node = txn.get_node(name) - if node is None: - # empty nodes are neutral. - return - node_kind = node.classify() - if ( - node_kind == dns.node.NodeKind.CNAME - and rdataset_kind == dns.node.NodeKind.REGULAR - ): - raise CNAMEAndOtherData("rdataset type is not compatible with a CNAME node") - elif ( - node_kind == dns.node.NodeKind.REGULAR - and rdataset_kind == dns.node.NodeKind.CNAME - ): - raise CNAMEAndOtherData( - "CNAME rdataset is not compatible with a regular data node" - ) - # Otherwise at least one of the node and the rdataset is neutral, so - # adding the rdataset is ok - - -SavedStateType = Tuple[ - dns.tokenizer.Tokenizer, - Optional[dns.name.Name], # current_origin - Optional[dns.name.Name], # last_name - Optional[Any], # current_file - int, # last_ttl - bool, # last_ttl_known - int, # default_ttl - bool, -] # default_ttl_known - - -def _upper_dollarize(s): - s = s.upper() - if not s.startswith("$"): - s = "$" + s - return s - - -class Reader: - """Read a DNS zone file into a transaction.""" - - def __init__( - self, - tok: dns.tokenizer.Tokenizer, - rdclass: dns.rdataclass.RdataClass, - txn: dns.transaction.Transaction, - allow_include: bool = False, - allow_directives: Union[bool, Iterable[str]] = True, - force_name: Optional[dns.name.Name] = None, - force_ttl: Optional[int] = None, - force_rdclass: Optional[dns.rdataclass.RdataClass] = None, - force_rdtype: Optional[dns.rdatatype.RdataType] = None, - default_ttl: Optional[int] = None, - ): - self.tok = tok - (self.zone_origin, self.relativize, _) = txn.manager.origin_information() - self.current_origin = self.zone_origin - self.last_ttl = 0 - self.last_ttl_known = False - if force_ttl is not None: - default_ttl = force_ttl - if default_ttl is None: - self.default_ttl = 0 - self.default_ttl_known = False - else: - self.default_ttl = default_ttl - self.default_ttl_known = True - self.last_name = self.current_origin - self.zone_rdclass = rdclass - self.txn = txn - self.saved_state: List[SavedStateType] = [] - self.current_file: Optional[Any] = None - self.allowed_directives: Set[str] - if allow_directives is True: - self.allowed_directives = {"$GENERATE", "$ORIGIN", "$TTL"} - if allow_include: - self.allowed_directives.add("$INCLUDE") - elif allow_directives is False: - # allow_include was ignored in earlier releases if allow_directives was - # False, so we continue that. - self.allowed_directives = set() - else: - # Note that if directives are explicitly specified, then allow_include - # is ignored. - self.allowed_directives = set(_upper_dollarize(d) for d in allow_directives) - self.force_name = force_name - self.force_ttl = force_ttl - self.force_rdclass = force_rdclass - self.force_rdtype = force_rdtype - self.txn.check_put_rdataset(_check_cname_and_other_data) - - def _eat_line(self): - while 1: - token = self.tok.get() - if token.is_eol_or_eof(): - break - - def _get_identifier(self): - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - return token - - def _rr_line(self): - """Process one line from a DNS zone file.""" - token = None - # Name - if self.force_name is not None: - name = self.force_name - else: - if self.current_origin is None: - raise UnknownOrigin - token = self.tok.get(want_leading=True) - if not token.is_whitespace(): - self.last_name = self.tok.as_name(token, self.current_origin) - else: - token = self.tok.get() - if token.is_eol_or_eof(): - # treat leading WS followed by EOL/EOF as if they were EOL/EOF. - return - self.tok.unget(token) - name = self.last_name - if not name.is_subdomain(self.zone_origin): - self._eat_line() - return - if self.relativize: - name = name.relativize(self.zone_origin) - - # TTL - if self.force_ttl is not None: - ttl = self.force_ttl - self.last_ttl = ttl - self.last_ttl_known = True - else: - token = self._get_identifier() - ttl = None - try: - ttl = dns.ttl.from_text(token.value) - self.last_ttl = ttl - self.last_ttl_known = True - token = None - except dns.ttl.BadTTL: - self.tok.unget(token) - - # Class - if self.force_rdclass is not None: - rdclass = self.force_rdclass - else: - token = self._get_identifier() - try: - rdclass = dns.rdataclass.from_text(token.value) - except dns.exception.SyntaxError: - raise - except Exception: - rdclass = self.zone_rdclass - self.tok.unget(token) - if rdclass != self.zone_rdclass: - raise dns.exception.SyntaxError("RR class is not zone's class") - - if ttl is None: - # support for syntax - token = self._get_identifier() - ttl = None - try: - ttl = dns.ttl.from_text(token.value) - self.last_ttl = ttl - self.last_ttl_known = True - token = None - except dns.ttl.BadTTL: - if self.default_ttl_known: - ttl = self.default_ttl - elif self.last_ttl_known: - ttl = self.last_ttl - self.tok.unget(token) - - # Type - if self.force_rdtype is not None: - rdtype = self.force_rdtype - else: - token = self._get_identifier() - try: - rdtype = dns.rdatatype.from_text(token.value) - except Exception: - raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) - - try: - rd = dns.rdata.from_text( - rdclass, - rdtype, - self.tok, - self.current_origin, - self.relativize, - self.zone_origin, - ) - except dns.exception.SyntaxError: - # Catch and reraise. - raise - except Exception: - # All exceptions that occur in the processing of rdata - # are treated as syntax errors. This is not strictly - # correct, but it is correct almost all of the time. - # We convert them to syntax errors so that we can emit - # helpful filename:line info. - (ty, va) = sys.exc_info()[:2] - raise dns.exception.SyntaxError( - "caught exception {}: {}".format(str(ty), str(va)) - ) - - if not self.default_ttl_known and rdtype == dns.rdatatype.SOA: - # The pre-RFC2308 and pre-BIND9 behavior inherits the zone default - # TTL from the SOA minttl if no $TTL statement is present before the - # SOA is parsed. - self.default_ttl = rd.minimum - self.default_ttl_known = True - if ttl is None: - # if we didn't have a TTL on the SOA, set it! - ttl = rd.minimum - - # TTL check. We had to wait until now to do this as the SOA RR's - # own TTL can be inferred from its minimum. - if ttl is None: - raise dns.exception.SyntaxError("Missing default TTL value") - - self.txn.add(name, ttl, rd) - - def _parse_modify(self, side: str) -> Tuple[str, str, int, int, str]: - # Here we catch everything in '{' '}' in a group so we can replace it - # with ''. - is_generate1 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+),(.)}).*$") - is_generate2 = re.compile(r"^.*\$({(\+|-?)(\d+)}).*$") - is_generate3 = re.compile(r"^.*\$({(\+|-?)(\d+),(\d+)}).*$") - # Sometimes there are modifiers in the hostname. These come after - # the dollar sign. They are in the form: ${offset[,width[,base]]}. - # Make names - g1 = is_generate1.match(side) - if g1: - mod, sign, offset, width, base = g1.groups() - if sign == "": - sign = "+" - g2 = is_generate2.match(side) - if g2: - mod, sign, offset = g2.groups() - if sign == "": - sign = "+" - width = 0 - base = "d" - g3 = is_generate3.match(side) - if g3: - mod, sign, offset, width = g3.groups() - if sign == "": - sign = "+" - base = "d" - - if not (g1 or g2 or g3): - mod = "" - sign = "+" - offset = 0 - width = 0 - base = "d" - - offset = int(offset) - width = int(width) - - if sign not in ["+", "-"]: - raise dns.exception.SyntaxError("invalid offset sign %s" % sign) - if base not in ["d", "o", "x", "X", "n", "N"]: - raise dns.exception.SyntaxError("invalid type %s" % base) - - return mod, sign, offset, width, base - - def _generate_line(self): - # range lhs [ttl] [class] type rhs [ comment ] - """Process one line containing the GENERATE statement from a DNS - zone file.""" - if self.current_origin is None: - raise UnknownOrigin - - token = self.tok.get() - # Range (required) - try: - start, stop, step = dns.grange.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except Exception: - raise dns.exception.SyntaxError - - # lhs (required) - try: - lhs = token.value - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except Exception: - raise dns.exception.SyntaxError - - # TTL - try: - ttl = dns.ttl.from_text(token.value) - self.last_ttl = ttl - self.last_ttl_known = True - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.ttl.BadTTL: - if not (self.last_ttl_known or self.default_ttl_known): - raise dns.exception.SyntaxError("Missing default TTL value") - if self.default_ttl_known: - ttl = self.default_ttl - elif self.last_ttl_known: - ttl = self.last_ttl - # Class - try: - rdclass = dns.rdataclass.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except dns.exception.SyntaxError: - raise dns.exception.SyntaxError - except Exception: - rdclass = self.zone_rdclass - if rdclass != self.zone_rdclass: - raise dns.exception.SyntaxError("RR class is not zone's class") - # Type - try: - rdtype = dns.rdatatype.from_text(token.value) - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError - except Exception: - raise dns.exception.SyntaxError("unknown rdatatype '%s'" % token.value) - - # rhs (required) - rhs = token.value - - def _calculate_index(counter: int, offset_sign: str, offset: int) -> int: - """Calculate the index from the counter and offset.""" - if offset_sign == "-": - offset *= -1 - return counter + offset - - def _format_index(index: int, base: str, width: int) -> str: - """Format the index with the given base, and zero-fill it - to the given width.""" - if base in ["d", "o", "x", "X"]: - return format(index, base).zfill(width) - - # base can only be n or N here - hexa = _format_index(index, "x", width) - nibbles = ".".join(hexa[::-1])[:width] - if base == "N": - nibbles = nibbles.upper() - return nibbles - - lmod, lsign, loffset, lwidth, lbase = self._parse_modify(lhs) - rmod, rsign, roffset, rwidth, rbase = self._parse_modify(rhs) - for i in range(start, stop + 1, step): - # +1 because bind is inclusive and python is exclusive - - lindex = _calculate_index(i, lsign, loffset) - rindex = _calculate_index(i, rsign, roffset) - - lzfindex = _format_index(lindex, lbase, lwidth) - rzfindex = _format_index(rindex, rbase, rwidth) - - name = lhs.replace("$%s" % (lmod), lzfindex) - rdata = rhs.replace("$%s" % (rmod), rzfindex) - - self.last_name = dns.name.from_text( - name, self.current_origin, self.tok.idna_codec - ) - name = self.last_name - if not name.is_subdomain(self.zone_origin): - self._eat_line() - return - if self.relativize: - name = name.relativize(self.zone_origin) - - try: - rd = dns.rdata.from_text( - rdclass, - rdtype, - rdata, - self.current_origin, - self.relativize, - self.zone_origin, - ) - except dns.exception.SyntaxError: - # Catch and reraise. - raise - except Exception: - # All exceptions that occur in the processing of rdata - # are treated as syntax errors. This is not strictly - # correct, but it is correct almost all of the time. - # We convert them to syntax errors so that we can emit - # helpful filename:line info. - (ty, va) = sys.exc_info()[:2] - raise dns.exception.SyntaxError( - "caught exception %s: %s" % (str(ty), str(va)) - ) - - self.txn.add(name, ttl, rd) - - def read(self) -> None: - """Read a DNS zone file and build a zone object. - - @raises dns.zone.NoSOA: No SOA RR was found at the zone origin - @raises dns.zone.NoNS: No NS RRset was found at the zone origin - """ - - try: - while 1: - token = self.tok.get(True, True) - if token.is_eof(): - if self.current_file is not None: - self.current_file.close() - if len(self.saved_state) > 0: - ( - self.tok, - self.current_origin, - self.last_name, - self.current_file, - self.last_ttl, - self.last_ttl_known, - self.default_ttl, - self.default_ttl_known, - ) = self.saved_state.pop(-1) - continue - break - elif token.is_eol(): - continue - elif token.is_comment(): - self.tok.get_eol() - continue - elif token.value[0] == "$" and len(self.allowed_directives) > 0: - # Note that we only run directive processing code if at least - # one directive is allowed in order to be backwards compatible - c = token.value.upper() - if c not in self.allowed_directives: - raise dns.exception.SyntaxError( - f"zone file directive '{c}' is not allowed" - ) - if c == "$TTL": - token = self.tok.get() - if not token.is_identifier(): - raise dns.exception.SyntaxError("bad $TTL") - self.default_ttl = dns.ttl.from_text(token.value) - self.default_ttl_known = True - self.tok.get_eol() - elif c == "$ORIGIN": - self.current_origin = self.tok.get_name() - self.tok.get_eol() - if self.zone_origin is None: - self.zone_origin = self.current_origin - self.txn._set_origin(self.current_origin) - elif c == "$INCLUDE": - token = self.tok.get() - filename = token.value - token = self.tok.get() - new_origin: Optional[dns.name.Name] - if token.is_identifier(): - new_origin = dns.name.from_text( - token.value, self.current_origin, self.tok.idna_codec - ) - self.tok.get_eol() - elif not token.is_eol_or_eof(): - raise dns.exception.SyntaxError("bad origin in $INCLUDE") - else: - new_origin = self.current_origin - self.saved_state.append( - ( - self.tok, - self.current_origin, - self.last_name, - self.current_file, - self.last_ttl, - self.last_ttl_known, - self.default_ttl, - self.default_ttl_known, - ) - ) - self.current_file = open(filename, "r") - self.tok = dns.tokenizer.Tokenizer(self.current_file, filename) - self.current_origin = new_origin - elif c == "$GENERATE": - self._generate_line() - else: - raise dns.exception.SyntaxError( - f"Unknown zone file directive '{c}'" - ) - continue - self.tok.unget(token) - self._rr_line() - except dns.exception.SyntaxError as detail: - (filename, line_number) = self.tok.where() - if detail is None: - detail = "syntax error" - ex = dns.exception.SyntaxError( - "%s:%d: %s" % (filename, line_number, detail) - ) - tb = sys.exc_info()[2] - raise ex.with_traceback(tb) from None - - -class RRsetsReaderTransaction(dns.transaction.Transaction): - def __init__(self, manager, replacement, read_only): - assert not read_only - super().__init__(manager, replacement, read_only) - self.rdatasets = {} - - def _get_rdataset(self, name, rdtype, covers): - return self.rdatasets.get((name, rdtype, covers)) - - def _get_node(self, name): - rdatasets = [] - for (rdataset_name, _, _), rdataset in self.rdatasets.items(): - if name == rdataset_name: - rdatasets.append(rdataset) - if len(rdatasets) == 0: - return None - node = dns.node.Node() - node.rdatasets = rdatasets - return node - - def _put_rdataset(self, name, rdataset): - self.rdatasets[(name, rdataset.rdtype, rdataset.covers)] = rdataset - - def _delete_name(self, name): - # First remove any changes involving the name - remove = [] - for key in self.rdatasets: - if key[0] == name: - remove.append(key) - if len(remove) > 0: - for key in remove: - del self.rdatasets[key] - - def _delete_rdataset(self, name, rdtype, covers): - try: - del self.rdatasets[(name, rdtype, covers)] - except KeyError: - pass - - def _name_exists(self, name): - for n, _, _ in self.rdatasets: - if n == name: - return True - return False - - def _changed(self): - return len(self.rdatasets) > 0 - - def _end_transaction(self, commit): - if commit and self._changed(): - rrsets = [] - for (name, _, _), rdataset in self.rdatasets.items(): - rrset = dns.rrset.RRset( - name, rdataset.rdclass, rdataset.rdtype, rdataset.covers - ) - rrset.update(rdataset) - rrsets.append(rrset) - self.manager.set_rrsets(rrsets) - - def _set_origin(self, origin): - pass - - def _iterate_rdatasets(self): - raise NotImplementedError # pragma: no cover - - def _iterate_names(self): - raise NotImplementedError # pragma: no cover - - -class RRSetsReaderManager(dns.transaction.TransactionManager): - def __init__( - self, origin=dns.name.root, relativize=False, rdclass=dns.rdataclass.IN - ): - self.origin = origin - self.relativize = relativize - self.rdclass = rdclass - self.rrsets = [] - - def reader(self): # pragma: no cover - raise NotImplementedError - - def writer(self, replacement=False): - assert replacement is True - return RRsetsReaderTransaction(self, True, False) - - def get_class(self): - return self.rdclass - - def origin_information(self): - if self.relativize: - effective = dns.name.empty - else: - effective = self.origin - return (self.origin, self.relativize, effective) - - def set_rrsets(self, rrsets): - self.rrsets = rrsets - - -def read_rrsets( - text: Any, - name: Optional[Union[dns.name.Name, str]] = None, - ttl: Optional[int] = None, - rdclass: Optional[Union[dns.rdataclass.RdataClass, str]] = dns.rdataclass.IN, - default_rdclass: Union[dns.rdataclass.RdataClass, str] = dns.rdataclass.IN, - rdtype: Optional[Union[dns.rdatatype.RdataType, str]] = None, - default_ttl: Optional[Union[int, str]] = None, - idna_codec: Optional[dns.name.IDNACodec] = None, - origin: Optional[Union[dns.name.Name, str]] = dns.name.root, - relativize: bool = False, -) -> List[dns.rrset.RRset]: - """Read one or more rrsets from the specified text, possibly subject - to restrictions. - - *text*, a file object or a string, is the input to process. - - *name*, a string, ``dns.name.Name``, or ``None``, is the owner name of - the rrset. If not ``None``, then the owner name is "forced", and the - input must not specify an owner name. If ``None``, then any owner names - are allowed and must be present in the input. - - *ttl*, an ``int``, string, or None. If not ``None``, the the TTL is - forced to be the specified value and the input must not specify a TTL. - If ``None``, then a TTL may be specified in the input. If it is not - specified, then the *default_ttl* will be used. - - *rdclass*, a ``dns.rdataclass.RdataClass``, string, or ``None``. If - not ``None``, then the class is forced to the specified value, and the - input must not specify a class. If ``None``, then the input may specify - a class that matches *default_rdclass*. Note that it is not possible to - return rrsets with differing classes; specifying ``None`` for the class - simply allows the user to optionally type a class as that may be convenient - when cutting and pasting. - - *default_rdclass*, a ``dns.rdataclass.RdataClass`` or string. The class - of the returned rrsets. - - *rdtype*, a ``dns.rdatatype.RdataType``, string, or ``None``. If not - ``None``, then the type is forced to the specified value, and the - input must not specify a type. If ``None``, then a type must be present - for each RR. - - *default_ttl*, an ``int``, string, or ``None``. If not ``None``, then if - the TTL is not forced and is not specified, then this value will be used. - if ``None``, then if the TTL is not forced an error will occur if the TTL - is not specified. - - *idna_codec*, a ``dns.name.IDNACodec``, specifies the IDNA - encoder/decoder. If ``None``, the default IDNA 2003 encoder/decoder - is used. Note that codecs only apply to the owner name; dnspython does - not do IDNA for names in rdata, as there is no IDNA zonefile format. - - *origin*, a string, ``dns.name.Name``, or ``None``, is the origin for any - relative names in the input, and also the origin to relativize to if - *relativize* is ``True``. - - *relativize*, a bool. If ``True``, names are relativized to the *origin*; - if ``False`` then any relative names in the input are made absolute by - appending the *origin*. - """ - if isinstance(origin, str): - origin = dns.name.from_text(origin, dns.name.root, idna_codec) - if isinstance(name, str): - name = dns.name.from_text(name, origin, idna_codec) - if isinstance(ttl, str): - ttl = dns.ttl.from_text(ttl) - if isinstance(default_ttl, str): - default_ttl = dns.ttl.from_text(default_ttl) - if rdclass is not None: - rdclass = dns.rdataclass.RdataClass.make(rdclass) - else: - rdclass = None - default_rdclass = dns.rdataclass.RdataClass.make(default_rdclass) - if rdtype is not None: - rdtype = dns.rdatatype.RdataType.make(rdtype) - else: - rdtype = None - manager = RRSetsReaderManager(origin, relativize, default_rdclass) - with manager.writer(True) as txn: - tok = dns.tokenizer.Tokenizer(text, "", idna_codec=idna_codec) - reader = Reader( - tok, - default_rdclass, - txn, - allow_directives=False, - force_name=name, - force_ttl=ttl, - force_rdclass=rdclass, - force_rdtype=rdtype, - default_ttl=default_ttl, - ) - reader.read() - return manager.rrsets diff --git a/.venv/lib/python3.12/site-packages/dns/zonetypes.py b/.venv/lib/python3.12/site-packages/dns/zonetypes.py deleted file mode 100644 index 195ee2e..0000000 --- a/.venv/lib/python3.12/site-packages/dns/zonetypes.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license - -"""Common zone-related types.""" - -# This is a separate file to avoid import circularity between dns.zone and -# the implementation of the ZONEMD type. - -import hashlib - -import dns.enum - - -class DigestScheme(dns.enum.IntEnum): - """ZONEMD Scheme""" - - SIMPLE = 1 - - @classmethod - def _maximum(cls): - return 255 - - -class DigestHashAlgorithm(dns.enum.IntEnum): - """ZONEMD Hash Algorithm""" - - SHA384 = 1 - SHA512 = 2 - - @classmethod - def _maximum(cls): - return 255 - - -_digest_hashers = { - DigestHashAlgorithm.SHA384: hashlib.sha384, - DigestHashAlgorithm.SHA512: hashlib.sha512, -} diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/METADATA deleted file mode 100644 index 129184e..0000000 --- a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/METADATA +++ /dev/null @@ -1,147 +0,0 @@ -Metadata-Version: 2.1 -Name: dnspython -Version: 2.6.1 -Summary: DNS toolkit -Project-URL: homepage, https://www.dnspython.org -Project-URL: repository, https://github.com/rthalley/dnspython.git -Project-URL: documentation, https://dnspython.readthedocs.io/en/stable/ -Project-URL: issues, https://github.com/rthalley/dnspython/issues -Author-email: Bob Halley -License-Expression: ISC -License-File: LICENSE -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: License :: OSI Approved :: ISC License (ISCL) -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: POSIX -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Topic :: Internet :: Name Service (DNS) -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Requires-Python: >=3.8 -Provides-Extra: dev -Requires-Dist: black>=23.1.0; extra == 'dev' -Requires-Dist: coverage>=7.0; extra == 'dev' -Requires-Dist: flake8>=7; extra == 'dev' -Requires-Dist: mypy>=1.8; extra == 'dev' -Requires-Dist: pylint>=3; extra == 'dev' -Requires-Dist: pytest-cov>=4.1.0; extra == 'dev' -Requires-Dist: pytest>=7.4; extra == 'dev' -Requires-Dist: sphinx>=7.2.0; extra == 'dev' -Requires-Dist: twine>=4.0.0; extra == 'dev' -Requires-Dist: wheel>=0.42.0; extra == 'dev' -Provides-Extra: dnssec -Requires-Dist: cryptography>=41; extra == 'dnssec' -Provides-Extra: doh -Requires-Dist: h2>=4.1.0; extra == 'doh' -Requires-Dist: httpcore>=1.0.0; extra == 'doh' -Requires-Dist: httpx>=0.26.0; extra == 'doh' -Provides-Extra: doq -Requires-Dist: aioquic>=0.9.25; extra == 'doq' -Provides-Extra: idna -Requires-Dist: idna>=3.6; extra == 'idna' -Provides-Extra: trio -Requires-Dist: trio>=0.23; extra == 'trio' -Provides-Extra: wmi -Requires-Dist: wmi>=1.5.1; extra == 'wmi' -Description-Content-Type: text/markdown - -# dnspython - -[![Build Status](https://github.com/rthalley/dnspython/actions/workflows/python-package.yml/badge.svg)](https://github.com/rthalley/dnspython/actions/) -[![Documentation Status](https://readthedocs.org/projects/dnspython/badge/?version=latest)](https://dnspython.readthedocs.io/en/latest/?badge=latest) -[![PyPI version](https://badge.fury.io/py/dnspython.svg)](https://badge.fury.io/py/dnspython) -[![License: ISC](https://img.shields.io/badge/License-ISC-brightgreen.svg)](https://opensource.org/licenses/ISC) -[![Coverage](https://codecov.io/github/rthalley/dnspython/coverage.svg?branch=master)](https://codecov.io/github/rthalley/dnspython) -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) - -## INTRODUCTION - -dnspython is a DNS toolkit for Python. It supports almost all record types. It -can be used for queries, zone transfers, and dynamic updates. It supports TSIG -authenticated messages and EDNS0. - -dnspython provides both high and low level access to DNS. The high level classes -perform queries for data of a given name, type, and class, and return an answer -set. The low level classes allow direct manipulation of DNS zones, messages, -names, and records. - -To see a few of the ways dnspython can be used, look in the `examples/` -directory. - -dnspython is a utility to work with DNS, `/etc/hosts` is thus not used. For -simple forward DNS lookups, it's better to use `socket.getaddrinfo()` or -`socket.gethostbyname()`. - -dnspython originated at Nominum where it was developed -to facilitate the testing of DNS software. - -## ABOUT THIS RELEASE - -This is dnspython 2.6.1. -Please read -[What's New](https://dnspython.readthedocs.io/en/stable/whatsnew.html) for -information about the changes in this release. - -## INSTALLATION - -* Many distributions have dnspython packaged for you, so you should - check there first. -* To use a wheel downloaded from PyPi, run: - - pip install dnspython - -* To install from the source code, go into the top-level of the source code - and run: - -``` - pip install --upgrade pip build - python -m build - pip install dist/*.whl -``` - -* To install the latest from the master branch, run `pip install git+https://github.com/rthalley/dnspython.git` - -Dnspython's default installation does not depend on any modules other than -those in the Python standard library. To use some features, additional modules -must be installed. For convenience, pip options are defined for the -requirements. - -If you want to use DNS-over-HTTPS, run -`pip install dnspython[doh]`. - -If you want to use DNSSEC functionality, run -`pip install dnspython[dnssec]`. - -If you want to use internationalized domain names (IDNA) -functionality, you must run -`pip install dnspython[idna]` - -If you want to use the Trio asynchronous I/O package, run -`pip install dnspython[trio]`. - -If you want to use WMI on Windows to determine the active DNS settings -instead of the default registry scanning method, run -`pip install dnspython[wmi]`. - -If you want to try the experimental DNS-over-QUIC code, run -`pip install dnspython[doq]`. - -Note that you can install any combination of the above, e.g.: -`pip install dnspython[doh,dnssec,idna]` - -### Notices - -Python 2.x support ended with the release of 1.16.0. Dnspython 2.0.0 through -2.2.x support Python 3.6 and later. For dnspython 2.3.x, the minimum -supported Python version is 3.7, and for 2.4.x the minimum supported verison is 3.8. -We plan to align future support with the lifetime of the Python 3 versions. - -Documentation has moved to -[dnspython.readthedocs.io](https://dnspython.readthedocs.io). diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/RECORD deleted file mode 100644 index ec1dbb9..0000000 --- a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/RECORD +++ /dev/null @@ -1,291 +0,0 @@ -dns/__init__.py,sha256=YJZtDG14Idw5ui3h1nWooSwPM9gsxQgB8M0GBZ3aly0,1663 -dns/__pycache__/__init__.cpython-312.pyc,, -dns/__pycache__/_asyncbackend.cpython-312.pyc,, -dns/__pycache__/_asyncio_backend.cpython-312.pyc,, -dns/__pycache__/_ddr.cpython-312.pyc,, -dns/__pycache__/_features.cpython-312.pyc,, -dns/__pycache__/_immutable_ctx.cpython-312.pyc,, -dns/__pycache__/_trio_backend.cpython-312.pyc,, -dns/__pycache__/asyncbackend.cpython-312.pyc,, -dns/__pycache__/asyncquery.cpython-312.pyc,, -dns/__pycache__/asyncresolver.cpython-312.pyc,, -dns/__pycache__/dnssec.cpython-312.pyc,, -dns/__pycache__/dnssectypes.cpython-312.pyc,, -dns/__pycache__/e164.cpython-312.pyc,, -dns/__pycache__/edns.cpython-312.pyc,, -dns/__pycache__/entropy.cpython-312.pyc,, -dns/__pycache__/enum.cpython-312.pyc,, -dns/__pycache__/exception.cpython-312.pyc,, -dns/__pycache__/flags.cpython-312.pyc,, -dns/__pycache__/grange.cpython-312.pyc,, -dns/__pycache__/immutable.cpython-312.pyc,, -dns/__pycache__/inet.cpython-312.pyc,, -dns/__pycache__/ipv4.cpython-312.pyc,, -dns/__pycache__/ipv6.cpython-312.pyc,, -dns/__pycache__/message.cpython-312.pyc,, -dns/__pycache__/name.cpython-312.pyc,, -dns/__pycache__/namedict.cpython-312.pyc,, -dns/__pycache__/nameserver.cpython-312.pyc,, -dns/__pycache__/node.cpython-312.pyc,, -dns/__pycache__/opcode.cpython-312.pyc,, -dns/__pycache__/query.cpython-312.pyc,, -dns/__pycache__/rcode.cpython-312.pyc,, -dns/__pycache__/rdata.cpython-312.pyc,, -dns/__pycache__/rdataclass.cpython-312.pyc,, -dns/__pycache__/rdataset.cpython-312.pyc,, -dns/__pycache__/rdatatype.cpython-312.pyc,, -dns/__pycache__/renderer.cpython-312.pyc,, -dns/__pycache__/resolver.cpython-312.pyc,, -dns/__pycache__/reversename.cpython-312.pyc,, -dns/__pycache__/rrset.cpython-312.pyc,, -dns/__pycache__/serial.cpython-312.pyc,, -dns/__pycache__/set.cpython-312.pyc,, -dns/__pycache__/tokenizer.cpython-312.pyc,, -dns/__pycache__/transaction.cpython-312.pyc,, -dns/__pycache__/tsig.cpython-312.pyc,, -dns/__pycache__/tsigkeyring.cpython-312.pyc,, -dns/__pycache__/ttl.cpython-312.pyc,, -dns/__pycache__/update.cpython-312.pyc,, -dns/__pycache__/version.cpython-312.pyc,, -dns/__pycache__/versioned.cpython-312.pyc,, -dns/__pycache__/win32util.cpython-312.pyc,, -dns/__pycache__/wire.cpython-312.pyc,, -dns/__pycache__/xfr.cpython-312.pyc,, -dns/__pycache__/zone.cpython-312.pyc,, -dns/__pycache__/zonefile.cpython-312.pyc,, -dns/__pycache__/zonetypes.cpython-312.pyc,, -dns/_asyncbackend.py,sha256=Ny0kGesm9wbLBnt-0u-tANOKsxcYt2jbMuRoRz_JZUA,2360 -dns/_asyncio_backend.py,sha256=q58xPdqAOLmOYOux8GFRyiH-fSZ7jiwZF-Jg2vHjYSU,8971 -dns/_ddr.py,sha256=rHXKC8kncCTT9N4KBh1flicl79nyDjQ-DDvq30MJ3B8,5247 -dns/_features.py,sha256=MUeyfM_nMYAYkasGfbY7I_15JmwftaZjseuP1L43MT0,2384 -dns/_immutable_ctx.py,sha256=gtoCLMmdHXI23zt5lRSIS3A4Ca3jZJngebdoFFOtiwU,2459 -dns/_trio_backend.py,sha256=Vab_wR2CxDgy2Jz3iM_64FZmP_kMUN9j8LS4eNl-Oig,8269 -dns/asyncbackend.py,sha256=82fXTFls_m7F_ekQbgUGOkoBbs4BI-GBLDZAWNGUvJ0,2796 -dns/asyncquery.py,sha256=Q7u04mbbqCoe9VxsqRcsWTPxgH2Cx49eWWgi2wUyZHU,26850 -dns/asyncresolver.py,sha256=GD86dCyW9YGKs6SggWXwBKEXifW7Qdx4cEAGFKY6fA4,17852 -dns/dnssec.py,sha256=xyYW1cf6eeFNXROrEs1pyY4TgC8jlmUiiootaPbVjjY,40693 -dns/dnssecalgs/__init__.py,sha256=DcnGIbL6m-USPSiLWHSw511awB7dytlljvCOOmzchS0,4279 -dns/dnssecalgs/__pycache__/__init__.cpython-312.pyc,, -dns/dnssecalgs/__pycache__/base.cpython-312.pyc,, -dns/dnssecalgs/__pycache__/cryptography.cpython-312.pyc,, -dns/dnssecalgs/__pycache__/dsa.cpython-312.pyc,, -dns/dnssecalgs/__pycache__/ecdsa.cpython-312.pyc,, -dns/dnssecalgs/__pycache__/eddsa.cpython-312.pyc,, -dns/dnssecalgs/__pycache__/rsa.cpython-312.pyc,, -dns/dnssecalgs/base.py,sha256=hsFHFr_eCYeDcI0eU6_WiLlOYL0GR4QJ__sXoMrIAfE,2446 -dns/dnssecalgs/cryptography.py,sha256=3uqMfRm-zCkJPOrxUqlu9CmdxIMy71dVor9eAHi0wZM,2425 -dns/dnssecalgs/dsa.py,sha256=hklh_HkT_ZffQBHQ7t6pKUStTH4x5nXlz8R9RUP72aY,3497 -dns/dnssecalgs/ecdsa.py,sha256=GWrJgEXAK08MCdbLk7LQcD2ajKqW_dbONWXh3wieLzw,3016 -dns/dnssecalgs/eddsa.py,sha256=9lQQZ92f2PiIhhylieInO-19aSTDQiyoY8X2kTkGlcs,1914 -dns/dnssecalgs/rsa.py,sha256=jWkhWKByylIo7Y9gAiiO8t8bowF8IZ0siVjgZpdhLSE,3555 -dns/dnssectypes.py,sha256=CyeuGTS_rM3zXr8wD9qMT9jkzvVfTY2JWckUcogG83E,1799 -dns/e164.py,sha256=EsK8cnOtOx7kQ0DmSwibcwkzp6efMWjbRiTyHZO8Q-M,3978 -dns/edns.py,sha256=d8QWhmRd6qlaGfO-tY6iDQZt9XUiyfJfKdjoGjvwOU4,15263 -dns/entropy.py,sha256=qkG8hXDLzrJS6R5My26iA59c0RhPwJNzuOhOCAZU5Bw,4242 -dns/enum.py,sha256=EepaunPKixTSrascy7iAe9UQEXXxP_MB5Gx4jUpHIhg,3691 -dns/exception.py,sha256=FphWy-JLRG06UUUq2VmUGwdPA1xWja_8YfrcffRFlQs,5957 -dns/flags.py,sha256=cQ3kTFyvcKiWHAxI5AwchNqxVOrsIrgJ6brgrH42Wq8,2750 -dns/grange.py,sha256=HA623Mv2mZDmOK_BZNDDakT0L6EHsMQU9lFFkE8dKr0,2148 -dns/immutable.py,sha256=InrtpKvPxl-74oYbzsyneZwAuX78hUqeG22f2aniZbk,2017 -dns/inet.py,sha256=j6jQs3K_ehVhDv-i4jwCKePr5HpEiSzvOXQ4uhgn1sU,5772 -dns/ipv4.py,sha256=qEUXtlqWDH_blicj6VMvyQhfX7-BF0gB_lWJliV-2FI,2552 -dns/ipv6.py,sha256=EyiF5T8t2oww9-W4ZA5Zk2GGnOjTy_uZ50CI7maed_8,6600 -dns/message.py,sha256=DyUtBHArPX-WGj_AtcngyIXZNpLppLZX-6q9TryL_wI,65993 -dns/name.py,sha256=eaR1wVR0rErnD3EPANquCuyqpbxy5VfFVhMenWlBPDE,42672 -dns/namedict.py,sha256=hJRYpKeQv6Bd2LaUOPV0L_a0eXEIuqgggPXaH4c3Tow,4000 -dns/nameserver.py,sha256=VkYRnX5wQ7RihAD6kYqidI_hb9NgKJSAE0GaYulNpHY,9909 -dns/node.py,sha256=NGZa0AUMq-CNledJ6wn1Rx6TFYc703cH2OraLysoNWM,12663 -dns/opcode.py,sha256=I6JyuFUL0msja_BYm6bzXHfbbfqUod_69Ss4xcv8xWQ,2730 -dns/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -dns/query.py,sha256=vB8C5u6HyjPWrEx9kUdTSg3kxrOoWbPGu7brC0eetIM,54832 -dns/quic/__init__.py,sha256=F6BybmRKnMGc4W8nX7K98PeyXiSwy1FHb_bJeA2lQSw,2202 -dns/quic/__pycache__/__init__.cpython-312.pyc,, -dns/quic/__pycache__/_asyncio.cpython-312.pyc,, -dns/quic/__pycache__/_common.cpython-312.pyc,, -dns/quic/__pycache__/_sync.cpython-312.pyc,, -dns/quic/__pycache__/_trio.cpython-312.pyc,, -dns/quic/_asyncio.py,sha256=vv4RR3Ol0Y1ZOj7rPAzXxy1UcWjPvhTGQvVkMidPs-o,8159 -dns/quic/_common.py,sha256=06TfauL2VciPYSfrL4gif1eR1rm-TRkQhS2Puuk5URU,7282 -dns/quic/_sync.py,sha256=kE0PRavzd27GPQ9UgYApXZ6SGSW2LwCt8k6XWUvrbVE,8133 -dns/quic/_trio.py,sha256=9zCCBtDs6GAtY_b8ck-A17QMiLZ0njjhVtfFT5qMP7s,7670 -dns/rcode.py,sha256=N6JjrIQjCdJy0boKIp8Hcky5tm__LSDscpDz3rE_sgU,4156 -dns/rdata.py,sha256=9cXM9Y9MK2hy9w5mYqmP-r7_aKjHosigfNn_SfqfGGw,29456 -dns/rdataclass.py,sha256=TK4W4ywB1L_X7EZqk2Gmwnu7vdQpolQF5DtQWyNk5xo,2984 -dns/rdataset.py,sha256=96gTaEIcYEL348VKtTOMAazXBVNtk7m0Xez0mF1eg4I,16756 -dns/rdatatype.py,sha256=gIdYZ0iHRlgiTEO-ftobUANmaAmjTnNc4JljMaP1OnQ,7339 -dns/rdtypes/ANY/AFSDB.py,sha256=k75wMwreF1DAfDymu4lHh16BUx7ulVP3PLeQBZnkurY,1661 -dns/rdtypes/ANY/AMTRELAY.py,sha256=19jfS61mT1CQT-8vf67ZylhDS9JVRVp4WCbFE-7l0jM,3381 -dns/rdtypes/ANY/AVC.py,sha256=SpsXYzlBirRWN0mGnQe0MdN6H8fvlgXPJX5PjOHnEak,1024 -dns/rdtypes/ANY/CAA.py,sha256=AHh59Is-4WiVWd26yovnPM3hXqKS-yx7IWfXSS0NZhE,2511 -dns/rdtypes/ANY/CDNSKEY.py,sha256=bJAdrBMsFHIJz8TF1AxZoNbdxVWBCRTG-bR_uR_r_G4,1225 -dns/rdtypes/ANY/CDS.py,sha256=Y9nIRUCAabztVLbxm2SXAdYapFemCOUuGh5JqroCDUs,1163 -dns/rdtypes/ANY/CERT.py,sha256=2Cu2LQM6-K4darqhHv1EM_blmpYpnrBIIX1GnL_rxKE,3533 -dns/rdtypes/ANY/CNAME.py,sha256=IHGGq2BDpeKUahTr1pvyBQgm0NGBI_vQ3Vs5mKTXO4w,1206 -dns/rdtypes/ANY/CSYNC.py,sha256=KkZ_rG6PfeL14il97nmJGWWmUGGS5o9nd2EqbJqOuYo,2439 -dns/rdtypes/ANY/DLV.py,sha256=J-pOrw5xXsDoaB9G0r6znlYXJtqtcqhsl1OXs6CPRU4,986 -dns/rdtypes/ANY/DNAME.py,sha256=yqXRtx4dAWwB4YCCv-qW6uaxeGhg2LPQ2uyKwWaMdXs,1150 -dns/rdtypes/ANY/DNSKEY.py,sha256=MD8HUVH5XXeAGOnFWg5aVz_w-2tXYwCeVXmzExhiIeQ,1223 -dns/rdtypes/ANY/DS.py,sha256=_gf8vk1O_uY8QXFjsfUw-bny-fm6e-QpCk3PT0JCyoM,995 -dns/rdtypes/ANY/EUI48.py,sha256=x0BkK0sY_tgzuCwfDYpw6tyuChHjjtbRpAgYhO0Y44o,1151 -dns/rdtypes/ANY/EUI64.py,sha256=1jCff2-SXHJLDnNDnMW8Cd_o-ok0P3x6zKy_bcCU5h4,1161 -dns/rdtypes/ANY/GPOS.py,sha256=pM3i6Tn4qwHWOGOuIuW9FENPlSXT_R4xsNJeGrrABc8,4433 -dns/rdtypes/ANY/HINFO.py,sha256=vYGCHGZmYOhtmxHlvPqrK7m4pBg3MSY5herBsKJTbKQ,2249 -dns/rdtypes/ANY/HIP.py,sha256=Ucrnndu3xDyHFB93AVUA3xW-r61GR50kpRHLyLacvZY,3228 -dns/rdtypes/ANY/ISDN.py,sha256=uymYB-ayZSBob6jQgXe4EefNB8-JMLW6VfxXn7ncwPg,2713 -dns/rdtypes/ANY/L32.py,sha256=TMz2kdGCd0siiQZyiocVDCSnvkOdjhUuYRFyf8o622M,1286 -dns/rdtypes/ANY/L64.py,sha256=sb2BjuPA0PQt67nEyT9rBt759C9e6lH71d3EJHGGnww,1592 -dns/rdtypes/ANY/LOC.py,sha256=hLkzgCxqEhg6fn5Uf-DJigKEIE6oavQ8rLpajp3HDLs,12024 -dns/rdtypes/ANY/LP.py,sha256=wTsKIjtK6vh66qZRLSsiE0k54GO8ieVBGZH8dzVvFnE,1338 -dns/rdtypes/ANY/MX.py,sha256=qQk83idY0-SbRMDmB15JOpJi7cSyiheF-ALUD0Ev19E,995 -dns/rdtypes/ANY/NID.py,sha256=N7Xx4kXf3yVAocTlCXQeJ3BtiQNPFPQVdL1iMuyl5W4,1544 -dns/rdtypes/ANY/NINFO.py,sha256=bdL_-6Bejb2EH-xwR1rfSr_9E3SDXLTAnov7x2924FI,1041 -dns/rdtypes/ANY/NS.py,sha256=ThfaPalUlhbyZyNyvBM3k-7onl3eJKq5wCORrOGtkMM,995 -dns/rdtypes/ANY/NSEC.py,sha256=6uRn1SxNuLRNumeoc76BkpECF8ztuqyaYviLjFe7FkQ,2475 -dns/rdtypes/ANY/NSEC3.py,sha256=696h-Zz30bmcT0n1rqoEtS5wqE6jIgsVGzaw5TfdGJo,4331 -dns/rdtypes/ANY/NSEC3PARAM.py,sha256=08p6NWS4DiLav1wOuPbxUxB9MtY2IPjfOMCtJwzzMuA,2635 -dns/rdtypes/ANY/OPENPGPKEY.py,sha256=Va0FGo_8vm1OeX62N5iDTWukAdLwrjTXIZeQ6oanE78,1851 -dns/rdtypes/ANY/OPT.py,sha256=W36RslT_Psp95OPUC70knumOYjKpaRHvGT27I-NV2qc,2561 -dns/rdtypes/ANY/PTR.py,sha256=5HcR1D77Otyk91vVY4tmqrfZfSxSXWyWvwIW-rIH5gc,997 -dns/rdtypes/ANY/RP.py,sha256=5Dgaava9mbLKr87XgbfKZPrunYPBaN8ejNzpmbW6r4s,2184 -dns/rdtypes/ANY/RRSIG.py,sha256=O8vwzS7ldfaj_x8DypvEGFsDSb7al-D7OEnprA3QQoo,4922 -dns/rdtypes/ANY/RT.py,sha256=2t9q3FZQ28iEyceeU25KU2Ur0T5JxELAu8BTwfOUgVw,1013 -dns/rdtypes/ANY/SMIMEA.py,sha256=6yjHuVDfIEodBU9wxbCGCDZ5cWYwyY6FCk-aq2VNU0s,222 -dns/rdtypes/ANY/SOA.py,sha256=Cn8yrag1YvrvwivQgWg-KXmOCaVQVdFHSkFF77w-CE0,3145 -dns/rdtypes/ANY/SPF.py,sha256=rA3Srs9ECQx-37lqm7Zf7aYmMpp_asv4tGS8_fSQ-CU,1022 -dns/rdtypes/ANY/SSHFP.py,sha256=l6TZH2R0kytiZGWez_g-Lq94o5a2xMuwLKwUwsPMx5w,2530 -dns/rdtypes/ANY/TKEY.py,sha256=HjJMIMl4Qb1Nt1JXS6iAymzd2nv_zdLWTt887PJU_5w,4931 -dns/rdtypes/ANY/TLSA.py,sha256=cytzebS3W7FFr9qeJ9gFSHq_bOwUk9aRVlXWHfnVrRs,218 -dns/rdtypes/ANY/TSIG.py,sha256=4fNQJSNWZXUKZejCciwQuUJtTw2g-YbPmqHrEj_pitg,4750 -dns/rdtypes/ANY/TXT.py,sha256=F1U9gIAhwXIV4UVT7CwOCEn_su6G1nJIdgWJsLktk20,1000 -dns/rdtypes/ANY/URI.py,sha256=dpcS8KwcJ2WJ7BkOp4CZYaUyRuw7U2S9GzvVwKUihQg,2921 -dns/rdtypes/ANY/X25.py,sha256=PxjYTKIuoq44LT2S2JHWOV8BOFD0ASqjq0S5VBeGkFM,1944 -dns/rdtypes/ANY/ZONEMD.py,sha256=JQicv69EvUxh4FCT7eZSLzzU5L5brw_dSM65Um2t5lQ,2393 -dns/rdtypes/ANY/__init__.py,sha256=Pox71HfsEnGGB1PGU44pwrrmjxPLQlA-IbX6nQRoA2M,1497 -dns/rdtypes/ANY/__pycache__/AFSDB.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/AVC.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/CAA.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/CDS.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/CERT.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/CNAME.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/CSYNC.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/DLV.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/DNAME.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/DS.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/EUI48.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/EUI64.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/GPOS.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/HINFO.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/HIP.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/ISDN.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/L32.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/L64.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/LOC.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/LP.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/MX.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/NID.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/NINFO.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/NS.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/NSEC.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/NSEC3.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/OPT.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/PTR.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/RP.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/RRSIG.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/RT.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/SOA.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/SPF.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/SSHFP.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/TKEY.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/TLSA.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/TSIG.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/TXT.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/URI.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/X25.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-312.pyc,, -dns/rdtypes/ANY/__pycache__/__init__.cpython-312.pyc,, -dns/rdtypes/CH/A.py,sha256=3S3OhOkSc7_ZsZBVB4GhTS19LPrrZ-yQ8sAp957qEgI,2216 -dns/rdtypes/CH/__init__.py,sha256=GD9YeDKb9VBDo-J5rrChX1MWEGyQXuR9Htnbhg_iYLc,923 -dns/rdtypes/CH/__pycache__/A.cpython-312.pyc,, -dns/rdtypes/CH/__pycache__/__init__.cpython-312.pyc,, -dns/rdtypes/IN/A.py,sha256=FfFn3SqbpneL9Ky63COP50V2ZFxqS1ldCKJh39Enwug,1814 -dns/rdtypes/IN/AAAA.py,sha256=AxrOlYy-1TTTWeQypDKeXrDCrdHGor0EKCE4fxzSQGo,1820 -dns/rdtypes/IN/APL.py,sha256=ppyFwn0KYMdyDzphxd0BUhgTmZv0QnDMRLjzQQM793U,5097 -dns/rdtypes/IN/DHCID.py,sha256=zRUh_EOxUPVpJjWY5m7taX8q4Oz5K70785ZtKv5OTCU,1856 -dns/rdtypes/IN/HTTPS.py,sha256=P-IjwcvDQMmtoBgsDHglXF7KgLX73G6jEDqCKsnaGpQ,220 -dns/rdtypes/IN/IPSECKEY.py,sha256=RyIy9K0Yt0uJRjdr6cj5S95ELHHbl--0xV-Qq9O3QQk,3290 -dns/rdtypes/IN/KX.py,sha256=K1JwItL0n5G-YGFCjWeh0C9DyDD8G8VzicsBeQiNAv0,1013 -dns/rdtypes/IN/NAPTR.py,sha256=SaOK-0hIYImwLtb5Hqewi-e49ykJaQiLNvk8ZzNoG7Q,3750 -dns/rdtypes/IN/NSAP.py,sha256=3OUpPOSOxU8fcdi0Oe6Ex2ERXcQ-U3iNf6FftZMtNOw,2165 -dns/rdtypes/IN/NSAP_PTR.py,sha256=iTxlV6fr_Y9lqivLLncSHxEhmFqz5UEElDW3HMBtuCU,1015 -dns/rdtypes/IN/PX.py,sha256=vHDNN2rfLObuUKwpYDIvpPB482BqXlHA-ZQpQn9Sb_E,2756 -dns/rdtypes/IN/SRV.py,sha256=a0zGaUwzvih_a4Q9BViUTFs7NZaCqgl7mls3-KRVHm8,2769 -dns/rdtypes/IN/SVCB.py,sha256=HeFmi2v01F00Hott8FlvQ4R7aPxFmT7RF-gt45R5K_M,218 -dns/rdtypes/IN/WKS.py,sha256=kErSG5AO2qIuot_hkMHnQuZB1_uUzUirNdqBoCp97rk,3652 -dns/rdtypes/IN/__init__.py,sha256=HbI8aw9HWroI6SgEvl8Sx6FdkDswCCXMbSRuJy5o8LQ,1083 -dns/rdtypes/IN/__pycache__/A.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/AAAA.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/APL.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/DHCID.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/HTTPS.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/KX.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/NAPTR.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/NSAP.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/PX.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/SRV.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/SVCB.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/WKS.cpython-312.pyc,, -dns/rdtypes/IN/__pycache__/__init__.cpython-312.pyc,, -dns/rdtypes/__init__.py,sha256=NYizfGglJfhqt_GMtSSXf7YQXIEHHCiJ_Y_qaLVeiOI,1073 -dns/rdtypes/__pycache__/__init__.cpython-312.pyc,, -dns/rdtypes/__pycache__/dnskeybase.cpython-312.pyc,, -dns/rdtypes/__pycache__/dsbase.cpython-312.pyc,, -dns/rdtypes/__pycache__/euibase.cpython-312.pyc,, -dns/rdtypes/__pycache__/mxbase.cpython-312.pyc,, -dns/rdtypes/__pycache__/nsbase.cpython-312.pyc,, -dns/rdtypes/__pycache__/svcbbase.cpython-312.pyc,, -dns/rdtypes/__pycache__/tlsabase.cpython-312.pyc,, -dns/rdtypes/__pycache__/txtbase.cpython-312.pyc,, -dns/rdtypes/__pycache__/util.cpython-312.pyc,, -dns/rdtypes/dnskeybase.py,sha256=FoDllfa9Pz2j2rf45VyUUYUsIt3kjjrwDy6LxrlPb5s,2856 -dns/rdtypes/dsbase.py,sha256=I85Aps1lBsiItdqGpsNY1O8icosfPtkWjiUn1J1lLUQ,3427 -dns/rdtypes/euibase.py,sha256=umN9A3VNw1TziAVtePvUses2jWPcynxINvjgyndPCdQ,2630 -dns/rdtypes/mxbase.py,sha256=DzjbiKoAAgpqbhwMBIFGA081jR5_doqGAq-kLvy2mns,3196 -dns/rdtypes/nsbase.py,sha256=tueXVV6E8lelebOmrmoOPq47eeRvOpsxHVXH4cOFxcs,2323 -dns/rdtypes/svcbbase.py,sha256=TQRT52m8F2NpSJsHUkTFS-hrkyhcIoAodW6bBHED4CY,16674 -dns/rdtypes/tlsabase.py,sha256=pIiWem6sF4IwyyKmyqx5xg55IG0w3K9r502Yx8PdziA,2596 -dns/rdtypes/txtbase.py,sha256=K4v2ulFu0DxPjxyf_Ul7YRjfBpUO-Ay_ChnR_Wx-ywA,3601 -dns/rdtypes/util.py,sha256=6AGQ-k3mLNlx4Ep_FiDABj1WVumUUGs3zQ6X-2iISec,9003 -dns/renderer.py,sha256=5THf1iKql2JPL2sKZt2-b4zqHKfk_vlx0FEfPtMJysY,11254 -dns/resolver.py,sha256=wagpUIu8Oh12O-zk48U30A6VQQOspjfibU4Ls2So-kM,73552 -dns/reversename.py,sha256=zoqXEbMZXm6R13nXbJHgTsf6L2C6uReODj6mqSHrTiE,3828 -dns/rrset.py,sha256=J-oQPEPJuKueLLiz1FN08P-ys9fjHhPWuwpDdrL4UTQ,9170 -dns/serial.py,sha256=-t5rPW-TcJwzBMfIJo7Tl-uDtaYtpqOfCVYx9dMaDCY,3606 -dns/set.py,sha256=Lr1qhyqywoobNkj9sAfdovoFy9vBfkz2eHdTCc7sZRs,9088 -dns/tokenizer.py,sha256=Dcc3lQgEIHCVZBuO6FaKWEojtPSd3EuaUC4vQA-spnk,23583 -dns/transaction.py,sha256=ZlnDT-V4W01J3cS501GaRLVhE9t1jZdnEZxPyZ0Cvg4,22636 -dns/tsig.py,sha256=I-Y-c3WMBX11bVioy5puFly2BhlpptUz82ikahxuh1c,11413 -dns/tsigkeyring.py,sha256=Z0xZemcU3XjZ9HlxBYv2E2PSuIhaFreqLDlD7HcmZDA,2633 -dns/ttl.py,sha256=fWFkw8qfk6saTp7lAPxZOuD3U3TRxVRvIpljQnG-01I,2979 -dns/update.py,sha256=y9d6LOO8xrUaH2UrZhy3ssnx8bJEsxqTArw5V8XqBRs,12243 -dns/version.py,sha256=sRMqE5tzPhXEzz-SEvdN82pP77xF_i1iELxaJN0roDE,1926 -dns/versioned.py,sha256=3YQj8mzGmZEsjnuVJJjcWopVmDKYLhEj4hEGTLEwzco,11765 -dns/win32util.py,sha256=NEjd5RXQU2aV1WsBMoIGZmXyqqKCxS4WYq9HqFQoVig,9107 -dns/wire.py,sha256=vy0SolgECbO1UXB4dnhXhDeFKOJT29nQxXvSfKOgA5s,2830 -dns/xfr.py,sha256=FKkKO-kSpyE1vHU5mnoPIP4YxiCl5gG7E5wOgY_4GO8,13273 -dns/zone.py,sha256=lLAarSxPtpx4Sw29OQ0ifPshD4QauGu8RnPh2dEropA,52086 -dns/zonefile.py,sha256=9pgkO0pV8Js53Oq9ZKOSbpFkGS5r_orU-25tmufGP9M,27929 -dns/zonetypes.py,sha256=HrQNZxZ_gWLWI9dskix71msi9wkYK5pgrBBbPb1T74Y,690 -dnspython-2.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -dnspython-2.6.1.dist-info/METADATA,sha256=2GJFv-NqkwIytog5VQe0wPtZKoS016uyYfG76lqftto,5808 -dnspython-2.6.1.dist-info/RECORD,, -dnspython-2.6.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -dnspython-2.6.1.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87 -dnspython-2.6.1.dist-info/licenses/LICENSE,sha256=w-o_9WVLMpwZ07xfdIGvYjw93tSmFFWFSZ-EOtPXQc0,1526 diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/WHEEL deleted file mode 100644 index 5998f3a..0000000 --- a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.21.1 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/licenses/LICENSE deleted file mode 100644 index 390a726..0000000 --- a/.venv/lib/python3.12/site-packages/dnspython-2.6.1.dist-info/licenses/LICENSE +++ /dev/null @@ -1,35 +0,0 @@ -ISC License - -Copyright (C) Dnspython Contributors - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted, provided that the -above copyright notice and this permission notice appear in all -copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL -WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE -AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL -DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR -PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - - - -Copyright (C) 2001-2017 Nominum, Inc. -Copyright (C) Google Inc. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose with or without fee is hereby granted, -provided that the above copyright notice and this permission notice -appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/LICENSE.md b/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/LICENSE.md deleted file mode 100644 index 19b6b45..0000000 --- a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/LICENSE.md +++ /dev/null @@ -1,31 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2013-2024, Kim Davies and contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. 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. - -3. 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. - -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. diff --git a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/METADATA b/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/METADATA deleted file mode 100644 index b28f6ec..0000000 --- a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/METADATA +++ /dev/null @@ -1,243 +0,0 @@ -Metadata-Version: 2.1 -Name: idna -Version: 3.7 -Summary: Internationalized Domain Names in Applications (IDNA) -Author-email: Kim Davies -Requires-Python: >=3.5 -Description-Content-Type: text/x-rst -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: System Administrators -Classifier: License :: OSI Approved :: BSD License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Internet :: Name Service (DNS) -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Utilities -Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst -Project-URL: Issue tracker, https://github.com/kjd/idna/issues -Project-URL: Source, https://github.com/kjd/idna - -Internationalized Domain Names in Applications (IDNA) -===================================================== - -Support for the Internationalized Domain Names in -Applications (IDNA) protocol as specified in `RFC 5891 -`_. This is the latest version of -the protocol and is sometimes referred to as “IDNA 2008”. - -This library also provides support for Unicode Technical -Standard 46, `Unicode IDNA Compatibility Processing -`_. - -This acts as a suitable replacement for the “encodings.idna” -module that comes with the Python standard library, but which -only supports the older superseded IDNA specification (`RFC 3490 -`_). - -Basic functions are simply executed: - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('ドメイン.テスト') - b'xn--eckwd4c7c.xn--zckzah' - >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) - ドメイン.テスト - - -Installation ------------- - -This package is available for installation from PyPI: - -.. code-block:: bash - - $ python3 -m pip install idna - - -Usage ------ - -For typical usage, the ``encode`` and ``decode`` functions will take a -domain name argument and perform a conversion to A-labels or U-labels -respectively. - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('ドメイン.テスト') - b'xn--eckwd4c7c.xn--zckzah' - >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) - ドメイン.テスト - -You may use the codec encoding and decoding methods using the -``idna.codec`` module: - -.. code-block:: pycon - - >>> import idna.codec - >>> print('домен.испытание'.encode('idna2008')) - b'xn--d1acufc.xn--80akhbyknj4f' - >>> print(b'xn--d1acufc.xn--80akhbyknj4f'.decode('idna2008')) - домен.испытание - -Conversions can be applied at a per-label basis using the ``ulabel`` or -``alabel`` functions if necessary: - -.. code-block:: pycon - - >>> idna.alabel('测试') - b'xn--0zwm56d' - -Compatibility Mapping (UTS #46) -+++++++++++++++++++++++++++++++ - -As described in `RFC 5895 `_, the -IDNA specification does not normalize input from different potential -ways a user may input a domain name. This functionality, known as -a “mapping”, is considered by the specification to be a local -user-interface issue distinct from IDNA conversion functionality. - -This library provides one such mapping that was developed by the -Unicode Consortium. Known as `Unicode IDNA Compatibility Processing -`_, it provides for both a regular -mapping for typical applications, as well as a transitional mapping to -help migrate from older IDNA 2003 applications. - -For example, “Königsgäßchen” is not a permissible label as *LATIN -CAPITAL LETTER K* is not allowed (nor are capital letters in general). -UTS 46 will convert this into lower case prior to applying the IDNA -conversion. - -.. code-block:: pycon - - >>> import idna - >>> idna.encode('Königsgäßchen') - ... - idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed - >>> idna.encode('Königsgäßchen', uts46=True) - b'xn--knigsgchen-b4a3dun' - >>> print(idna.decode('xn--knigsgchen-b4a3dun')) - königsgäßchen - -Transitional processing provides conversions to help transition from -the older 2003 standard to the current standard. For example, in the -original IDNA specification, the *LATIN SMALL LETTER SHARP S* (ß) was -converted into two *LATIN SMALL LETTER S* (ss), whereas in the current -IDNA specification this conversion is not performed. - -.. code-block:: pycon - - >>> idna.encode('Königsgäßchen', uts46=True, transitional=True) - 'xn--knigsgsschen-lcb0w' - -Implementers should use transitional processing with caution, only in -rare cases where conversion from legacy labels to current labels must be -performed (i.e. IDNA implementations that pre-date 2008). For typical -applications that just need to convert labels, transitional processing -is unlikely to be beneficial and could produce unexpected incompatible -results. - -``encodings.idna`` Compatibility -++++++++++++++++++++++++++++++++ - -Function calls from the Python built-in ``encodings.idna`` module are -mapped to their IDNA 2008 equivalents using the ``idna.compat`` module. -Simply substitute the ``import`` clause in your code to refer to the new -module name. - -Exceptions ----------- - -All errors raised during the conversion following the specification -should raise an exception derived from the ``idna.IDNAError`` base -class. - -More specific exceptions that may be generated as ``idna.IDNABidiError`` -when the error reflects an illegal combination of left-to-right and -right-to-left characters in a label; ``idna.InvalidCodepoint`` when -a specific codepoint is an illegal character in an IDN label (i.e. -INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is -illegal based on its positional context (i.e. it is CONTEXTO or CONTEXTJ -but the contextual requirements are not satisfied.) - -Building and Diagnostics ------------------------- - -The IDNA and UTS 46 functionality relies upon pre-calculated lookup -tables for performance. These tables are derived from computing against -eligibility criteria in the respective standards. These tables are -computed using the command-line script ``tools/idna-data``. - -This tool will fetch relevant codepoint data from the Unicode repository -and perform the required calculations to identify eligibility. There are -three main modes: - -* ``idna-data make-libdata``. Generates ``idnadata.py`` and - ``uts46data.py``, the pre-calculated lookup tables used for IDNA and - UTS 46 conversions. Implementers who wish to track this library against - a different Unicode version may use this tool to manually generate a - different version of the ``idnadata.py`` and ``uts46data.py`` files. - -* ``idna-data make-table``. Generate a table of the IDNA disposition - (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix - B.1 of RFC 5892 and the pre-computed tables published by `IANA - `_. - -* ``idna-data U+0061``. Prints debugging output on the various - properties associated with an individual Unicode codepoint (in this - case, U+0061), that are used to assess the IDNA and UTS 46 status of a - codepoint. This is helpful in debugging or analysis. - -The tool accepts a number of arguments, described using ``idna-data --h``. Most notably, the ``--version`` argument allows the specification -of the version of Unicode to be used in computing the table data. For -example, ``idna-data --version 9.0.0 make-libdata`` will generate -library data against Unicode 9.0.0. - - -Additional Notes ----------------- - -* **Packages**. The latest tagged release version is published in the - `Python Package Index `_. - -* **Version support**. This library supports Python 3.5 and higher. - As this library serves as a low-level toolkit for a variety of - applications, many of which strive for broad compatibility with older - Python versions, there is no rush to remove older interpreter support. - Removing support for older versions should be well justified in that the - maintenance burden has become too high. - -* **Python 2**. Python 2 is supported by version 2.x of this library. - While active development of the version 2.x series has ended, notable - issues being corrected may be backported to 2.x. Use "idna<3" in your - requirements file if you need this library for a Python 2 application. - -* **Testing**. The library has a test suite based on each rule of the - IDNA specification, as well as tests that are provided as part of the - Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing - `_. - -* **Emoji**. It is an occasional request to support emoji domains in - this library. Encoding of symbols like emoji is expressly prohibited by - the technical standard IDNA 2008 and emoji domains are broadly phased - out across the domain industry due to associated security risks. For - now, applications that need to support these non-compliant labels - may wish to consider trying the encode/decode operation in this library - first, and then falling back to using `encodings.idna`. See `the Github - project `_ for more discussion. - diff --git a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/RECORD b/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/RECORD deleted file mode 100644 index 16e596b..0000000 --- a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/RECORD +++ /dev/null @@ -1,22 +0,0 @@ -idna-3.7.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -idna-3.7.dist-info/LICENSE.md,sha256=pZ8LDvNjWHQQmkRhykT_enDVBpboFHZ7-vch1Mmw2w8,1541 -idna-3.7.dist-info/METADATA,sha256=OixCk-dKLZkPy-MfviOmiPvwJ1O2K_8rqCrFjC_uxy4,9888 -idna-3.7.dist-info/RECORD,, -idna-3.7.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81 -idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849 -idna/__pycache__/__init__.cpython-312.pyc,, -idna/__pycache__/codec.cpython-312.pyc,, -idna/__pycache__/compat.cpython-312.pyc,, -idna/__pycache__/core.cpython-312.pyc,, -idna/__pycache__/idnadata.cpython-312.pyc,, -idna/__pycache__/intranges.cpython-312.pyc,, -idna/__pycache__/package_data.cpython-312.pyc,, -idna/__pycache__/uts46data.cpython-312.pyc,, -idna/codec.py,sha256=PS6m-XmdST7Wj7J7ulRMakPDt5EBJyYrT3CPtjh-7t4,3426 -idna/compat.py,sha256=0_sOEUMT4CVw9doD3vyRhX80X19PwqFoUBs7gWsFME4,321 -idna/core.py,sha256=lyhpoe2vulEaB_65xhXmoKgO-xUqFDvcwxu5hpNNO4E,12663 -idna/idnadata.py,sha256=dqRwytzkjIHMBa2R1lYvHDwACenZPt8eGVu1Y8UBE-E,78320 -idna/intranges.py,sha256=YBr4fRYuWH7kTKS2tXlFjM24ZF1Pdvcir-aywniInqg,1881 -idna/package_data.py,sha256=Tkt0KnIeyIlnHddOaz9WSkkislNgokJAuE-p5GorMqo,21 -idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -idna/uts46data.py,sha256=1KuksWqLuccPXm2uyRVkhfiFLNIhM_H2m4azCcnOqEU,206503 diff --git a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/WHEEL deleted file mode 100644 index 3b5e64b..0000000 --- a/.venv/lib/python3.12/site-packages/idna-3.7.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.9.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/idna/__init__.py b/.venv/lib/python3.12/site-packages/idna/__init__.py deleted file mode 100644 index a40eeaf..0000000 --- a/.venv/lib/python3.12/site-packages/idna/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -from .package_data import __version__ -from .core import ( - IDNABidiError, - IDNAError, - InvalidCodepoint, - InvalidCodepointContext, - alabel, - check_bidi, - check_hyphen_ok, - check_initial_combiner, - check_label, - check_nfc, - decode, - encode, - ulabel, - uts46_remap, - valid_contextj, - valid_contexto, - valid_label_length, - valid_string_length, -) -from .intranges import intranges_contain - -__all__ = [ - "IDNABidiError", - "IDNAError", - "InvalidCodepoint", - "InvalidCodepointContext", - "alabel", - "check_bidi", - "check_hyphen_ok", - "check_initial_combiner", - "check_label", - "check_nfc", - "decode", - "encode", - "intranges_contain", - "ulabel", - "uts46_remap", - "valid_contextj", - "valid_contexto", - "valid_label_length", - "valid_string_length", -] diff --git a/.venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/idna/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index e9388612c999d2b36eca97247212e86d5861f540..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 868 zcmbu7zmL-}6vyo}O`E2_)9W!HHY74!qXWc*kl7-w@{0_$mMO+<{t8R2X%NCb-9mx-a|bepn&&LpAXQ057Cg1(1?%G zm`~7zPtlZzDC9FVtMyv>5uc+u1Z$AGA0Rs3wJvfDNRgj?NQNux6PJdTa^zVVMRG9^sp|` zh}DI>x#3GaZT{CMaZbg$%5>Po%1SY@J_OChOiYW4h&3&1Il@E~D%V_^YFl^~9gD7o zZ(#?n0*k)Iz+z}IvY1btu2FX|CV7tWM{sGzDul diff --git a/.venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc b/.venv/lib/python3.12/site-packages/idna/__pycache__/codec.cpython-312.pyc deleted file mode 100644 index 2a6e9843fda40f8cf3688f78223c1b70445c30b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4973 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 diff --git a/.venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc b/.venv/lib/python3.12/site-packages/idna/__pycache__/core.cpython-312.pyc deleted file mode 100644 index e5b0af2e40ca43942d8158c03572652260bfc8b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15835 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 Df|SU) diff --git a/.venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc b/.venv/lib/python3.12/site-packages/idna/__pycache__/idnadata.cpython-312.pyc deleted file mode 100644 index 1ba232045cb79bccd7f158c86155e0185c970767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99463 zcmYh^37nO4|NrsRbec2M9;qbld(%GcQ%yDP(>{_)l1tj8WlAd(*+U2+Ns=gpBne4K zLK2dYgb+fv+YY36HzM4xT9E<87vtr6)YVr z6D%7n7c3vF5Iik-daz>fj9{f;cwul{@SMcu8F4w1!o5@56%hB4bBVB4=xBU3|@iZD_t4pqTr(7Rl%!+i-Xq$ zuMJ)oygqnC@W$Xx!6m_)gSP~4#qgDuhWWPO?ZG>O%Yt_X?+V@>Tpqk9cyI8&;ELe= z!Ii-Wf~zomr3b^jI`~jb-_o2j|JBUHv}IKJ`sE}xH0$?hOhK=n4bx5 z3O*a$9DFXgCHQ=BYw(5Ow&07wmx3=x+wJGgE71<~)!3#m zp}!T}6MQ@PPVn7uo4vvJg8PE+hmYh2M-262_6c58vHEydGK)b zg{}Qa=wAkp2EPh^9sCBvS2`BvZ^K%?3;p}x@$hK;5d1OtQ}9Ia=io2FUxO!u{|WvU z{BQ7g3}5MwF#j3+D|jmSckrL!zqqtmlu#C9EjFhqi|crj_1f_g%5-H)nW4-i`RaDt zlDdyl%F@a*%CgFG%JRwz%F~pmD=R9`P*ze_R#s7-sjT|{t>rAOubT2~Wp!l@Wld!* zWo>00WnE=GWqoA>WkY2nWn*O%QjbfP*4I?oOxax7LfKN;O4(YOt!$%gt8Axiuk4`g zsO+T7A+?syI@U$mRhg^ortGflq0CeERQ6K#R`yZ$RrXW%SLQ1RkXp+?9UG(^tQ?{o zs>gDe<^tt8IzC)^u5yI(JmpB`DCKD7`N}cepVo4Lj*ZpVxKQ&r%@-*n9luyPUKyVG z?7xBV+-Dh{{Vc=tpJjLkv<%OImf=~@GCT)bhG#*oB|I0}vG8nY8J-U;dxSPS)|Vi&zE*zSL-&5mDeb* z)$OiRUa!1Cd86_s-F}JkX5}r)TXnmonr|bumfLmg4&^fCoyxnEcPp1G@6mnUtNA|7 zD>UD)d8OtDl&f?+Jk#2rJv`THE#Vp0j)mu3%kZpg8J>47!!xgCc|_U*b&%mzt?=7)(F2OSF6D-3{!7}U>w3e`Auw!ArVEL!M#$U=)%D7V0!hqS1a=F-YC%Cbt|JETSB zHCIsj-XSeIU2{dH?;X-2-#es5zIRBAeD9EE|0?~zwN%yToE7#!X;C%h*~;q58p@i= zTFTnWI?B526YX)Tr@6kefwG~pk+LzV$EAsmWht8~n<<+sTPRy9TPa&Bvz2X>ZI$hm z?Ufyr9hIH3fH?$oCFu(VaSem-24i*K*}O%6pafDShvd7Wv*GEn2DL4=7hDA0)Mw)jIZ& za*gs~o>8?~tawLz?yuY1%uaY44Ecdk3v0>>uoV;CqKO?H$sz zcSzITAx(RSH0>SIw0B6;-XYEQ4*oq2|H8NX3;)Kqd|Ai0D_>FWP`*lPEjxAWHRbEd zHTzO8&m`L1%W@;&7~<@?J0q}K9*j(w>7NO?f{vGSnu6XhZ0r^?Ti zpDPb5zfc}geyKdF{EF0CzSgmCl*g3cD!)^HuRN~&LHVQ7_YP^=JEUpvkfyyun)VK9 z+B>9a?~vwu2dyRSAME?;dxtdb9nx&?;NSmnzQH~o&Nx{9t>gbF{|#AO=N*dM-ht06 zZs#35iz}1L63TRCiVSP9Tk2S*vZS(aqS(7M`!DJb!82u?;VOqwKUgO)=}0~`re^<wWk?$RfN4|F`9)-Pw z))Mv)_L%zKp?KtbhvHFF-IwnjibuY8C?5IVp?KtbhvJd%9g0W3cPJiZ>+^i?P(1Rz zL-EM>4#gwiI~0$6?@&Andk3whlRh^`*;&~|*;SdV?56Cl?4it4_Eh##_Ez>$_Eq*% z_9wNLd>tF09H<IzB>qo^qselyWqwwVbbGW0V&t z$0{#Wj#FNwjFcBE$15*UPEbx%PEt-*PElS;YAsWB>@wvv<#go?4 zEY-2wl(#GIP%cy6sk}>hw{p4i9_78t`;;q`_bXQ_A5g9$wU!5UY_;+sS8h-~u6#oIq;jM3DN<{BTF0JIZc;w0+^l>~xkdTBa;x$MJu%9oTc zE4M3OQSKnMmREIbr}8!B>&iEjyOeJ#cPrmg?oqz2d`J1Na4xqpz;&tA?2sa&y=4l4=cY=9#MX&JW6UUU+LJ_%5Rj%l;0}9Q+}^JuKYpy zqw*)^3FXhqUzEQpPb&XIYAwI%*ngG3EB{dbsr*ZMO8K|)ALYLxliE8ZwRcEr?~t_f z4)!&dChe-WX9;CE?-16Kw5#0wTikA;k7X)LDoZK-yhAeb^A5?#&pRX|Kkty#d52`= z=N*!fpLa;=yhAeb^A5?#&pRY_-XZDd9kdod_mK2+54I*h?~shn3j6t_?H%mj40il% zWp!l@Wld!*Wo>00WnE=GWqoA>WkXVHX{2L~l}(gc%BIR@%I3-z%9hGj%GSzkWgBH% zWjkeiWd~Ag>8N9!lz!eJ8TomKWaQ@^l98WxNJf6%A!+9w{PF6p^z#nMC{MGWcSuIP zH2Zmnq|Q4eb>1QA=N+^bKlhM~^7T1>-XR(Jd52^)NXPxWL(Phbaq`=O~9O z{k%gm8llPjo>+kq@9{%aqfU)0H!nGnIwPSxP_ekhE(S{U9<4x^OXyf3rVfz z3LU#rS)^Q~yh?esamn9g>mn9g>mn9g=q5!GA{i-XR(J-XR(J-XR(J-XR(J-XR(J-XR(J-XR(J z-XUq{9sGWL?~shb-a%^#`v+T#?;VnU-NI(yJ0x|@BB^T@NnNu@>Y7DT*DR8{W|7o2 zi=?huBz4UqscRNVop(s;yhGB@J7_Io|6pI+_YO(FZeg?U9g;flkkomHq|Q4eb>1PV z^A1U!cS!2ILsI7*l9BHnk~;5@)Om+wvtz0SZMC#@yyAM9)U-XR(J-XR(J-XR(J z-XUq%Ec~(cy+bncy+bncy+bncy+bncy+bncy+bncy+bncy+bncy+bncy+bkzdk3v0 z>>q3`zIRAQzIRC4H4FdRzIRC4H4C48?~t@>7C!slAsPAJA!*kv{J8HOl9BHnl9BHn zl9BHnlJ=?vzg^fnXf0v?U~BQcL(;BW_-%aekc@oqkc@oqkc@oqkhE(SemmbgBqQHD zBqQHDBqQHDBz4{)sq+p=op(s;yhGB@J7_Io|6ptJy+hKkTiEP-hosIsBz4{)8TsBJ zsq+p=op(q^zIRAQzIQ00y+aA@9ZG2LP(pi$652bIu-7}-pDC=xULhC$d?ox9a!c(a zntkt3!d~ydpTC4%v+yjfETb%|ET=55te`widAhQq@(g7qQfsNKV^x%ADyu5bQdU!* zt*oxBp{%K_rL3*2qpYi}r>w7RKx!=wb*z!Hv9gIWOW9P}Oxax7LfKN;O4(YOt!$%g zt8AxiPiie4bgZMYlQKuyS=mL|Rhg^ortGflq0CeERQ6K#R`yZ$CAF4*I@VvAuN)P@bb4t~^&cLV2EYB&oHG(y`IX^Oa+i7bwSueQ}BCLe1lp7bzp< z#me!@OOz9o6P1&clS!>*ijG~XoT|J`IZZiTIYT*9S*V<)oUOcEIY&8HIZruXxq#GK z7V6j)$}5#c%0&x_UYLB%Kgd@lpltDYze*Ip@d%V zP(rVFC}DdC_S7Y!Pn3t0pDI67ey%*M{6cwz)LOpOv7^eblwT{qQ65u%tNc#+z4Ey7 z2j!2-pOhz*KP!Jx{z_^sCw1&U%HNd#RsOF0L;0ujFXbua-^zcK|AtK0>mAbddWUqq zRx(|$cSyIrL%f!B+duFar`z7aGo{S1W0~P2CBt_sUAu<#sI=xX%CgFG%JRwz%F~pm zD=R9`P*x(tBa?1t9jx0|uA)3sSyg$KvYPU2Wp!l@Wld!*Wo>00WnE=GWqoA>Qfq0b zV~v!Jl}(gc%BIR@%I3-z%9hGj%GSzkWgBH%WjkeiQfuj;V;z;9lsU@I$}Y;T%3NhP zWp`x{WuCI9vX`>AvX8PaskQXevHr?@mw^B4wn!SUFyKiE@H+qH>aQGO4vp(XmUFQ?oZdEQ--ln`=KXL9*E>qsAyi0j^c*R4yopG>NOW4l{f0bmqopJE_ zzW@EiTA?HUT8DIdtwa3!igY{g;J5bIN~YU62miUZTKSN2jq>6D-RoK%c|^HR>92N3 zx3dm@PwVv+HfVla`GoSx|8F%Lb=#-(SUjzKMz`Ihd{((x`J6s)i{|GwZ`J&QW`Ct* zx}A0KKlMxc_{+-eq`ptD=r%j_u~(Hlb^JBWuWNooxl70Wm6GXp*1^BlTgpAkx0Ubc z_U|h9{_hcfk0V;mK7I6k<$mP{O5Z!AYwwV*y+gY84(ZxEq-*bxZs#5RyW@L@biGzG z-OfAsao;l%ZoZEK>G*2YcMY-`$kG z=D{C_hRR0D#>ytjEM-$=Gi7sS3uQ}XE2ZxsQhL2ZD)JpfO0RcF+3Owb>uW9T^*Q0X zg*^@(_2&w&m$W%Yb7$SIi{`G%TpjPG^w&zJq8^&_ls%Qb^m)CNeK@YQ^wqI`x@~{` zm!Gn$7XDrFJw(c`TKMdHh*aczh?HHm@Z-LRNZG3;efB*>D)K!yey;{vx zDZSGorFUAS^iGSE-f5B2J1tUrr$x%HTKL++9ztsg`w075zK2LfzK2LfzK2LfzK2NZ z)siW_S~8_qOQ!T{$&|fX((l{%5UI%b5UI%b5Gh@?NJYMfNa?CYDhhiDttIRuY%RWr zNJYMfNa?CYN>?pXx@wWqRg09aTBPjNlD>w!ly@taEALU>tGrLSLU})_wXD>!2b8On z4=PtHA5yMSKCE1;d_=iU`KaSgHNUh~b9owk%{XK9m-dgJC&~~Ust}N z+@*X|xm)>`a*y(DEJIYc>B>3fHa$oCEz+B;-u?~tLrLx%4i zw3hSqxg(XMl%tjBE5|4=P>xkzs2r!fNEsq%Ua2fnE>d2lyjr!EkXpsY{8Rat@|5y# z2W&&QMlTR#sL~o~f*=JWE+kdA72;vWBvzvX-*8 zvW~JYskPM8vHHpe%7)5D%Ernj$}DA5Wiw@SWea6XWh-TCWwx>nskOA#v3AP#$_~no z%1+80WoKmewXZ zWaSj)rOK(w%aqfU)0H!nGnIwPS<2bU%awDKb4jgbo{r5|E>JF1UZK2FS)^Q~yh?es zapdmnv^l-mbhuxlDPd@-F4w%H_&?l=mv{ zBej+lI(ENurSbvgD&>R9)yjvIYm^Tw*D4=Tu2Vj$d`!7sxq;ML9@nualus%*DxXq5 zt$aqgN%^dDv+_CR7UlEGt;!da+mtVoTFXm1_Of!j@)hL{<*Uk_%GZ>yE8kG=QogC& zt$a(lNBOq$9a3v~SI71$-&5{WzOUS`{6P7k@+0K|<;Ti{N`J*ermk9K>Nh-O>Z(Pi zU$xL${K`e9u3TjL?|HK4Hot0-sjC*5x@wWBs}`BMYLTg{7Mb>HNzPhl>Z(Pie#1ki zu3BX3szs)~TGDUlS1mH_)spcU-b`J&$h22X`fb9M3%gH$#Y3iDweaKqiib?QYT>iL z;vv(nTKMd*c*wM?7C!qc9x`>+BGXBs%5MM=Hlp`>562#-riUAZW!D;FjG%Ei)> zx@u8US1n5FszpiLJ8+*R^&1{a>bFCc)NgnwX?q9%xL>s>sdrkG)H^Lo>YWxP^-hbD z{!R<6<#c^+MdcaFO3KR0D#|mJRh4Hct0~V``c;dPI`2?2su}+Nl+?R4OGdRd*CDl* zx;j=*Szp;e*-+U?*;v^`nWb#1Y^H3kY@uwaY^7|i%qF##HagZ;*-qJB*+JP+*-4qB z?5ymf?5fOFc2jm&_E6?2dy-m9FCFWx?4#_f?5FIn%vTOj4pa_O4pt6P4pk0Q7AVhA z4kxvib9HQl@;v28>Qn zyjFRg@_OYBq}Fnyj@_hOqP$soi}F_GQsr&R+m&}HmnrX5-le=-xmAiTKSN2jq+jTTID0kb;?JTk15xaTFV9zLtsADfFUsi5czM|Zrd{wzq`I_=|At!q}KAejvZEhp**7e zQh8MQmGW!lH_BtmZdG3*n#x+r+N9P}N5|?a>nZCi8z>tp8z~zrn<%rCO_j}*&6O>bEtRd5tx2sV zTgTcc+bY{B+bcULJ1RRVbCjKxU6fswxyo+J?#dp@JW^}vsbjsAy_J2GeU<%`{gwI3 z0m^~OLCV3(ASb9H|_o9IZTGIYxPba;)+~E;cgF{@a>k|C?+7 zn`i%_lRDVW{J*7S0NF zGDp2`w|Z)JI9jQOt#m=C)vb>8+RJK@*MU}REu_D z*DI_p@cOLP#1-MwUbA|@>wc?Mt_(*HSsmf^gw;Z?|FOEu>%UeHTsg)5goy^V>`##_ zmI%Z6CdCrVnwIe4)?#UiM2QR^9<-q-T|=*-w)#G06OAf`&$_pn-A_l|#Yr2c#KVWp z?ISt*NP3GzqGGOw3>&J&Lt`5j$3u<{hiy2{>b4Is2J3SYHH+CdD$N%(b)F4T%7=qC zY@ev%=w)e%>${fnJNU(h?eTE9ehIs~^8S&I?QE!ah7Z--+i<>y)dNc;ipFaAVseSZ z_KubOW@Dz>(7dvL(dzT;bH-NokMuvCUyfhJhmCd86S)JbBpPLgZ^tgXU0$W*e8<1H znp-&>{g2gcmBRzrZho;u;S?>uvkiyip{otQ$3u4;5|`>`J#8o*4})!}Vnd_!@TlEw z4`H&K9<^0A6n588vt{u_GPjH`v$YMo@-z&yA-884|DzR~!8f>EqEXp!e}D1)iSHuO zGLbmhL*Ji7EBhYx)}8-rLvDW!BU;-ZH($fFD~lzTFVxCc+OX4xaMvfB7q{<^)^N&( z{qgXx4JT~~k5I7|wjf(~80y$ki{f<;x1qq+9qw&{4NGlkRN`QX-ZL(~c=EyGeJ-4O zFul*k<7QlZ`N51n6K7m_$%RudJebk{!omwDTl@BLTjsX~#>9kuiHXG^4T?h&NtO>t4o|?7uo0eur{Njc z1kb`|cn-F}^RN|OfNk(1ybQ0v4%i8=!5dKCekM+=0H*;zA}3aaGoTVwhAMC-RE26# z)A%-z|1N9OhSKcEs{NIRdQcx4KtpH*jiCu-K~rc3&7lRfgjUcRvY`#Mg?7*$IzUJ0 z1Ub+dxz!9L|Li za2||=Q7{_LhcR#gOohu}8cc^7FcW6MY`7fez+9LI^I-ujge%}mD1t?B6+yb}4Qn(FnhdW>y+zEHV-LM?)fqUUTSONFLN_YTP!Go|G9)dOS zFsy|~U>!UPkHLD_0FT2H@FZ-6r{HOL1~$R7uo<3%E$}>Sg%@BOya+GB%dj0@fgSKF z?1b0gb$A1I!JDuf-hw^wHoODx!d`d}_QCt`0elD_!2$Rf4#FpJ2tJ3y@C6)!FX1SB z1IOSy_#TeK5AY-W1b@O`a0>o~aw(%cRDjdqbf^etKqaUQRp3mh3THtzI2)=%4X6pV zpf=Qjx=;`5Lj!0Cji50!fh=eW&7e87fR@k-T0=Ipfws^N+CvBE2%R7YIzt!e3c1h? zx2-57zPD!4h)BLVFa89BViPbhVx+zTmWO?LKp`Z zfo-4d*$>9UB`^VOD{aqyFd1wEYR`T!6)uBmFdb&VOelm|FdHt1IWQOI!F*T%wga>u zC~zgLg-2i=JPMD&de{Ju!xQi%Y=qtL7VLqy;T?Dv_QHFx58j9U@Bw@XAHf0m7!JZG za0oty&){=73}3(z_!5r7SMW7_1IOT7_zu2@kyI2)=%4X6pVpf=QjdQcx4KqF`jO&|-JLN0WJ?$87BpeOW#-p~j7LO1(w4-a4*~kE8u=u2@k+3co0^@ zL$C%OhPChrtb<44F<1{9;0bsVHo{ZzG&}>F;91xV&%qXW9=5^@unk^>m*8dC4zIut zcolZSYw$X}0lQ!~yajvUZFmRXg}v||?1T4VKYRcm!bflbK8Azv2^@k?;WPLg4#OAl zB^-sX;A{8>j={I^9efYR;RpB;eu5M5GyDR-!b$iK{09Gp-{BAV6aIoz@HhMe|3V_u zCgL5%?1R zfnxUm+HYbSeb!ox`v%1moZ$SOmLa zAG{Cy;X`Oy*2so7&=%T3d*}cip*PqWI{R@A{h&YO!vGivPr)X57B<6kumzrnt?&YD zgI(|@?1r~s54;WUK$UXFna~E>K?j%)GhikZ!Yr5#m%|*G3-e$;EP#b@1r)&|xC*X@ z#c(ZL2RFkla4Rf@+u(M%1D3&^a2MPS_rSeyAFP1;VI@2OtKdOc4G+N@co^2gBd`u0 zg~wn$Y=Fn%33w7V!c*`xJOi8HS=bEE!4`NPw!#ar4PJ+)<&9?099lptXbsuW2HHY9 z=l~s|GjxFwa2||-3t%ivhZ!&v3gIfa2Cjvh;eI&(bYl!$0H;+pPKS!n3R=TZ7zQ(7 zCTxOd;W^j>&%;)D0k*-5@H)H+yWuU^18>7S@Gk6y_h28q51&G+icu0uL1`!h<)9|i zg4$3A>OwuJ4-KFpG=j#^1hSwhG=t{Q0$M^VXbsuW2HHY9Xb&BrBXois=nP$;E962q z=ng#~4|+l`=nZ|KFZ6@{kPmi(+I|YaAQ%ioU?>cO0yqbT!?`d5&V!LK3P!{EFa|Dw zv2Y=bgNq=7i(x!m0ux{&OoGWU1uliDa2ZU4=`aIkLLtn8*>E|`fotGexDKv|8{kH` z36{Xka0}cDOW`)S9qxc-a3|aacf)eH2kwRYU|?}fpzdG zJO(w-G-^R@XaEhN6?A}(&m#$FTu;O9bSPQ z@G9(t*Wh({19riiup8cjJ@7WX1BtVYVvq*KAsgC4J7^Cbpd;i!cbE)Q;8K_hm%%ic z4l`gT6u}}`4G+N@co^2fqp%(}z~k^W9D{G+2lxq2z|W9X&1eeEpgFXFme2}XLtAJE zd*MCU2m9dz_z*sV1Mo2%giqiQdA@~$NgU{hGd;v$`OE?N&!PoE&9D{G+JNO=s!w>Ky`~)XL%c8;0aAi~DFnj?= z;7d3PU%}V#4IG1S;XC*qj>8Y|Bm4v>;Ai*+eub0pANURa3%|o3@F)BQr{Hh+2mS>v zcu#BsSjfqUVywZ=4<4l|$-X2EQ@9Ol4Wm^n_l}8~Q+B=m-5F9|pic7zBf12n>Z`Pypw^a5xu6znq5h*r184}1pfNOoENBY7p%3(he$XHCVE_z-K`f49~$9cpkRFi|`dRddz4HO&|-JLNjOyt)LCGg&vR(3t%HW4bQ+Pcn-F} zOYkzh0>|M8_zk{VZybXkp!#1%;vb_Jq(N~=LJ3HR6l6dql!Q`H8p=RfC){5t5pIGd za5LNjx584m4IYKZU_ESr$KeS`lrf4y8We{llz?$pNa3)lRv!EKB4b`Cr)P!148|pw^s0a0-0jw`;Y=FPvANUtqlrvgFD`*Yb z&<5H8-&VGs<4Autq%K>E|`fw?db=EDM52v@+BPy~zMD!3XJ!!@uGo`z>&6Fdu>VGBGDTj2%R z1~0-(@G`stJK$B=32(w~cngY^H`+r7=m?!42RcI+=nA>e4Z1@Q$b+8H3#P$LD1?!c zJPsS-DR>&5flcr%JO^i2HmXAns10?XF4Tht&=49yV`u`+s~9byCA5OpkPU61EwqCU z&=ESFY2-i`=mGoT1Naa=f`jl09EM_5jWj3@Nhks7kb(@zgp$x2vY`#Mg?7*$IzbL} zhAz+>hQLr51_f{q42SbzB#eR^;U-uDH^VJ(D=dZE;C8qJmcgBH7c7T+phY#KCA5Op zkPU61EwqOY&=ER84s?dDa68-q%ivD93+{&Ha1Y!I_rVIdA6CKxunHc8)$kCkfrnu& zJOYowde{Ju!xQi%Y=o!b8F&^pL(8*`R?r%mZil0A|2AVKtbzw&H9Q1~ca36@2E`!>B_JJAkO7%c5=ud7COp;I01crLG=?US1x=v^w1igB8nU4c zw1sxi9y&lr=ma^?8M;7M$c1ju9eO|>^n_k;$$Q2GmKLtdO#lZgkI1a`aoak2mK)*2EafV z1cPA+425A(0O!DPI2T61c`y=2!Du)i#=r$I7A}Nwa1lgsF^q>xU;<2pNiZ3vz${n{ z_rVIdA6CKx@F1*)hhPmn3~S*LSO<^7W3V1Jz~k@)JPC=zMlnc(;*f+AkPaD;2_>Nv zl!h`;7Ro_+r~s$I=}-~QfJ#sqs=!%L4bFz@Py=d0EvN%^p*}Q#hR_Ha!>!Qb3!^2p zg4U1?ZJ;eYal|+dKfsUhQ#f{%WAG#V1bdGe@4-HJANIos@F66=HHtwR6o({~fOJSf z24q4>CdAJgfpNLRE8>WCRBy9pcOftn2lb%= zG=xUb7@9y9G=*l+99lq2Xa%hy8`?lyXb0_~19XH=kOQ5e3v`8C=my=P2joFd=mou@ z5A=n8&>xB=%Gw_gYC&zN19hPu)Q1Mp5E?;aXaZT#6q-SEXaOyu6|{zIXajAb9khoI z&=ER84s?bt&=qo_8+3;rkOw`X7xacc&=>kaf5?XcFc1d8U>E{JVHgy^IWQc~g%NNb zjD%4z8qS9?Z~=^k3t=2w1QA>eZz^888zHVHV7W%V7@8 zg?TU^7QjNd0uMK}X0L1m}{ zXF^pt3#!4{P#tPOO{fL6p$^oAdQcx4KtpH*jiCu-K~rc3&7lRfgjUcRvY`#Mg?7*$ zIzUJ01Ub+dxKLtdO#lZgkI1a`aoak2mK)*2EafV1cPA+425A(0O!DPI2T61 zc`y=2!Du)i#=r$I7A}Nwa1lgsF^q>xU;<2pNiZ3vz@;!1E`w<>9cI8xD1=!s8!m@A zFc;>*d{_Vr;R?7CieM331y{pjxCX9;>)?900d9nwU1wR5*~n6@F1*)hhPmn3~S*LSO<^7W3V1Jz~k@)JP8}&DR>&5flcr% zY=-Ax3p@{7;RV`twYBjGV zt=9Eg+G>_pJK3Gc_G;%g6FFY(WM3lBYelR1UhVu;qQI-2H%g55TGeXgwVKr_UhSXw zi9)Y6tuF9d+v;Mkb*(P(THoq2uMMrP@Y>kw8n0PaH+XGkb(7Z?R=0U=Wp$_5wpRCh z?O^qg*G^WCdF^cVgx9WCfA`wMYDS6hNcOZ^-mASMH&MlFU#m5}+PiTRSzZTP&GtIj zYL3^TR`a|TSk3o3+-iZ>5mrZg9ceZ4I@;N2kr ztgi5CXCD%4yiT#Y!Ru73o4ii5y3Okht2@0GTHWJyw$=S!=U6@DmG@2`^SaRL39naL z{oU&#tBLgRNG`To+UvDeD|)@bYE7@VSZ(3;4y%2<-etAG>k6xp*9WZ5@w(dTVy|ng zF7dk7>N2m7T3zLJgVl9jpRl^g>r+;@dwtgGUa!wtJ>b>Ot|X3n-D<S35P3XrBsy zHv9K@BG>EtR{MDU!0KSHA6XsY^<%4Jy?$bKg4fTiPV;)$>H@DvtSnW?pz5ZkMq}PP~!h%y?)2t>l z{3ndn(q7Z8R`i--wVKzGR_l5#Z8ghlS*zJz%UjLydYaWduNAH4d#z-(z-tw&qrFzO z8hNc|b&A*ORtvq>w7S4+ZL5pD*0s9CYkjNByf(DD!fRuzYrJMz-QcyE)lFVoSl#Be zmDQbIJ6hf2wX@X|Ub|U6<+X>^4F8n{eXLgV+RtiTulZK9ybiRQ?RBHo9Ird99xWLj z^?g>WlnV6+s~f%kWOa*I-Y%Xf9d5u|#WVc(cJR*cDqg2q&GpK!BiQMcUjnknE58Kf zkXL>Q$Z@Z?SmjRCynbf2(CcBV3%nk&y4dSct4q9o zZFQN~V^&vq{m$wdug9%!@cN_GON2krtgi4n$?6)f{HlZvUZ+~!K?DNt?u_a$Lb-k z^Q<2Ay1?pjuUA+->9xq}DX&*qO`aCk%RAvqdtG8R%j+#xv%N00n&b6$t9f3RSEV%dL*~dau>U>k6w=ysorb=yjFV1zuNMUF>y@)g@lnT3zOKoz)dyAG5l~>jtYE zygp%dlh=(_w|Qj`wA1S*t9!g|wz}Wz7ORK6Znb*U>o%*$y}o4iq}T0MPkDXSYR2i| z;d{+$d9QC+t>X1ft2Mp8WwoK#x2?ADy4PxZulua#dEIX{-|L4~3%nk%I@;?&tC813 zR;PIV%xa<6!&Vn~Jz{mS*P~XKc>UVyGOx$1uJHPu)iqxEB^?{Q{%CcR*ArH^dHu!e zPOm4e?(zDY)%{+7w|dCypH`20J!SQ{*MF>@^qR2WL2$}zn$={*@SrBGmiC%%wW8My ztJS=gv|86|X{%XY%UaF$THb1o*VC-#d97$Q-)kkS1zxLI9qqNM)yQi#t5dvIw_516 zrqu;rYg=9HwXW4AUitMk%e*$Uy25K?t82VwS>52Znbl2RTUg!Zb)?nkjPN*IXSK-d z4OVMb3P)eEn&)-9)l*(ywVL!_Tl2cr@?LjYtyXzP8T&aNKKECf?Kj%k53$5q>Gngo zN~pP3i@c7tnmjWcon>{4*Zo$Hdi~UD!CB!3-&$SZ^@P=RUK94d*kfM%Tg|N&Zu_*= z?6X7t&1#}XsQ*|!>9tvg9jzITj;7WMb+XkWuh(1M?)6ryb!&$k9I-mq>xfMIF4PG} zBdbOALY-!Hx!1*3cY3|qYQ_5D2De$w@%pIMe6LSgUFY=~tGm2zwR*toYgSKqebZ{S z2I0=$wVLJifYs4nk6JDA`is@&UjMdw!t1|QfA^X!Y2Sc`;m%4~t?0Fa)f}<mb=d zX?2~~)>e0UZEJPE*N#>Xd+luXgx4NcfA`wgYPCk;s|~c8<#njlJg?_lo#u76)#Y9n zTTM0&pLUDYie7KGn&b6RtNC7^vbxUeCab%=Zne7K>+4oec-?KaT9a_U?^w<9`kvKX zuScyGc|Bouxz|J~`|Ig7X*HP@?xBp;ie4*N&GFjEYQEQ&R@ZsWwz|t}N2~k2_OyD! zYhSC?n)(A}HOuP=s|8*os|&nNv%1{t468f6F1DJ{EZoCQRx5hF&1!qEcUkS@^**bS z*ZZwb^Sa9F9Ip>sUE+1U)m2`%THWIHRja>yebZ`{=Ki2sZRqtqtGQkeSRL*4gw^F< z%apeN3R{Fvt6;Ug*G5+Rc(f>zc->}okJp#1p7i>L)kM4SX>VC===B4u zIbJ`qI>PH=t7E;Mu)5sqNvpfOwk~UHXdmvZlhwUm2U$&Y2uDX+&GmXUx9u2?USqYq z*PEhE6rSS{Z<-0uLZb-fO?+TQEA zR`b1%wmR19M5~2fFSWYPEAOn`>6N$E?)A#sWsiBi#%e~F@YQ%DY!$D|tq%6eJ6_PxYxolV%G+a>8|q4{x!pqji`pa9LFt`-Xb1)m*RVSsm+jjMWKVZ?IbA^%koQ`-M+?%4)9H%~p4Lec9?^ zudi55^bfav-D-x{kF3`8TEX7FI@W6?t9|mrZAWp`>sYIW1H#dzRucn5{mp8T*Gnte zU)sUpXpz-z=Y@J7M@NSGz11SGe^}i)Djcn3Z%$2&4z-Qd1zrm{dVV;1snsH{)2(jt zT4;5bSKddP7!z*0-|7Lc2ds|1ARPV7>NKz4SuOHf)!uxVJ2u>4uGJ#1>#Zing`>Y& z-R`x9z4tH?g`-(ki@fGo-R^a&)hZW<8_eRU*9}$^ve#=TP`sr+;*hZ+)G0(w7S9T zNp3JT9BprJP)ke;wI6kQs8?9c_WHin+?nC%UseZu&9e7p+3#^=|J%aqHm^BWk9!?t zwf(GcgE>|=c)ic+QLl;W_PLjb8>CrH&IvVXwY1lCs};RwSgq!@q}94!OIywITGnc| z*YZ|#yq;z?&uc}i`CcnoE$~{!>S(W3twvs}S)Jmwy46CjHLWi2THESkuXU|1@mk;N zGOrD-uJGE}>Kd%tXA~e$7(gN{jApYnr}7B>p-j7UI$yv@jBFMp4S4a z`Cf-xE$}+R>S(Vctwvr)Tb<%{jMYM~W34XmI?n21uaVUyUdLNq=5>PA6<#MMyXIS0owb1Gwud}W0_d3VwA+PhS9`(Aw>T$1ESUu^r$m%JtS6NNY z3y<$&tEIhOYqg@+>#bJvdZX34UYA(S@_LKaY_ChL=6Jo`YM$3+R`b2yYqh}ZN~_bn zuClto>uRfuy{@sk#OqqC%e=0$y29&YR@ZplV0DAnC#-Jry3y)3uTNXu>2;IUJzh6k z-S2ga)k9vlT0QD@o7Ll9U$T1A>vpTByza1?oF5*(omNYGecftBue+>P^Saw=U9Wqr zW_f+bYPQ$CR&%`Wvzq61ztw!NA6hN&dcf*vuLrG0UJqHF;`KADgvvYycs*`)gV!IeZt{A<>Nc;xSl#LMq}4rMf3v#Z>+e<%dHvJs zQLm@09{2i>)stQ`YS`oJm0#1GyC6Ki4Xx^TGq<<8-H-Acn-dFX*!k*2xYY!1wJ_A_ z+{)`rtGm3;vbxvnusnO4>#ISHy)PQkQEQN*pM9$OKr%Bhudw)i-%=4%Dep4GHk^!y?KYf>haEN~TkHF? z(}vRV@VX5Z<6)N#)#72d4RzyTj}2Mz@Qw}H@vzs1oOsx0LtZ@Ww;?|sKD41A9uC+r zIvx(%5XHkG8>Ym=XEqeZ!(kg1#KRFA7RSR^HY|yUA8gnd55L&3B_2-NuqPhsH?{vd z>^1V?FKj~_R>VUy8#cwm1RJ);!`(KVYNOA2#D=1_8lJRaRXjY$=fuOyHXM$JH*83> z)91WvL(O>j$cEf__}qrE@xW^^isGS#4cpr54)`7G|NDjO&1_hgtB-WH;e-t{>>s!O zC%n&&MBVk_2W;S1hR?8noB4HIb~*R{*C z@$YK?$A;X~^=swJ)VGh=$?|YlSvKU_Ir1=cw;_3^hCCZe$3rh0D#k+}8>+=aKO5@C zL%t1J@i5Sa?06V#Lry#lwIMGa3T()ahv7C9#KQ<1M#sZQ8=`m^ZNrp!7-K_WJdCwr zK|GAJVR1Y}HY|yU@ir`rhY2>Uh=)lwtciyyHf)H8X*O(%hbwH@8xKV`oQj95Y)IOr zhVY0kwxM)9Tx&zcc(~q%YVmNR4RzyTi49rtaElGu@vzi}oOrn1hP-%KW3Ha6L&bRLV?(ug=x0OSc*wUQD;@^gkR1<$ZODm-p*G~jLxBzX z@i5$mf_NBV!{~SzX+snbqivWH4`Xa7jEAu{EQp73HY|>Z$c82HFy4k`@i4)L74b01 zhBfgp#fA;>Fx7@l@i5JXZSgSIhMn;+--cuHu+WAR@la&Lsd%``hNN9n^S}N!l#YjM zZKxOzH`q`u9&WawMLaCEp?y5uZbM!?++{<4JS?|ibUfT^Llh4yY?u-cD{Ux@hgCK# zh=OvpJ>7eZtNDH3_p@txSNGNGV%hOUYh5hKx~y!=x~?Ri zB{{s`Z$ol89Nvca+we9VlEYIVZDl}6jG%wGhT|AT{&0*Ib&a4+TNK?ObzlT8kQPB} zsBYi}2$D8Jx`%FSR~4H6_1g+XZS^j-!gp6U7UCCK4jYaI$wHp->Cc4 zXb($=KX#uU?cu#o&VL(Y51LOqeA)0#hu<-L+u?0P(&0yjYX{#8+`<3j@Z?4C|Gw~| zyNC?@&GW@$?*{*!!!yJGa>jd@y!K;m*?YlLhrerxI=u6JOm+@F!*8E&^L{4(jl(m; zKX!QS6(+y^4)^nX0Q{cAvk!tFIox~*eCEg9y?q$89o{nh2M$+1!Q>AdzGwI^9RAqw zUpf51@YR>yz5OJ&DW37Opy%++@NbJxa z_ZcQHJAByiVTUgne$pXt_=-c@@J)w5Gx!{SWH>lH`z&|;Hyxh*BKV^-J_r7V!zT@2 zd5@cXo=NeHFMx%^GsE9FPu!IZ-eVzhzEbf^;Lp$a74Vt&IeZy>!{IH%@0~Ac{05W% z$l*UU{DH$;hM#@KJr=%c_vY|hZ-9)$TZVt^~CQApOVRK$)_(O*$7cBd4 z9X=TX|C7Vl4F9vk%<#eUMW11oea#_mc+=r84gcZgX2PIeJ1h-jhu<))9Nsd-&X>!kx$BC9F9Vtm_lAFXUY2F@M-D%e z1K&U2#_*qh#4Rf_`TGukX85NLHzg)d&ll!if}q0(%An%lGyLc0Wfdm>=!`1(>5n?N zONPA1+~*yZhHp5$WeA;@)mZk;^CiGH;D0-#1z!Fscg7pu?cla+)Lnnr!R@UmeAK~h zASisr!R_=XeBI$s4FQKA7%C3F4$FSm;n^+t6Nle5{4+( z!^=PG?t01O-43rAzUuIK!#5rNrQzESZyUl6KQdGtd@Jtl*5R3<@9Rg({Dr%?``M0VuYSyZ*L(0yhp@r#@Rx>)gYUqy|HR>~NAR1UaChK7 zSl&DN0-tjDAb9?|yUn-3m%i*KK2SWveT=+!o6v>B2j0!OGjMqAJwp8qceRqYY~b+B z@LxN;W%x(uWuIf&zjp8${?GF=!&irH+2>gnIO7YT;@~sfJABXZ>kj{i;n~D}2Vdtl zv8lTQ_W|(U_x4?fH@*da=ZtTIf9CMaeMG#s?4LV)&*AR;oVHi~^1N4@kMaNTUWeD- ztI500`?P)ey-)7Wui4*y`23HL*sac6%boX`JAX*|lk+EDKe=2Dt9ejqr<*+n-@qD@a8}HY$ki%Psub%fZ z>oFNS{J^kw_$PfP|F**i2H5JaKuHBbqchd7NkAM78uMXTjKl>@~U(b6bz4g=Hz5K{Mv-$(~ z%<5-i9yx7=S_1 z{TAWfHYA3{Q$&n1jfw6(V&39}n1m@Y4KrdE=EOWKh(%ZuFJW1%z^Zr!YvMJmi#M<# zHepL_!;W|hyJ8RaMfZ!(`3F1=#Szo7=zfv$e*CF8gLCl?F2p5ViEFqK@8MQ_fID#y z58@*{ioPGyxAD~bBlYb!pFd{}_OpJ@Cgy^dn=6WzA1-n1^dUqU=KME6^S$Cl`RkMP(L z-Bzz2yQ2HOz++z=z@g~=f9*}jqTBxU%+pkK?@{!obJ4y2>2V>t_bPf^iSE||j~mgw zebM7qe1JP~4-cYygQK_jDEi(ZK7EHCztArRU{Jh(Au$XiVid;2I82C1m=e?CDI;c? z=EOWKh(%ZuFJW1%z^Zr!YvMJmi#M<#HepM2Z;SN)<8(y#)<}OkiEFqK@8MQ_fID#y58@*{ioPG$clA`i2fYdXVgLrk z3m6i^Fd{}_OpL>Xn1m@Y4KrdE=ES^sDu_jGdLIBU+#2##U)&cYq$~b;Z}SQ zPdjnX^dLUMqv(5CU+Ag+>hdP^i*7eq@7oB9ZcA7lUoi|LVid;2I82C1m=e=4BW7Vv zbo#-}kR{=ft#Q_|8eC}Qr^rmBR0;l2(&PDh4c5m@Qbo8T+$nYKjtB(!(ij_96j_ShBOlgb|ZqIX1Xm6Sm z^ROTmVM)A%Ww8RQ;uWlk*RU?$z=qg_EwK$d;w|ioJ@M2R2TX_J2#&=GoQg9z7u^%7 zI=Fu=y7zl~EQ=Lb6|Z1TyoPo01~x>uBd&K> zEz!Mw++#<)g!3nx($52FZA@idi+Aa7=S_1ZS?Cc4vB8JUyl*d?fdI7Cc4dkJtoAYcuI+BrWr8{ zb7CGAM7MFUcUL9x5|+gZtcq8#Cc1a8d$+BNH?SeP-H5$uOKiiAcniB?5B9|Y9Eu}2 z7AJ5jy0^=pztCwex|ct^ue%VJa3!wcM!bhx@d56{Jv@ky@F=>M;Jh#N)O$|7o%F$q&*8fL_-c*==+rUkJGOQL)Gz4u*}#R{y7SFk2t z!@76_8=~6|+PkZk=-$omu_NBXuGoWpaR7(n2#&=GoQm$nOYffNqI+G^<3e1DrjEQc8Ywt%$h;El_k0~(? zGh!Cz#5^pBMe$S;FPWA__u&NIJy*pmSQD>dUA%z}u?bsZ8+OE7*cIIizup(>i|#dD zk3(?;$KnJ|#TlH7?)@I#Z5QGauEaImi1*@YD?TvYiS7kt@2(EwBRq<}S2TU(7=vRDyMRndL$ zf_GOn@fy~}8`uz=uqC!(N4$kyu?PF&01m|w9E%e;6=!fRy2}>4Z*n2J4c0xb#5LTA z_i!sdz@4}kPY2QMyzbrAQS|Y`>eCPC@eBQ800zYi7!t!UBDznA@a{P##$iHC!jzbX z88HiUVjdR6A}on+dvos#mBk9IidV2Ex*gEZi%)gYZG-OJwjnlQOKiiAcniB?5B9|Y z9Eu}27AJ5j&fr{hSEP8~#zJ%(v3p#JYq$~b;Z}TrJ8=&W;v+nYzE}0*KlNTYfBvTU zwqFdupm+g8Vi-omD2$15m=KdNC8l9U%)*?QhXt_+OX4LgixpTEuV78QhIR16;Gi0*@2yq_o~reQ|R!kn0g1+fTA;w3DL6<8Io zU`=$N5958^x_AQ{ViUH+HtdME;;Ae4nD)g19Eu}27AJ5j&fr{hZ<5sU6_;=&uHi!3n`aYyD^whi9<@uY?F9u*xynrDwES@4_lxa+i!-SZGDKQN*VixAa zJS>PsSQ0N`S**aScm-?XHLQ#7qjRpUwZmsJ$|8I48Wjx0YhRKM#LzLiE;6i z5R*((Vj5<|EX;{{SP+Y_BwoU@Sbzmd$<)J;7;7bgZK!KqVFR-e!c6{p1%qGVgLrk3m6i^ zFd{}_OpL>Xn1m@YEuJ!BmT6AR!-80ZCGirL#R{y7SFk2t!@76_8)6f-#5U}Rx3DYr zU|$@-p*VtLaRR5}49-P&A@KS5o)+Sg=}KI~jd%~Y;se}?dw38Z;ZgK)#o^PR(Bl{S z#Q+S77ceA-VML6=m>7o%F$q&*8fL^S%!zsNR1k|yOX4LgixpTEuV78QhIR1sZ(&#L!M-?vLvaMh;sj2`8JvsmVs1Tt#U)&cYw@%Z@0o7J2e=dW@E|_Iqv-o6 zk6-VS(&ukNzZif)@dAd#FpP*%7!%_#Atqr;Ov8+rg*nlEgq-)|7sMhgiI?K3ELNCS z#Vc47uVG!hfeo<7o%F$q&*8fL^S z%!zqe5R0%RUc$0ifmQJe*2HUA7jMKsZ(&#L!M-?vLvaMhqWe@x?;$xA zXK*gw!G*YlD{&1s;yv7o4{#^$;X!3zrF9w(f#S0h`!!RO7VN8s} zgqVaWF%2_f7Usk}EQm!|5-(v{tiY;x1#99ptcy3WAvR%4Y>TIkc+0da_F!Kez@a#T zV{rnf;tbBkJGc;+a3!wcM!bhx@d56{Jv@ky@F@Czn#ZsA#*OE1LcbV*LGeO7g~Tw^ zh!}-2F%A=A5~jp7%!pZ-6Z5bj7GX)egk{lvN~!lqsft&yCSJq3cmo?^6Sl-Q?1;Cp zEB3@wUmP$UiX%7{CvYmx;9R_e3(!3n`gkkO)1T4f z7y88j42l;pB!*!`bf55g{w7Z`G0rq0CSgiU!;F}PIWZ3lViA_aOIQ{wuqs}`ns^QC z;tgzwP1q9Kup{2WuGoWpaR7(nNIZ?j3Dc=KgLCl?F2p5ViEFqK@8MQ_fID#y52E{U zUGG#mioTx}pL*}Ldj2N#ivbuEFJMRv!-yD#F)Gx0PR@0c#cC0vPXxDnk) z7<-3oD?Y%TxOaTdci(*bAU?vQ=yUI$J5QhfoF2c>F9u*xynrDw3?pI`#>6;Gh)I|d z(=a1u#ZykqGcAZkSQ0N`S**aScm-?XHLQy_upu^KOKiiAcniB?5B9|Y9Eu}27AJ5j z&fr|Ug9~vfo>trz$+Rq1U{$<=HSrqO#T(cVo3JIeVMn}$UD18A ztoP&h#Q_|OBRCc(a4OE=T)cw|aS2!A8g9gU@w62mnC`?qJcy6*DEizRUcJZfYkK@b zzZif)@dAd#FpP*%7!%_#Atqr;Ov8+rg*h<~3t|zL#7kHfE8?jtUNNnS*RU?$z=qg_ zEwK$d;w|ioJ=hlqa43%8Se(GAID>QX4lcwcT#0MA5%1wve1JP~FP;wKBh#bk`+1J< z&+GU?zZif)@dAd#FpP*%7!%_#Atqr;Ov8+rg*h<~3t|zL#7kHfE3hhF!J2q2p6cQa z(}viDEwK$d;w|ioJ=hlqa43%8Se(GAID>QX4lcwcT#0MA5%1wve1JP~4-euaJc_=L zd&l?m^y50d;@f^P0E6NM42fYF5u-3B#$iHC!jzbX88HiUVjdR6A}ooQuq;+!RlI^V z@fy~}8`uz=;;ALJnRdim*cE%QFAm^P9Ko?Tfm3k?=i(h)h)cK<*Ki}=!>#xLcj6u% z#7B4(eV^d*`w1Oi=obUxDJWhr4T)hG5u-3B#$iHC!jzbX88HiUVjdR6A}ooQuq;+! zRlI^V@fy~}8`uz=uqC!(N4ynJU9rcsFAm^P9Ko?Tfm3k?=i(h)h)cK<*Ki}=!>#xL zcj6u%#7B4(eV^p`ep1I5`o#bYiWe{>hQ(7vj53XhahMR3FeRp8M$E#Tn1=92y5pQ8v?7_Y`5KlvK#B?l9;8dK!xp)T`;u5aJHQb2z za4SB*ow$bw@ev+H-={dfpVIM#elY-p;sp$eVHgpkFeb*uQ$kEKO^Iok5wkES=3zlB z!jgCi%VGsq#Vc47uVG!hfeo<ryC+^`ve1u2Q_X`}~U(oS|elY-p;sp$eVHgpkFeb)fLQKMxm=;eNG0QY3 z=3zlB!jgCi%VGsq#Vc47uVG!hfeo<ryC+^`ve1u2Q_i2vrr*(XxUkt#YcmYFV7)Hb>jEQlW5R)(^reQ|R z!km~FPX)2av?N}_vRHvt@e0<&YgiX=U_)%eme__J@fLQ)9_)()I21>4EKcB5oWZ$x z2N&WJuEe!?+KBf|x8eibiFv3m#Q+S77ceA-VML6=m>7o% zF$q&*8fL^S%!zqe5R0%RUc$0ifmQKJJk`W&rgiZKHpC`uiEY>sZ(&#L!M-?vLvaMh z;sj2`8JvrEa3L;48Wjx0YhRKM#LzL ziE)?^lQ1QwVMfftoS26Nu?S1zB`k{-SQW2eO}vJ6@kTr~#3s|0*oGbP7IwuR?27|9 z6i09@PT*9W!MS(`7vd7G#5LTA_i!sdz@4~<2k{XeMc?N*zMs?a6;FOKz%(dcz>pY* z5its5VjL#KBut5Em=UuuC+1;6EW(m_3Cm&yR>dn=6R%-iynzj|30q=YJaxodrd_cI z`{Do&#St8f6F3!Ta4z1#g}8((aSb=(J=}^9a3}8JL41Tq(f4_d@8@-Vp5Zl2~%PkX2dMaiFsHMi?AeK!m?O_Rq+be#A{d=Z(u`g!j{;E9q|@+ z#h!TTivy-ZaRkTW1Wv^noQrpGAui!cT*Hlc54Yk2+=+X55Fg=D^nHQj`vo0e=obSp zC|Xm=sSbG0ik1W?@du!-80ZCGirL#R{y7SFk2t z!@76_8)6f-#5U}Rx3DYrU|$@-p*VtLaRR5}OgzoSJEjY930LA8Zp3@I6(8VE+{1(T z2#=!gb&l`rI=;{^24GOUfFUsqBVrWB#5hcdNthDTFe7HgQ%=k?Er>-}5-(v{tiY;x z1#99ptcy3WAvR%4Y{QOt3%g4!64!7e z-ixQL_`q~0?%_dvgh$c$OB~-{((#3UF#v<&1q_K{7!ji|CdOexOv03yh8Zynb7CGA z#3C$-m#{2W#8XwgVp4! z64!7e-ovf<0C(bEJRQVGrbp5D%N*Zd*71dYF#v<&1q_K{7!ji|CdOexOv03yh8Zyn zb7CGA#3C$-m#{2WU{$<=HSt4!64!7e-ovf<0C(aZ9>hm@6n($q9pBT_U(xXu-}Z|E7!)sHNDRY>7=zmd$<)J;7;7bgZK!KqVLQ45uSco#~1p=fOrau7feH9 z7)Hb>jEQlW5R)(^reQ|R!kn0g1+fTA;w3DL6<8IoU`@P+b@ArRU(3GT5SvU}VjFhE zTk+HtdrbS{01m|w9E%e;6=!fR-ob^qJkxzwD{;+qBi_TU_yBj}9v;L;cocoV%JKbG z9bf1d128CFz>pXgPZ2T7G$zJjLQKMxn1&fK3v*%~7Q`YfiI=b}R$x`Uf;I6P*2Npx z5Sy?iwqZxSgdUA%z}u?bsZ8+OE7*cE%QFAm^P9Ko?T5l>Tb#&j;;!G*YlD{&1s;yv7o4{#^$ z;X!e4$?qz@T^mLt+?4#3+o3ahMR3FeRqNQ%1}(&53zf5R0%RUc$0i zfmQJe*2HUA7jIxgY{Hhw zJ=3lD0C(aZ9>hm@6n$Uk_zmd$<)J;7;7bgZL<(j-v0IJbu5a;|u*_00zYi7!t!UB1U0MjKhSO zgefr%Gh!Cz#5^pBMOYFqVOgxes(1x!;x(*`H{z)wHkr1>HtdMEuq*apUmU=pID%tw z0;l2(&c!>p5SMTzuHi!3n`rhF9zMWH^YyJ8Ra#Q_|O zBRCc(a4OE=T)cw|aS2!A8g9gUxD_AZPTa$T_y~`p?^_(-Z|V3#zZif)@j^U>#4yu{ z7=h-+d96`F9u*xynrDw z3?pJxJjKK~(}b9WDKQN*VixAaJS>PsSQ0N`S**aScm-?XHLQy_upu^KOKiiAcniB? z5B9|Y9Ev0HG!`dJr{WCG#XGnVmvAMn;YPfNTk!$z#63KSkMJn^zQghTj*c(%ivbuE zFJMRv!-yD#F)Gx0PR@0c#cC0vPXxDoH+R(yavaSspTBRq<}^9Hf!-2$vShgo=eK z;mSgdaBZPZxUtY6G%d6UZ3`X3t%WY3XQ59RSQrvU7RH2$g(+cXVNSTSuplf6rxjss zD>j6C3tPg2g&kpU;Xrt_a3uJi+WDa!grC1@!A}TS2of$Vga}~^5kk~Lj1aewAS5lM z2x$u$Le@f#khf4E6bYvi;nG%=2^9-f!j**@;o3r-aAToCXj*6y+7>#5TMJ!6&qALt zurMTyEQ|>g3sb_(!klntVL@10SP|A1HiUb^X-jyp6+6P-!h!H;;YjeEca--2{CJZ; zKP>nO0SiIGg@q6yY#~C3T8I(i77~P{g%lxeAw$So$Pw}u3WTDC65-N9nNT5|s)Q?B zQ6pSis1t51Gzd)#EkfHuhj445OXykX69yKBgpq|YVPauQm|2(;?kp?_OA9N)+QNo# zZ(&P#u&^WS38w?$(N-J@KEItG+H(B)n-=_pfQ2C8!a|4;wh$piEyM_M3kgEfLW+>K zkRfC(60R)N2-k#Dop56-8ib~W7NKpSL%6ljCG;%x2?Gm5 z!pOpyFtIQt%q+|acNP|erG*t?ZDB*Wx3DEVSlAKv77m0*3rB+Qyam1c^W*e1z@Hz4 zxBY~Gg&^U=LWmHy5Ftb@#0YT<2}06BijcODA!IG&2zd(yLeWBraA~1Ts92~Ht}N6D z*B0u88w(9WlW=Mg+P0!YxV6wF^eprV0}DgK$ikQ~u`ngfEX)aa78Zo1g%x3KVMDmL zuq8ZL*b(*?4unSwM}jYCe|`ixKP>nO0m3OrxUdx=LfArt5Va5^#4RKUNed}L+Cqkq zwU8s^Effev3njv(g)*UHp-Q;2P$OJhs1t51Gzd)#EkfHuhj2?cbqPIN(I*Tn3<)C( zW5UG3lrXa}C)`$_5hKJcBnU|hDMH#phLE+8Bjha<2t^Ae!li{Wp<(#KM#?voI&zSy&L37FL9{g$?1}!j|x0VMo|o zI1nB!90|UVogX324-0-mz(SC4VIf2aTZj;%7Gi`r;gldGZAFTZwvZuYE#wG!3k5>a zLWyu`p-iY)s1mL$)CkuW>Vz8$4MNjGi_o^vA>3N%5_%T;gn@-2VPs)Um=I1=!pv68 z33nD2gr$WQVQpbUxVNw+JXqKf_7)C=M+--SFKp*WnDfJepAfJRBwSbs5yBQCgs6oW zA#Nc-NLok{(u7lnkhK*#Lf%4wP_$4YTv{j-Di*4QD+@KkwS_w2#zKS8w9q27Ep!OC z7P^FHEi z`4Qp#u;3>IECdM`7D9xug$N;PAx4N>NDz`1QiQaH3?XYFN5~US1wzqQln9p=%7luA zD&fjPjc{$DPPnnqAT%ws2yF`;!mWiap=Y5_7+4q*Mi$0|iG?X)W?@dav#=m6EvyJ@ z!f8Xew-sB$gM}SoZ{a|Av~VQ&qIP~nIX^7;2>}a1!i9wpA#5Q+h+2pd;uaEwq=ghA zZ6QO*TF4Rd77B!-g%aVCa4HijwxUY7vQQ&jTc{ImEHnsB3oSz1LWgi`p-bpl=o1DO zhJ=xYF=1k1N|;%g6YeZ52ulkq!rH=yaBpEtcp#j1guSge5FRZY3BH(}A2H4k3w}bt zLXdD_Aw&pUh!CO{VuZMb1R-f5MMzu75V96>guI0Up=hB*xU^6vR4h~pSAnO0SiIGg@q6yY#~C3T8I(i77~P{g%lxeAw$So$Pw}u z3WTDC65-N9nNYD%C0tpk5w0!N2{(jOgV3}UEkfHuhj445OXykX69yKBgpq|YVPauQ zm|2(;?kp?_OA9N)+QNo#Z(&P#u&^WSEgT4s7LEj8!p@Hb=Lg~BCj@LokZ@rkL_YZ7W#yNg&|>NVN943Qo5*{q<2zv_$!lQ*F z!I!l2Bgy$;!A}TS2of#`rw}1*D#5TMJ!6k8tV}2DV~I7+Dw-CKjfInT0vw&ccGQ zw6G$qEo=z)7Pf>33p>Kz!h!H;;YjeM?EFY^epv7m0v3XV3kxAa*g}L5C7fb}xUEPK zk`_{gw1o^IYavI-TPP5U7D|Lm3uQvZLX~i3p+>m2P$%42Xb_qfT7Ec6Kj3q!)l!k93zFeS_gr#a!yRxAih3oF9f!iI2f zVM}2BB%8MQB^-5N<7W2|Wva!ob3iFtRWv zOe{BSnv}97J`He3n4<-LWB^t5F^AbBnU|hDMH#phLE+8Bjha<2t^Ae z!li{Wp+Y!S30JnFM!2?6C)`+Q5SkWRgtmna;nqTz(6i7d3@i)@BMW1~#KM#?voI&z zSy&L37FL9{g$?1}!j|x0VMo{#P6xuHtvC{Vc{@MyoF5kagn)%0;le_Q5VjB@L@mS! zaSI7T(n5-mwvZuYE#wG!3k5>aLWyu`p-iY)s1mL$)CkvvQ=M>QD;k8Rg%+W0p+mT} z&?WRN^a%qCL&C_ym@u(0CCn_$33nD2gr$WQVQpbUxVNw+JXqKf_7)C=M+--Sub}he z^t8bFL3rCw2v`UbE-ZuyVG9vL)Iy99w~!ztEu;u(3mHPzLXMEPP#_d7ln9p=%7luA zD&fjPjc{$DPPnnqAT$Z57NKn`I)qybT|&=7pD?g6B#bPK2@?xb!py>)aA#pbSXx*S z))qE|dkb5_gM}SoZ{a|Av~VQ&iuUJ6k@LfXpAaCNf`kiO5h8>wLD%Y`BCEhu;3>I zECdM`7D9wD;S?c6ZAFX_w~!ztEu;u(3mHPzLXMEPP#_d7ln9p=%7luAD&fjPjc{$D zPPnnqAT%ws2yF`;!mWiap=Y5_7!XcF!pK&P2@?xb!py>)aA#pbSXx*S))qE|dkb5_ zgM}SoZ{a|Av~VQ&F75od2j9ev~;sEcgil3qit#g%BZZ zAwq~+h!Nrz5`?6M6d_GGWe8bYkt5_S6bMBNCBmhJGNEFjO1QF6BV1dk6K*Ut2u%wu zLfb-zaBHDU=vn9!1{Q{dk%ci~Vqr>{S(p><2&V;MX)9KQwS^7g-olpfU|~ntTR0FN zEgT8Hik%-7&JPQILcl_haA6@t2wR8{q84I=xP=5EX(2^OTgVWy7IK6<;Zz_LZAFQ2 zX`xK0Sf~=NEYt|s7V3l>3k^ckLW|J0&>`Gf=n{Gs`h_EcAKpVv9RL6T diff --git a/.venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc b/.venv/lib/python3.12/site-packages/idna/__pycache__/intranges.cpython-312.pyc deleted file mode 100644 index 6df38b4add41851e3e02d97b5cb95b2ee7456307..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2625 zcmZuyO>7fK6rTNaf*t42$`3y!6GGc81e?TwP!(FKpdbMOK>}(+t7Y+ylO^k2XLb!Z zbxK_RwB(a~n=v=$lzPA&8N7=e>D9^XAR> z-uxboh7k<)%G31c4G8_snED9Sh|T*zJVXYXK!#w5mM|f}ELmbknvgQ`ge)LKCK6G0 zgPx%fnJ7jmEix`G8x7yd6CtA!)^@?C5G5Tl!m!q0L^u!HuvXV@0!gD01xfRpe!y-Q z*tLK(oEOzreV2ex_^f%G*jUGw>3Y~nW7GD?B%$sA&gcfgxhz}kpK`J!owKaG3aV~K zw=8VuGB*ijQp>R?G0oX-7?_k}yCjwK!0j8pQcfnD^9UGIlBL8Ywx@fhV>5Gair}U?8a*dWqB+qJ<|L14^5H3Usvy+$>hl@&XraTcIB-47bV{SQDU1+u!%9yF&E#9IK;w}-c@ zzEV&OiF;NIq2?1rPhnR!>X8blAkRtnKa-Ic2_z7USeK3=Ro3@H60X#58*ct>gkiWY z026@=v{Qs3f$H`oaX$)U7&S0xNBQk*N4Mah1^CbyKo}whu$xOw!CmX#jtl2r>S)F~ zC79gRQ=XLvEypE*lx;hXJI%zyr(k)Q!4DX~Bz7HuiWv`cXIFT5fh#-Z7{p*la7}yC z+Qxbfw0L+JCx>u>`GOE_$78x41C<-+-Zoyt*kC}REVpXN54jHYh~X5pJ*gqez~n~)Lyct^u*$>q!;eZa>q{)n z7{9udn1WL#XqI@?xoxJV5^STysOhE>acB;=6P9^1k|}g!DB*(3BUwE)t;204 zOvBa_kmq3AcsB3Hwm28rI^-&*wGe<-AMEeMrJ)57J&8UNk|#jXZXhVDC6`%YOgq>@bNz>baNFQFk1DYHmBsVL z(EN%@QQpm2ug-a)+md5M_L#j zd2oLMv9l=}LcNL1iQDb)mJEt-4QO2AmaYU%Oqf0c3G4N?8dt5gL9=6Neag-Q;|oad zoD3~aEjQfL$CsSOmv#MADFcn{EgDeJht%oL$>n3GeUFK8~y6sv9+9BqEvc2U9;?dhgX zRnYkmKKC2=&7*&!dy2mT8LtKogcYo%*z;eq+n)evfoEL{Hf&U$BG zX{4Nl+B^Pubggq}F|r^pkV>Rw>DY@%Px1Zdk)BGdt16);%x&(EK|Ni6-q5|?-ct&d z+{cl%_JPF)Fw(iv_rsuGiL`Q9sS&?H(+nr2Y4iX@1&7RMp}|syDaxSYOY9&Fok0OH z!20q{$FcZ%ILx^#e(YC`!x_iOS>$6H2StVxcL9D?Nf3m8MMaP|n~~7`7usKyW5VvD zS33PDUqwKcuP!H_Ny{gnXq$|!9ukC}Vyuc_Dkb?;PHr-;8WMzor7^ZNP`p}7mZj3k kM_>eOwXsELE1s?*n93bxclmVr?vozQJ{y0@R(YiV1AI=MU;qFB diff --git a/.venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc b/.venv/lib/python3.12/site-packages/idna/__pycache__/package_data.cpython-312.pyc deleted file mode 100644 index 673ad48eb6a2ad92fc3921ae734d454201f58e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 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

I1 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⩓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 NXO&9#EM}h(|33%7PkR6W diff --git a/.venv/lib/python3.12/site-packages/idna/codec.py b/.venv/lib/python3.12/site-packages/idna/codec.py deleted file mode 100644 index c855a4d..0000000 --- a/.venv/lib/python3.12/site-packages/idna/codec.py +++ /dev/null @@ -1,118 +0,0 @@ -from .core import encode, decode, alabel, ulabel, IDNAError -import codecs -import re -from typing import Any, Tuple, Optional - -_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') - -class Codec(codecs.Codec): - - def encode(self, data: str, errors: str = 'strict') -> Tuple[bytes, int]: - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return b"", 0 - - return encode(data), len(data) - - def decode(self, data: bytes, errors: str = 'strict') -> Tuple[str, int]: - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return '', 0 - - return decode(data), len(data) - -class IncrementalEncoder(codecs.BufferedIncrementalEncoder): - def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]: - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return b'', 0 - - labels = _unicode_dots_re.split(data) - trailing_dot = b'' - if labels: - if not labels[-1]: - trailing_dot = b'.' - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = b'.' - - result = [] - size = 0 - for label in labels: - result.append(alabel(label)) - if size: - size += 1 - size += len(label) - - # Join with U+002E - result_bytes = b'.'.join(result) + trailing_dot - size += len(trailing_dot) - return result_bytes, size - -class IncrementalDecoder(codecs.BufferedIncrementalDecoder): - def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]: - if errors != 'strict': - raise IDNAError('Unsupported error handling \"{}\"'.format(errors)) - - if not data: - return ('', 0) - - if not isinstance(data, str): - data = str(data, 'ascii') - - labels = _unicode_dots_re.split(data) - trailing_dot = '' - if labels: - if not labels[-1]: - trailing_dot = '.' - del labels[-1] - elif not final: - # Keep potentially unfinished label until the next call - del labels[-1] - if labels: - trailing_dot = '.' - - result = [] - size = 0 - for label in labels: - result.append(ulabel(label)) - if size: - size += 1 - size += len(label) - - result_str = '.'.join(result) + trailing_dot - size += len(trailing_dot) - return (result_str, size) - - -class StreamWriter(Codec, codecs.StreamWriter): - pass - - -class StreamReader(Codec, codecs.StreamReader): - pass - - -def search_function(name: str) -> Optional[codecs.CodecInfo]: - if name != 'idna2008': - return None - return codecs.CodecInfo( - name=name, - encode=Codec().encode, - decode=Codec().decode, - incrementalencoder=IncrementalEncoder, - incrementaldecoder=IncrementalDecoder, - streamwriter=StreamWriter, - streamreader=StreamReader, - ) - -codecs.register(search_function) diff --git a/.venv/lib/python3.12/site-packages/idna/compat.py b/.venv/lib/python3.12/site-packages/idna/compat.py deleted file mode 100644 index 786e6bd..0000000 --- a/.venv/lib/python3.12/site-packages/idna/compat.py +++ /dev/null @@ -1,13 +0,0 @@ -from .core import * -from .codec import * -from typing import Any, Union - -def ToASCII(label: str) -> bytes: - return encode(label) - -def ToUnicode(label: Union[bytes, bytearray]) -> str: - return decode(label) - -def nameprep(s: Any) -> None: - raise NotImplementedError('IDNA 2008 does not utilise nameprep protocol') - diff --git a/.venv/lib/python3.12/site-packages/idna/core.py b/.venv/lib/python3.12/site-packages/idna/core.py deleted file mode 100644 index 0dae61a..0000000 --- a/.venv/lib/python3.12/site-packages/idna/core.py +++ /dev/null @@ -1,395 +0,0 @@ -from . import idnadata -import bisect -import unicodedata -import re -from typing import Union, Optional -from .intranges import intranges_contain - -_virama_combining_class = 9 -_alabel_prefix = b'xn--' -_unicode_dots_re = re.compile('[\u002e\u3002\uff0e\uff61]') - -class IDNAError(UnicodeError): - """ Base exception for all IDNA-encoding related problems """ - pass - - -class IDNABidiError(IDNAError): - """ Exception when bidirectional requirements are not satisfied """ - pass - - -class InvalidCodepoint(IDNAError): - """ Exception when a disallowed or unallocated codepoint is used """ - pass - - -class InvalidCodepointContext(IDNAError): - """ Exception when the codepoint is not valid in the context it is used """ - pass - - -def _combining_class(cp: int) -> int: - v = unicodedata.combining(chr(cp)) - if v == 0: - if not unicodedata.name(chr(cp)): - raise ValueError('Unknown character in unicodedata') - return v - -def _is_script(cp: str, script: str) -> bool: - return intranges_contain(ord(cp), idnadata.scripts[script]) - -def _punycode(s: str) -> bytes: - return s.encode('punycode') - -def _unot(s: int) -> str: - return 'U+{:04X}'.format(s) - - -def valid_label_length(label: Union[bytes, str]) -> bool: - if len(label) > 63: - return False - return True - - -def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: - if len(label) > (254 if trailing_dot else 253): - return False - return True - - -def check_bidi(label: str, check_ltr: bool = False) -> bool: - # Bidi rules should only be applied if string contains RTL characters - bidi_label = False - for (idx, cp) in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - if direction == '': - # String likely comes from a newer version of Unicode - raise IDNABidiError('Unknown directionality in label {} at position {}'.format(repr(label), idx)) - if direction in ['R', 'AL', 'AN']: - bidi_label = True - if not bidi_label and not check_ltr: - return True - - # Bidi rule 1 - direction = unicodedata.bidirectional(label[0]) - if direction in ['R', 'AL']: - rtl = True - elif direction == 'L': - rtl = False - else: - raise IDNABidiError('First codepoint in label {} must be directionality L, R or AL'.format(repr(label))) - - valid_ending = False - number_type = None # type: Optional[str] - for (idx, cp) in enumerate(label, 1): - direction = unicodedata.bidirectional(cp) - - if rtl: - # Bidi rule 2 - if not direction in ['R', 'AL', 'AN', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: - raise IDNABidiError('Invalid direction for codepoint at position {} in a right-to-left label'.format(idx)) - # Bidi rule 3 - if direction in ['R', 'AL', 'EN', 'AN']: - valid_ending = True - elif direction != 'NSM': - valid_ending = False - # Bidi rule 4 - if direction in ['AN', 'EN']: - if not number_type: - number_type = direction - else: - if number_type != direction: - raise IDNABidiError('Can not mix numeral types in a right-to-left label') - else: - # Bidi rule 5 - if not direction in ['L', 'EN', 'ES', 'CS', 'ET', 'ON', 'BN', 'NSM']: - raise IDNABidiError('Invalid direction for codepoint at position {} in a left-to-right label'.format(idx)) - # Bidi rule 6 - if direction in ['L', 'EN']: - valid_ending = True - elif direction != 'NSM': - valid_ending = False - - if not valid_ending: - raise IDNABidiError('Label ends with illegal codepoint directionality') - - return True - - -def check_initial_combiner(label: str) -> bool: - if unicodedata.category(label[0])[0] == 'M': - raise IDNAError('Label begins with an illegal combining character') - return True - - -def check_hyphen_ok(label: str) -> bool: - if label[2:4] == '--': - raise IDNAError('Label has disallowed hyphens in 3rd and 4th position') - if label[0] == '-' or label[-1] == '-': - raise IDNAError('Label must not start or end with a hyphen') - return True - - -def check_nfc(label: str) -> None: - if unicodedata.normalize('NFC', label) != label: - raise IDNAError('Label must be in Normalization Form C') - - -def valid_contextj(label: str, pos: int) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x200c: - - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - - ok = False - for i in range(pos-1, -1, -1): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord('T'): - continue - elif joining_type in [ord('L'), ord('D')]: - ok = True - break - else: - break - - if not ok: - return False - - ok = False - for i in range(pos+1, len(label)): - joining_type = idnadata.joining_types.get(ord(label[i])) - if joining_type == ord('T'): - continue - elif joining_type in [ord('R'), ord('D')]: - ok = True - break - else: - break - return ok - - if cp_value == 0x200d: - - if pos > 0: - if _combining_class(ord(label[pos - 1])) == _virama_combining_class: - return True - return False - - else: - - return False - - -def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: - cp_value = ord(label[pos]) - - if cp_value == 0x00b7: - if 0 < pos < len(label)-1: - if ord(label[pos - 1]) == 0x006c and ord(label[pos + 1]) == 0x006c: - return True - return False - - elif cp_value == 0x0375: - if pos < len(label)-1 and len(label) > 1: - return _is_script(label[pos + 1], 'Greek') - return False - - elif cp_value == 0x05f3 or cp_value == 0x05f4: - if pos > 0: - return _is_script(label[pos - 1], 'Hebrew') - return False - - elif cp_value == 0x30fb: - for cp in label: - if cp == '\u30fb': - continue - if _is_script(cp, 'Hiragana') or _is_script(cp, 'Katakana') or _is_script(cp, 'Han'): - return True - return False - - elif 0x660 <= cp_value <= 0x669: - for cp in label: - if 0x6f0 <= ord(cp) <= 0x06f9: - return False - return True - - elif 0x6f0 <= cp_value <= 0x6f9: - for cp in label: - if 0x660 <= ord(cp) <= 0x0669: - return False - return True - - return False - - -def check_label(label: Union[str, bytes, bytearray]) -> None: - if isinstance(label, (bytes, bytearray)): - label = label.decode('utf-8') - if len(label) == 0: - raise IDNAError('Empty Label') - - check_nfc(label) - check_hyphen_ok(label) - check_initial_combiner(label) - - for (pos, cp) in enumerate(label): - cp_value = ord(cp) - if intranges_contain(cp_value, idnadata.codepoint_classes['PVALID']): - continue - elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTJ']): - if not valid_contextj(label, pos): - raise InvalidCodepointContext('Joiner {} not allowed at position {} in {}'.format( - _unot(cp_value), pos+1, repr(label))) - elif intranges_contain(cp_value, idnadata.codepoint_classes['CONTEXTO']): - if not valid_contexto(label, pos): - raise InvalidCodepointContext('Codepoint {} not allowed at position {} in {}'.format(_unot(cp_value), pos+1, repr(label))) - else: - raise InvalidCodepoint('Codepoint {} at position {} of {} not allowed'.format(_unot(cp_value), pos+1, repr(label))) - - check_bidi(label) - - -def alabel(label: str) -> bytes: - try: - label_bytes = label.encode('ascii') - ulabel(label_bytes) - if not valid_label_length(label_bytes): - raise IDNAError('Label too long') - return label_bytes - except UnicodeEncodeError: - pass - - check_label(label) - label_bytes = _alabel_prefix + _punycode(label) - - if not valid_label_length(label_bytes): - raise IDNAError('Label too long') - - return label_bytes - - -def ulabel(label: Union[str, bytes, bytearray]) -> str: - if not isinstance(label, (bytes, bytearray)): - try: - label_bytes = label.encode('ascii') - except UnicodeEncodeError: - check_label(label) - return label - else: - label_bytes = label - - label_bytes = label_bytes.lower() - if label_bytes.startswith(_alabel_prefix): - label_bytes = label_bytes[len(_alabel_prefix):] - if not label_bytes: - raise IDNAError('Malformed A-label, no Punycode eligible content found') - if label_bytes.decode('ascii')[-1] == '-': - raise IDNAError('A-label must not end with a hyphen') - else: - check_label(label_bytes) - return label_bytes.decode('ascii') - - try: - label = label_bytes.decode('punycode') - except UnicodeError: - raise IDNAError('Invalid A-label') - check_label(label) - return label - - -def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: - """Re-map the characters in the string according to UTS46 processing.""" - from .uts46data import uts46data - output = '' - - for pos, char in enumerate(domain): - code_point = ord(char) - try: - uts46row = uts46data[code_point if code_point < 256 else - bisect.bisect_left(uts46data, (code_point, 'Z')) - 1] - status = uts46row[1] - replacement = None # type: Optional[str] - if len(uts46row) == 3: - replacement = uts46row[2] - if (status == 'V' or - (status == 'D' and not transitional) or - (status == '3' and not std3_rules and replacement is None)): - output += char - elif replacement is not None and (status == 'M' or - (status == '3' and not std3_rules) or - (status == 'D' and transitional)): - output += replacement - elif status != 'I': - raise IndexError() - except IndexError: - raise InvalidCodepoint( - 'Codepoint {} not allowed at position {} in {}'.format( - _unot(code_point), pos + 1, repr(domain))) - - return unicodedata.normalize('NFC', output) - - -def encode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False, transitional: bool = False) -> bytes: - if not isinstance(s, str): - try: - s = str(s, 'ascii') - except UnicodeDecodeError: - raise IDNAError('should pass a unicode string to the function rather than a byte string.') - if uts46: - s = uts46_remap(s, std3_rules, transitional) - trailing_dot = False - result = [] - if strict: - labels = s.split('.') - else: - labels = _unicode_dots_re.split(s) - if not labels or labels == ['']: - raise IDNAError('Empty domain') - if labels[-1] == '': - del labels[-1] - trailing_dot = True - for label in labels: - s = alabel(label) - if s: - result.append(s) - else: - raise IDNAError('Empty label') - if trailing_dot: - result.append(b'') - s = b'.'.join(result) - if not valid_string_length(s, trailing_dot): - raise IDNAError('Domain too long') - return s - - -def decode(s: Union[str, bytes, bytearray], strict: bool = False, uts46: bool = False, std3_rules: bool = False) -> str: - try: - if not isinstance(s, str): - s = str(s, 'ascii') - except UnicodeDecodeError: - raise IDNAError('Invalid ASCII in A-label') - if uts46: - s = uts46_remap(s, std3_rules, False) - trailing_dot = False - result = [] - if not strict: - labels = _unicode_dots_re.split(s) - else: - labels = s.split('.') - if not labels or labels == ['']: - raise IDNAError('Empty domain') - if not labels[-1]: - del labels[-1] - trailing_dot = True - for label in labels: - s = ulabel(label) - if s: - result.append(s) - else: - raise IDNAError('Empty label') - if trailing_dot: - result.append('') - return '.'.join(result) diff --git a/.venv/lib/python3.12/site-packages/idna/idnadata.py b/.venv/lib/python3.12/site-packages/idna/idnadata.py deleted file mode 100644 index c61dcf9..0000000 --- a/.venv/lib/python3.12/site-packages/idna/idnadata.py +++ /dev/null @@ -1,4245 +0,0 @@ -# This file is automatically generated by tools/idna-data - -__version__ = '15.1.0' -scripts = { - 'Greek': ( - 0x37000000374, - 0x37500000378, - 0x37a0000037e, - 0x37f00000380, - 0x38400000385, - 0x38600000387, - 0x3880000038b, - 0x38c0000038d, - 0x38e000003a2, - 0x3a3000003e2, - 0x3f000000400, - 0x1d2600001d2b, - 0x1d5d00001d62, - 0x1d6600001d6b, - 0x1dbf00001dc0, - 0x1f0000001f16, - 0x1f1800001f1e, - 0x1f2000001f46, - 0x1f4800001f4e, - 0x1f5000001f58, - 0x1f5900001f5a, - 0x1f5b00001f5c, - 0x1f5d00001f5e, - 0x1f5f00001f7e, - 0x1f8000001fb5, - 0x1fb600001fc5, - 0x1fc600001fd4, - 0x1fd600001fdc, - 0x1fdd00001ff0, - 0x1ff200001ff5, - 0x1ff600001fff, - 0x212600002127, - 0xab650000ab66, - 0x101400001018f, - 0x101a0000101a1, - 0x1d2000001d246, - ), - 'Han': ( - 0x2e8000002e9a, - 0x2e9b00002ef4, - 0x2f0000002fd6, - 0x300500003006, - 0x300700003008, - 0x30210000302a, - 0x30380000303c, - 0x340000004dc0, - 0x4e000000a000, - 0xf9000000fa6e, - 0xfa700000fada, - 0x16fe200016fe4, - 0x16ff000016ff2, - 0x200000002a6e0, - 0x2a7000002b73a, - 0x2b7400002b81e, - 0x2b8200002cea2, - 0x2ceb00002ebe1, - 0x2ebf00002ee5e, - 0x2f8000002fa1e, - 0x300000003134b, - 0x31350000323b0, - ), - 'Hebrew': ( - 0x591000005c8, - 0x5d0000005eb, - 0x5ef000005f5, - 0xfb1d0000fb37, - 0xfb380000fb3d, - 0xfb3e0000fb3f, - 0xfb400000fb42, - 0xfb430000fb45, - 0xfb460000fb50, - ), - 'Hiragana': ( - 0x304100003097, - 0x309d000030a0, - 0x1b0010001b120, - 0x1b1320001b133, - 0x1b1500001b153, - 0x1f2000001f201, - ), - 'Katakana': ( - 0x30a1000030fb, - 0x30fd00003100, - 0x31f000003200, - 0x32d0000032ff, - 0x330000003358, - 0xff660000ff70, - 0xff710000ff9e, - 0x1aff00001aff4, - 0x1aff50001affc, - 0x1affd0001afff, - 0x1b0000001b001, - 0x1b1200001b123, - 0x1b1550001b156, - 0x1b1640001b168, - ), -} -joining_types = { - 0xad: 84, - 0x300: 84, - 0x301: 84, - 0x302: 84, - 0x303: 84, - 0x304: 84, - 0x305: 84, - 0x306: 84, - 0x307: 84, - 0x308: 84, - 0x309: 84, - 0x30a: 84, - 0x30b: 84, - 0x30c: 84, - 0x30d: 84, - 0x30e: 84, - 0x30f: 84, - 0x310: 84, - 0x311: 84, - 0x312: 84, - 0x313: 84, - 0x314: 84, - 0x315: 84, - 0x316: 84, - 0x317: 84, - 0x318: 84, - 0x319: 84, - 0x31a: 84, - 0x31b: 84, - 0x31c: 84, - 0x31d: 84, - 0x31e: 84, - 0x31f: 84, - 0x320: 84, - 0x321: 84, - 0x322: 84, - 0x323: 84, - 0x324: 84, - 0x325: 84, - 0x326: 84, - 0x327: 84, - 0x328: 84, - 0x329: 84, - 0x32a: 84, - 0x32b: 84, - 0x32c: 84, - 0x32d: 84, - 0x32e: 84, - 0x32f: 84, - 0x330: 84, - 0x331: 84, - 0x332: 84, - 0x333: 84, - 0x334: 84, - 0x335: 84, - 0x336: 84, - 0x337: 84, - 0x338: 84, - 0x339: 84, - 0x33a: 84, - 0x33b: 84, - 0x33c: 84, - 0x33d: 84, - 0x33e: 84, - 0x33f: 84, - 0x340: 84, - 0x341: 84, - 0x342: 84, - 0x343: 84, - 0x344: 84, - 0x345: 84, - 0x346: 84, - 0x347: 84, - 0x348: 84, - 0x349: 84, - 0x34a: 84, - 0x34b: 84, - 0x34c: 84, - 0x34d: 84, - 0x34e: 84, - 0x34f: 84, - 0x350: 84, - 0x351: 84, - 0x352: 84, - 0x353: 84, - 0x354: 84, - 0x355: 84, - 0x356: 84, - 0x357: 84, - 0x358: 84, - 0x359: 84, - 0x35a: 84, - 0x35b: 84, - 0x35c: 84, - 0x35d: 84, - 0x35e: 84, - 0x35f: 84, - 0x360: 84, - 0x361: 84, - 0x362: 84, - 0x363: 84, - 0x364: 84, - 0x365: 84, - 0x366: 84, - 0x367: 84, - 0x368: 84, - 0x369: 84, - 0x36a: 84, - 0x36b: 84, - 0x36c: 84, - 0x36d: 84, - 0x36e: 84, - 0x36f: 84, - 0x483: 84, - 0x484: 84, - 0x485: 84, - 0x486: 84, - 0x487: 84, - 0x488: 84, - 0x489: 84, - 0x591: 84, - 0x592: 84, - 0x593: 84, - 0x594: 84, - 0x595: 84, - 0x596: 84, - 0x597: 84, - 0x598: 84, - 0x599: 84, - 0x59a: 84, - 0x59b: 84, - 0x59c: 84, - 0x59d: 84, - 0x59e: 84, - 0x59f: 84, - 0x5a0: 84, - 0x5a1: 84, - 0x5a2: 84, - 0x5a3: 84, - 0x5a4: 84, - 0x5a5: 84, - 0x5a6: 84, - 0x5a7: 84, - 0x5a8: 84, - 0x5a9: 84, - 0x5aa: 84, - 0x5ab: 84, - 0x5ac: 84, - 0x5ad: 84, - 0x5ae: 84, - 0x5af: 84, - 0x5b0: 84, - 0x5b1: 84, - 0x5b2: 84, - 0x5b3: 84, - 0x5b4: 84, - 0x5b5: 84, - 0x5b6: 84, - 0x5b7: 84, - 0x5b8: 84, - 0x5b9: 84, - 0x5ba: 84, - 0x5bb: 84, - 0x5bc: 84, - 0x5bd: 84, - 0x5bf: 84, - 0x5c1: 84, - 0x5c2: 84, - 0x5c4: 84, - 0x5c5: 84, - 0x5c7: 84, - 0x610: 84, - 0x611: 84, - 0x612: 84, - 0x613: 84, - 0x614: 84, - 0x615: 84, - 0x616: 84, - 0x617: 84, - 0x618: 84, - 0x619: 84, - 0x61a: 84, - 0x61c: 84, - 0x620: 68, - 0x622: 82, - 0x623: 82, - 0x624: 82, - 0x625: 82, - 0x626: 68, - 0x627: 82, - 0x628: 68, - 0x629: 82, - 0x62a: 68, - 0x62b: 68, - 0x62c: 68, - 0x62d: 68, - 0x62e: 68, - 0x62f: 82, - 0x630: 82, - 0x631: 82, - 0x632: 82, - 0x633: 68, - 0x634: 68, - 0x635: 68, - 0x636: 68, - 0x637: 68, - 0x638: 68, - 0x639: 68, - 0x63a: 68, - 0x63b: 68, - 0x63c: 68, - 0x63d: 68, - 0x63e: 68, - 0x63f: 68, - 0x640: 67, - 0x641: 68, - 0x642: 68, - 0x643: 68, - 0x644: 68, - 0x645: 68, - 0x646: 68, - 0x647: 68, - 0x648: 82, - 0x649: 68, - 0x64a: 68, - 0x64b: 84, - 0x64c: 84, - 0x64d: 84, - 0x64e: 84, - 0x64f: 84, - 0x650: 84, - 0x651: 84, - 0x652: 84, - 0x653: 84, - 0x654: 84, - 0x655: 84, - 0x656: 84, - 0x657: 84, - 0x658: 84, - 0x659: 84, - 0x65a: 84, - 0x65b: 84, - 0x65c: 84, - 0x65d: 84, - 0x65e: 84, - 0x65f: 84, - 0x66e: 68, - 0x66f: 68, - 0x670: 84, - 0x671: 82, - 0x672: 82, - 0x673: 82, - 0x675: 82, - 0x676: 82, - 0x677: 82, - 0x678: 68, - 0x679: 68, - 0x67a: 68, - 0x67b: 68, - 0x67c: 68, - 0x67d: 68, - 0x67e: 68, - 0x67f: 68, - 0x680: 68, - 0x681: 68, - 0x682: 68, - 0x683: 68, - 0x684: 68, - 0x685: 68, - 0x686: 68, - 0x687: 68, - 0x688: 82, - 0x689: 82, - 0x68a: 82, - 0x68b: 82, - 0x68c: 82, - 0x68d: 82, - 0x68e: 82, - 0x68f: 82, - 0x690: 82, - 0x691: 82, - 0x692: 82, - 0x693: 82, - 0x694: 82, - 0x695: 82, - 0x696: 82, - 0x697: 82, - 0x698: 82, - 0x699: 82, - 0x69a: 68, - 0x69b: 68, - 0x69c: 68, - 0x69d: 68, - 0x69e: 68, - 0x69f: 68, - 0x6a0: 68, - 0x6a1: 68, - 0x6a2: 68, - 0x6a3: 68, - 0x6a4: 68, - 0x6a5: 68, - 0x6a6: 68, - 0x6a7: 68, - 0x6a8: 68, - 0x6a9: 68, - 0x6aa: 68, - 0x6ab: 68, - 0x6ac: 68, - 0x6ad: 68, - 0x6ae: 68, - 0x6af: 68, - 0x6b0: 68, - 0x6b1: 68, - 0x6b2: 68, - 0x6b3: 68, - 0x6b4: 68, - 0x6b5: 68, - 0x6b6: 68, - 0x6b7: 68, - 0x6b8: 68, - 0x6b9: 68, - 0x6ba: 68, - 0x6bb: 68, - 0x6bc: 68, - 0x6bd: 68, - 0x6be: 68, - 0x6bf: 68, - 0x6c0: 82, - 0x6c1: 68, - 0x6c2: 68, - 0x6c3: 82, - 0x6c4: 82, - 0x6c5: 82, - 0x6c6: 82, - 0x6c7: 82, - 0x6c8: 82, - 0x6c9: 82, - 0x6ca: 82, - 0x6cb: 82, - 0x6cc: 68, - 0x6cd: 82, - 0x6ce: 68, - 0x6cf: 82, - 0x6d0: 68, - 0x6d1: 68, - 0x6d2: 82, - 0x6d3: 82, - 0x6d5: 82, - 0x6d6: 84, - 0x6d7: 84, - 0x6d8: 84, - 0x6d9: 84, - 0x6da: 84, - 0x6db: 84, - 0x6dc: 84, - 0x6df: 84, - 0x6e0: 84, - 0x6e1: 84, - 0x6e2: 84, - 0x6e3: 84, - 0x6e4: 84, - 0x6e7: 84, - 0x6e8: 84, - 0x6ea: 84, - 0x6eb: 84, - 0x6ec: 84, - 0x6ed: 84, - 0x6ee: 82, - 0x6ef: 82, - 0x6fa: 68, - 0x6fb: 68, - 0x6fc: 68, - 0x6ff: 68, - 0x70f: 84, - 0x710: 82, - 0x711: 84, - 0x712: 68, - 0x713: 68, - 0x714: 68, - 0x715: 82, - 0x716: 82, - 0x717: 82, - 0x718: 82, - 0x719: 82, - 0x71a: 68, - 0x71b: 68, - 0x71c: 68, - 0x71d: 68, - 0x71e: 82, - 0x71f: 68, - 0x720: 68, - 0x721: 68, - 0x722: 68, - 0x723: 68, - 0x724: 68, - 0x725: 68, - 0x726: 68, - 0x727: 68, - 0x728: 82, - 0x729: 68, - 0x72a: 82, - 0x72b: 68, - 0x72c: 82, - 0x72d: 68, - 0x72e: 68, - 0x72f: 82, - 0x730: 84, - 0x731: 84, - 0x732: 84, - 0x733: 84, - 0x734: 84, - 0x735: 84, - 0x736: 84, - 0x737: 84, - 0x738: 84, - 0x739: 84, - 0x73a: 84, - 0x73b: 84, - 0x73c: 84, - 0x73d: 84, - 0x73e: 84, - 0x73f: 84, - 0x740: 84, - 0x741: 84, - 0x742: 84, - 0x743: 84, - 0x744: 84, - 0x745: 84, - 0x746: 84, - 0x747: 84, - 0x748: 84, - 0x749: 84, - 0x74a: 84, - 0x74d: 82, - 0x74e: 68, - 0x74f: 68, - 0x750: 68, - 0x751: 68, - 0x752: 68, - 0x753: 68, - 0x754: 68, - 0x755: 68, - 0x756: 68, - 0x757: 68, - 0x758: 68, - 0x759: 82, - 0x75a: 82, - 0x75b: 82, - 0x75c: 68, - 0x75d: 68, - 0x75e: 68, - 0x75f: 68, - 0x760: 68, - 0x761: 68, - 0x762: 68, - 0x763: 68, - 0x764: 68, - 0x765: 68, - 0x766: 68, - 0x767: 68, - 0x768: 68, - 0x769: 68, - 0x76a: 68, - 0x76b: 82, - 0x76c: 82, - 0x76d: 68, - 0x76e: 68, - 0x76f: 68, - 0x770: 68, - 0x771: 82, - 0x772: 68, - 0x773: 82, - 0x774: 82, - 0x775: 68, - 0x776: 68, - 0x777: 68, - 0x778: 82, - 0x779: 82, - 0x77a: 68, - 0x77b: 68, - 0x77c: 68, - 0x77d: 68, - 0x77e: 68, - 0x77f: 68, - 0x7a6: 84, - 0x7a7: 84, - 0x7a8: 84, - 0x7a9: 84, - 0x7aa: 84, - 0x7ab: 84, - 0x7ac: 84, - 0x7ad: 84, - 0x7ae: 84, - 0x7af: 84, - 0x7b0: 84, - 0x7ca: 68, - 0x7cb: 68, - 0x7cc: 68, - 0x7cd: 68, - 0x7ce: 68, - 0x7cf: 68, - 0x7d0: 68, - 0x7d1: 68, - 0x7d2: 68, - 0x7d3: 68, - 0x7d4: 68, - 0x7d5: 68, - 0x7d6: 68, - 0x7d7: 68, - 0x7d8: 68, - 0x7d9: 68, - 0x7da: 68, - 0x7db: 68, - 0x7dc: 68, - 0x7dd: 68, - 0x7de: 68, - 0x7df: 68, - 0x7e0: 68, - 0x7e1: 68, - 0x7e2: 68, - 0x7e3: 68, - 0x7e4: 68, - 0x7e5: 68, - 0x7e6: 68, - 0x7e7: 68, - 0x7e8: 68, - 0x7e9: 68, - 0x7ea: 68, - 0x7eb: 84, - 0x7ec: 84, - 0x7ed: 84, - 0x7ee: 84, - 0x7ef: 84, - 0x7f0: 84, - 0x7f1: 84, - 0x7f2: 84, - 0x7f3: 84, - 0x7fa: 67, - 0x7fd: 84, - 0x816: 84, - 0x817: 84, - 0x818: 84, - 0x819: 84, - 0x81b: 84, - 0x81c: 84, - 0x81d: 84, - 0x81e: 84, - 0x81f: 84, - 0x820: 84, - 0x821: 84, - 0x822: 84, - 0x823: 84, - 0x825: 84, - 0x826: 84, - 0x827: 84, - 0x829: 84, - 0x82a: 84, - 0x82b: 84, - 0x82c: 84, - 0x82d: 84, - 0x840: 82, - 0x841: 68, - 0x842: 68, - 0x843: 68, - 0x844: 68, - 0x845: 68, - 0x846: 82, - 0x847: 82, - 0x848: 68, - 0x849: 82, - 0x84a: 68, - 0x84b: 68, - 0x84c: 68, - 0x84d: 68, - 0x84e: 68, - 0x84f: 68, - 0x850: 68, - 0x851: 68, - 0x852: 68, - 0x853: 68, - 0x854: 82, - 0x855: 68, - 0x856: 82, - 0x857: 82, - 0x858: 82, - 0x859: 84, - 0x85a: 84, - 0x85b: 84, - 0x860: 68, - 0x862: 68, - 0x863: 68, - 0x864: 68, - 0x865: 68, - 0x867: 82, - 0x868: 68, - 0x869: 82, - 0x86a: 82, - 0x870: 82, - 0x871: 82, - 0x872: 82, - 0x873: 82, - 0x874: 82, - 0x875: 82, - 0x876: 82, - 0x877: 82, - 0x878: 82, - 0x879: 82, - 0x87a: 82, - 0x87b: 82, - 0x87c: 82, - 0x87d: 82, - 0x87e: 82, - 0x87f: 82, - 0x880: 82, - 0x881: 82, - 0x882: 82, - 0x883: 67, - 0x884: 67, - 0x885: 67, - 0x886: 68, - 0x889: 68, - 0x88a: 68, - 0x88b: 68, - 0x88c: 68, - 0x88d: 68, - 0x88e: 82, - 0x898: 84, - 0x899: 84, - 0x89a: 84, - 0x89b: 84, - 0x89c: 84, - 0x89d: 84, - 0x89e: 84, - 0x89f: 84, - 0x8a0: 68, - 0x8a1: 68, - 0x8a2: 68, - 0x8a3: 68, - 0x8a4: 68, - 0x8a5: 68, - 0x8a6: 68, - 0x8a7: 68, - 0x8a8: 68, - 0x8a9: 68, - 0x8aa: 82, - 0x8ab: 82, - 0x8ac: 82, - 0x8ae: 82, - 0x8af: 68, - 0x8b0: 68, - 0x8b1: 82, - 0x8b2: 82, - 0x8b3: 68, - 0x8b4: 68, - 0x8b5: 68, - 0x8b6: 68, - 0x8b7: 68, - 0x8b8: 68, - 0x8b9: 82, - 0x8ba: 68, - 0x8bb: 68, - 0x8bc: 68, - 0x8bd: 68, - 0x8be: 68, - 0x8bf: 68, - 0x8c0: 68, - 0x8c1: 68, - 0x8c2: 68, - 0x8c3: 68, - 0x8c4: 68, - 0x8c5: 68, - 0x8c6: 68, - 0x8c7: 68, - 0x8c8: 68, - 0x8ca: 84, - 0x8cb: 84, - 0x8cc: 84, - 0x8cd: 84, - 0x8ce: 84, - 0x8cf: 84, - 0x8d0: 84, - 0x8d1: 84, - 0x8d2: 84, - 0x8d3: 84, - 0x8d4: 84, - 0x8d5: 84, - 0x8d6: 84, - 0x8d7: 84, - 0x8d8: 84, - 0x8d9: 84, - 0x8da: 84, - 0x8db: 84, - 0x8dc: 84, - 0x8dd: 84, - 0x8de: 84, - 0x8df: 84, - 0x8e0: 84, - 0x8e1: 84, - 0x8e3: 84, - 0x8e4: 84, - 0x8e5: 84, - 0x8e6: 84, - 0x8e7: 84, - 0x8e8: 84, - 0x8e9: 84, - 0x8ea: 84, - 0x8eb: 84, - 0x8ec: 84, - 0x8ed: 84, - 0x8ee: 84, - 0x8ef: 84, - 0x8f0: 84, - 0x8f1: 84, - 0x8f2: 84, - 0x8f3: 84, - 0x8f4: 84, - 0x8f5: 84, - 0x8f6: 84, - 0x8f7: 84, - 0x8f8: 84, - 0x8f9: 84, - 0x8fa: 84, - 0x8fb: 84, - 0x8fc: 84, - 0x8fd: 84, - 0x8fe: 84, - 0x8ff: 84, - 0x900: 84, - 0x901: 84, - 0x902: 84, - 0x93a: 84, - 0x93c: 84, - 0x941: 84, - 0x942: 84, - 0x943: 84, - 0x944: 84, - 0x945: 84, - 0x946: 84, - 0x947: 84, - 0x948: 84, - 0x94d: 84, - 0x951: 84, - 0x952: 84, - 0x953: 84, - 0x954: 84, - 0x955: 84, - 0x956: 84, - 0x957: 84, - 0x962: 84, - 0x963: 84, - 0x981: 84, - 0x9bc: 84, - 0x9c1: 84, - 0x9c2: 84, - 0x9c3: 84, - 0x9c4: 84, - 0x9cd: 84, - 0x9e2: 84, - 0x9e3: 84, - 0x9fe: 84, - 0xa01: 84, - 0xa02: 84, - 0xa3c: 84, - 0xa41: 84, - 0xa42: 84, - 0xa47: 84, - 0xa48: 84, - 0xa4b: 84, - 0xa4c: 84, - 0xa4d: 84, - 0xa51: 84, - 0xa70: 84, - 0xa71: 84, - 0xa75: 84, - 0xa81: 84, - 0xa82: 84, - 0xabc: 84, - 0xac1: 84, - 0xac2: 84, - 0xac3: 84, - 0xac4: 84, - 0xac5: 84, - 0xac7: 84, - 0xac8: 84, - 0xacd: 84, - 0xae2: 84, - 0xae3: 84, - 0xafa: 84, - 0xafb: 84, - 0xafc: 84, - 0xafd: 84, - 0xafe: 84, - 0xaff: 84, - 0xb01: 84, - 0xb3c: 84, - 0xb3f: 84, - 0xb41: 84, - 0xb42: 84, - 0xb43: 84, - 0xb44: 84, - 0xb4d: 84, - 0xb55: 84, - 0xb56: 84, - 0xb62: 84, - 0xb63: 84, - 0xb82: 84, - 0xbc0: 84, - 0xbcd: 84, - 0xc00: 84, - 0xc04: 84, - 0xc3c: 84, - 0xc3e: 84, - 0xc3f: 84, - 0xc40: 84, - 0xc46: 84, - 0xc47: 84, - 0xc48: 84, - 0xc4a: 84, - 0xc4b: 84, - 0xc4c: 84, - 0xc4d: 84, - 0xc55: 84, - 0xc56: 84, - 0xc62: 84, - 0xc63: 84, - 0xc81: 84, - 0xcbc: 84, - 0xcbf: 84, - 0xcc6: 84, - 0xccc: 84, - 0xccd: 84, - 0xce2: 84, - 0xce3: 84, - 0xd00: 84, - 0xd01: 84, - 0xd3b: 84, - 0xd3c: 84, - 0xd41: 84, - 0xd42: 84, - 0xd43: 84, - 0xd44: 84, - 0xd4d: 84, - 0xd62: 84, - 0xd63: 84, - 0xd81: 84, - 0xdca: 84, - 0xdd2: 84, - 0xdd3: 84, - 0xdd4: 84, - 0xdd6: 84, - 0xe31: 84, - 0xe34: 84, - 0xe35: 84, - 0xe36: 84, - 0xe37: 84, - 0xe38: 84, - 0xe39: 84, - 0xe3a: 84, - 0xe47: 84, - 0xe48: 84, - 0xe49: 84, - 0xe4a: 84, - 0xe4b: 84, - 0xe4c: 84, - 0xe4d: 84, - 0xe4e: 84, - 0xeb1: 84, - 0xeb4: 84, - 0xeb5: 84, - 0xeb6: 84, - 0xeb7: 84, - 0xeb8: 84, - 0xeb9: 84, - 0xeba: 84, - 0xebb: 84, - 0xebc: 84, - 0xec8: 84, - 0xec9: 84, - 0xeca: 84, - 0xecb: 84, - 0xecc: 84, - 0xecd: 84, - 0xece: 84, - 0xf18: 84, - 0xf19: 84, - 0xf35: 84, - 0xf37: 84, - 0xf39: 84, - 0xf71: 84, - 0xf72: 84, - 0xf73: 84, - 0xf74: 84, - 0xf75: 84, - 0xf76: 84, - 0xf77: 84, - 0xf78: 84, - 0xf79: 84, - 0xf7a: 84, - 0xf7b: 84, - 0xf7c: 84, - 0xf7d: 84, - 0xf7e: 84, - 0xf80: 84, - 0xf81: 84, - 0xf82: 84, - 0xf83: 84, - 0xf84: 84, - 0xf86: 84, - 0xf87: 84, - 0xf8d: 84, - 0xf8e: 84, - 0xf8f: 84, - 0xf90: 84, - 0xf91: 84, - 0xf92: 84, - 0xf93: 84, - 0xf94: 84, - 0xf95: 84, - 0xf96: 84, - 0xf97: 84, - 0xf99: 84, - 0xf9a: 84, - 0xf9b: 84, - 0xf9c: 84, - 0xf9d: 84, - 0xf9e: 84, - 0xf9f: 84, - 0xfa0: 84, - 0xfa1: 84, - 0xfa2: 84, - 0xfa3: 84, - 0xfa4: 84, - 0xfa5: 84, - 0xfa6: 84, - 0xfa7: 84, - 0xfa8: 84, - 0xfa9: 84, - 0xfaa: 84, - 0xfab: 84, - 0xfac: 84, - 0xfad: 84, - 0xfae: 84, - 0xfaf: 84, - 0xfb0: 84, - 0xfb1: 84, - 0xfb2: 84, - 0xfb3: 84, - 0xfb4: 84, - 0xfb5: 84, - 0xfb6: 84, - 0xfb7: 84, - 0xfb8: 84, - 0xfb9: 84, - 0xfba: 84, - 0xfbb: 84, - 0xfbc: 84, - 0xfc6: 84, - 0x102d: 84, - 0x102e: 84, - 0x102f: 84, - 0x1030: 84, - 0x1032: 84, - 0x1033: 84, - 0x1034: 84, - 0x1035: 84, - 0x1036: 84, - 0x1037: 84, - 0x1039: 84, - 0x103a: 84, - 0x103d: 84, - 0x103e: 84, - 0x1058: 84, - 0x1059: 84, - 0x105e: 84, - 0x105f: 84, - 0x1060: 84, - 0x1071: 84, - 0x1072: 84, - 0x1073: 84, - 0x1074: 84, - 0x1082: 84, - 0x1085: 84, - 0x1086: 84, - 0x108d: 84, - 0x109d: 84, - 0x135d: 84, - 0x135e: 84, - 0x135f: 84, - 0x1712: 84, - 0x1713: 84, - 0x1714: 84, - 0x1732: 84, - 0x1733: 84, - 0x1752: 84, - 0x1753: 84, - 0x1772: 84, - 0x1773: 84, - 0x17b4: 84, - 0x17b5: 84, - 0x17b7: 84, - 0x17b8: 84, - 0x17b9: 84, - 0x17ba: 84, - 0x17bb: 84, - 0x17bc: 84, - 0x17bd: 84, - 0x17c6: 84, - 0x17c9: 84, - 0x17ca: 84, - 0x17cb: 84, - 0x17cc: 84, - 0x17cd: 84, - 0x17ce: 84, - 0x17cf: 84, - 0x17d0: 84, - 0x17d1: 84, - 0x17d2: 84, - 0x17d3: 84, - 0x17dd: 84, - 0x1807: 68, - 0x180a: 67, - 0x180b: 84, - 0x180c: 84, - 0x180d: 84, - 0x180f: 84, - 0x1820: 68, - 0x1821: 68, - 0x1822: 68, - 0x1823: 68, - 0x1824: 68, - 0x1825: 68, - 0x1826: 68, - 0x1827: 68, - 0x1828: 68, - 0x1829: 68, - 0x182a: 68, - 0x182b: 68, - 0x182c: 68, - 0x182d: 68, - 0x182e: 68, - 0x182f: 68, - 0x1830: 68, - 0x1831: 68, - 0x1832: 68, - 0x1833: 68, - 0x1834: 68, - 0x1835: 68, - 0x1836: 68, - 0x1837: 68, - 0x1838: 68, - 0x1839: 68, - 0x183a: 68, - 0x183b: 68, - 0x183c: 68, - 0x183d: 68, - 0x183e: 68, - 0x183f: 68, - 0x1840: 68, - 0x1841: 68, - 0x1842: 68, - 0x1843: 68, - 0x1844: 68, - 0x1845: 68, - 0x1846: 68, - 0x1847: 68, - 0x1848: 68, - 0x1849: 68, - 0x184a: 68, - 0x184b: 68, - 0x184c: 68, - 0x184d: 68, - 0x184e: 68, - 0x184f: 68, - 0x1850: 68, - 0x1851: 68, - 0x1852: 68, - 0x1853: 68, - 0x1854: 68, - 0x1855: 68, - 0x1856: 68, - 0x1857: 68, - 0x1858: 68, - 0x1859: 68, - 0x185a: 68, - 0x185b: 68, - 0x185c: 68, - 0x185d: 68, - 0x185e: 68, - 0x185f: 68, - 0x1860: 68, - 0x1861: 68, - 0x1862: 68, - 0x1863: 68, - 0x1864: 68, - 0x1865: 68, - 0x1866: 68, - 0x1867: 68, - 0x1868: 68, - 0x1869: 68, - 0x186a: 68, - 0x186b: 68, - 0x186c: 68, - 0x186d: 68, - 0x186e: 68, - 0x186f: 68, - 0x1870: 68, - 0x1871: 68, - 0x1872: 68, - 0x1873: 68, - 0x1874: 68, - 0x1875: 68, - 0x1876: 68, - 0x1877: 68, - 0x1878: 68, - 0x1885: 84, - 0x1886: 84, - 0x1887: 68, - 0x1888: 68, - 0x1889: 68, - 0x188a: 68, - 0x188b: 68, - 0x188c: 68, - 0x188d: 68, - 0x188e: 68, - 0x188f: 68, - 0x1890: 68, - 0x1891: 68, - 0x1892: 68, - 0x1893: 68, - 0x1894: 68, - 0x1895: 68, - 0x1896: 68, - 0x1897: 68, - 0x1898: 68, - 0x1899: 68, - 0x189a: 68, - 0x189b: 68, - 0x189c: 68, - 0x189d: 68, - 0x189e: 68, - 0x189f: 68, - 0x18a0: 68, - 0x18a1: 68, - 0x18a2: 68, - 0x18a3: 68, - 0x18a4: 68, - 0x18a5: 68, - 0x18a6: 68, - 0x18a7: 68, - 0x18a8: 68, - 0x18a9: 84, - 0x18aa: 68, - 0x1920: 84, - 0x1921: 84, - 0x1922: 84, - 0x1927: 84, - 0x1928: 84, - 0x1932: 84, - 0x1939: 84, - 0x193a: 84, - 0x193b: 84, - 0x1a17: 84, - 0x1a18: 84, - 0x1a1b: 84, - 0x1a56: 84, - 0x1a58: 84, - 0x1a59: 84, - 0x1a5a: 84, - 0x1a5b: 84, - 0x1a5c: 84, - 0x1a5d: 84, - 0x1a5e: 84, - 0x1a60: 84, - 0x1a62: 84, - 0x1a65: 84, - 0x1a66: 84, - 0x1a67: 84, - 0x1a68: 84, - 0x1a69: 84, - 0x1a6a: 84, - 0x1a6b: 84, - 0x1a6c: 84, - 0x1a73: 84, - 0x1a74: 84, - 0x1a75: 84, - 0x1a76: 84, - 0x1a77: 84, - 0x1a78: 84, - 0x1a79: 84, - 0x1a7a: 84, - 0x1a7b: 84, - 0x1a7c: 84, - 0x1a7f: 84, - 0x1ab0: 84, - 0x1ab1: 84, - 0x1ab2: 84, - 0x1ab3: 84, - 0x1ab4: 84, - 0x1ab5: 84, - 0x1ab6: 84, - 0x1ab7: 84, - 0x1ab8: 84, - 0x1ab9: 84, - 0x1aba: 84, - 0x1abb: 84, - 0x1abc: 84, - 0x1abd: 84, - 0x1abe: 84, - 0x1abf: 84, - 0x1ac0: 84, - 0x1ac1: 84, - 0x1ac2: 84, - 0x1ac3: 84, - 0x1ac4: 84, - 0x1ac5: 84, - 0x1ac6: 84, - 0x1ac7: 84, - 0x1ac8: 84, - 0x1ac9: 84, - 0x1aca: 84, - 0x1acb: 84, - 0x1acc: 84, - 0x1acd: 84, - 0x1ace: 84, - 0x1b00: 84, - 0x1b01: 84, - 0x1b02: 84, - 0x1b03: 84, - 0x1b34: 84, - 0x1b36: 84, - 0x1b37: 84, - 0x1b38: 84, - 0x1b39: 84, - 0x1b3a: 84, - 0x1b3c: 84, - 0x1b42: 84, - 0x1b6b: 84, - 0x1b6c: 84, - 0x1b6d: 84, - 0x1b6e: 84, - 0x1b6f: 84, - 0x1b70: 84, - 0x1b71: 84, - 0x1b72: 84, - 0x1b73: 84, - 0x1b80: 84, - 0x1b81: 84, - 0x1ba2: 84, - 0x1ba3: 84, - 0x1ba4: 84, - 0x1ba5: 84, - 0x1ba8: 84, - 0x1ba9: 84, - 0x1bab: 84, - 0x1bac: 84, - 0x1bad: 84, - 0x1be6: 84, - 0x1be8: 84, - 0x1be9: 84, - 0x1bed: 84, - 0x1bef: 84, - 0x1bf0: 84, - 0x1bf1: 84, - 0x1c2c: 84, - 0x1c2d: 84, - 0x1c2e: 84, - 0x1c2f: 84, - 0x1c30: 84, - 0x1c31: 84, - 0x1c32: 84, - 0x1c33: 84, - 0x1c36: 84, - 0x1c37: 84, - 0x1cd0: 84, - 0x1cd1: 84, - 0x1cd2: 84, - 0x1cd4: 84, - 0x1cd5: 84, - 0x1cd6: 84, - 0x1cd7: 84, - 0x1cd8: 84, - 0x1cd9: 84, - 0x1cda: 84, - 0x1cdb: 84, - 0x1cdc: 84, - 0x1cdd: 84, - 0x1cde: 84, - 0x1cdf: 84, - 0x1ce0: 84, - 0x1ce2: 84, - 0x1ce3: 84, - 0x1ce4: 84, - 0x1ce5: 84, - 0x1ce6: 84, - 0x1ce7: 84, - 0x1ce8: 84, - 0x1ced: 84, - 0x1cf4: 84, - 0x1cf8: 84, - 0x1cf9: 84, - 0x1dc0: 84, - 0x1dc1: 84, - 0x1dc2: 84, - 0x1dc3: 84, - 0x1dc4: 84, - 0x1dc5: 84, - 0x1dc6: 84, - 0x1dc7: 84, - 0x1dc8: 84, - 0x1dc9: 84, - 0x1dca: 84, - 0x1dcb: 84, - 0x1dcc: 84, - 0x1dcd: 84, - 0x1dce: 84, - 0x1dcf: 84, - 0x1dd0: 84, - 0x1dd1: 84, - 0x1dd2: 84, - 0x1dd3: 84, - 0x1dd4: 84, - 0x1dd5: 84, - 0x1dd6: 84, - 0x1dd7: 84, - 0x1dd8: 84, - 0x1dd9: 84, - 0x1dda: 84, - 0x1ddb: 84, - 0x1ddc: 84, - 0x1ddd: 84, - 0x1dde: 84, - 0x1ddf: 84, - 0x1de0: 84, - 0x1de1: 84, - 0x1de2: 84, - 0x1de3: 84, - 0x1de4: 84, - 0x1de5: 84, - 0x1de6: 84, - 0x1de7: 84, - 0x1de8: 84, - 0x1de9: 84, - 0x1dea: 84, - 0x1deb: 84, - 0x1dec: 84, - 0x1ded: 84, - 0x1dee: 84, - 0x1def: 84, - 0x1df0: 84, - 0x1df1: 84, - 0x1df2: 84, - 0x1df3: 84, - 0x1df4: 84, - 0x1df5: 84, - 0x1df6: 84, - 0x1df7: 84, - 0x1df8: 84, - 0x1df9: 84, - 0x1dfa: 84, - 0x1dfb: 84, - 0x1dfc: 84, - 0x1dfd: 84, - 0x1dfe: 84, - 0x1dff: 84, - 0x200b: 84, - 0x200d: 67, - 0x200e: 84, - 0x200f: 84, - 0x202a: 84, - 0x202b: 84, - 0x202c: 84, - 0x202d: 84, - 0x202e: 84, - 0x2060: 84, - 0x2061: 84, - 0x2062: 84, - 0x2063: 84, - 0x2064: 84, - 0x206a: 84, - 0x206b: 84, - 0x206c: 84, - 0x206d: 84, - 0x206e: 84, - 0x206f: 84, - 0x20d0: 84, - 0x20d1: 84, - 0x20d2: 84, - 0x20d3: 84, - 0x20d4: 84, - 0x20d5: 84, - 0x20d6: 84, - 0x20d7: 84, - 0x20d8: 84, - 0x20d9: 84, - 0x20da: 84, - 0x20db: 84, - 0x20dc: 84, - 0x20dd: 84, - 0x20de: 84, - 0x20df: 84, - 0x20e0: 84, - 0x20e1: 84, - 0x20e2: 84, - 0x20e3: 84, - 0x20e4: 84, - 0x20e5: 84, - 0x20e6: 84, - 0x20e7: 84, - 0x20e8: 84, - 0x20e9: 84, - 0x20ea: 84, - 0x20eb: 84, - 0x20ec: 84, - 0x20ed: 84, - 0x20ee: 84, - 0x20ef: 84, - 0x20f0: 84, - 0x2cef: 84, - 0x2cf0: 84, - 0x2cf1: 84, - 0x2d7f: 84, - 0x2de0: 84, - 0x2de1: 84, - 0x2de2: 84, - 0x2de3: 84, - 0x2de4: 84, - 0x2de5: 84, - 0x2de6: 84, - 0x2de7: 84, - 0x2de8: 84, - 0x2de9: 84, - 0x2dea: 84, - 0x2deb: 84, - 0x2dec: 84, - 0x2ded: 84, - 0x2dee: 84, - 0x2def: 84, - 0x2df0: 84, - 0x2df1: 84, - 0x2df2: 84, - 0x2df3: 84, - 0x2df4: 84, - 0x2df5: 84, - 0x2df6: 84, - 0x2df7: 84, - 0x2df8: 84, - 0x2df9: 84, - 0x2dfa: 84, - 0x2dfb: 84, - 0x2dfc: 84, - 0x2dfd: 84, - 0x2dfe: 84, - 0x2dff: 84, - 0x302a: 84, - 0x302b: 84, - 0x302c: 84, - 0x302d: 84, - 0x3099: 84, - 0x309a: 84, - 0xa66f: 84, - 0xa670: 84, - 0xa671: 84, - 0xa672: 84, - 0xa674: 84, - 0xa675: 84, - 0xa676: 84, - 0xa677: 84, - 0xa678: 84, - 0xa679: 84, - 0xa67a: 84, - 0xa67b: 84, - 0xa67c: 84, - 0xa67d: 84, - 0xa69e: 84, - 0xa69f: 84, - 0xa6f0: 84, - 0xa6f1: 84, - 0xa802: 84, - 0xa806: 84, - 0xa80b: 84, - 0xa825: 84, - 0xa826: 84, - 0xa82c: 84, - 0xa840: 68, - 0xa841: 68, - 0xa842: 68, - 0xa843: 68, - 0xa844: 68, - 0xa845: 68, - 0xa846: 68, - 0xa847: 68, - 0xa848: 68, - 0xa849: 68, - 0xa84a: 68, - 0xa84b: 68, - 0xa84c: 68, - 0xa84d: 68, - 0xa84e: 68, - 0xa84f: 68, - 0xa850: 68, - 0xa851: 68, - 0xa852: 68, - 0xa853: 68, - 0xa854: 68, - 0xa855: 68, - 0xa856: 68, - 0xa857: 68, - 0xa858: 68, - 0xa859: 68, - 0xa85a: 68, - 0xa85b: 68, - 0xa85c: 68, - 0xa85d: 68, - 0xa85e: 68, - 0xa85f: 68, - 0xa860: 68, - 0xa861: 68, - 0xa862: 68, - 0xa863: 68, - 0xa864: 68, - 0xa865: 68, - 0xa866: 68, - 0xa867: 68, - 0xa868: 68, - 0xa869: 68, - 0xa86a: 68, - 0xa86b: 68, - 0xa86c: 68, - 0xa86d: 68, - 0xa86e: 68, - 0xa86f: 68, - 0xa870: 68, - 0xa871: 68, - 0xa872: 76, - 0xa8c4: 84, - 0xa8c5: 84, - 0xa8e0: 84, - 0xa8e1: 84, - 0xa8e2: 84, - 0xa8e3: 84, - 0xa8e4: 84, - 0xa8e5: 84, - 0xa8e6: 84, - 0xa8e7: 84, - 0xa8e8: 84, - 0xa8e9: 84, - 0xa8ea: 84, - 0xa8eb: 84, - 0xa8ec: 84, - 0xa8ed: 84, - 0xa8ee: 84, - 0xa8ef: 84, - 0xa8f0: 84, - 0xa8f1: 84, - 0xa8ff: 84, - 0xa926: 84, - 0xa927: 84, - 0xa928: 84, - 0xa929: 84, - 0xa92a: 84, - 0xa92b: 84, - 0xa92c: 84, - 0xa92d: 84, - 0xa947: 84, - 0xa948: 84, - 0xa949: 84, - 0xa94a: 84, - 0xa94b: 84, - 0xa94c: 84, - 0xa94d: 84, - 0xa94e: 84, - 0xa94f: 84, - 0xa950: 84, - 0xa951: 84, - 0xa980: 84, - 0xa981: 84, - 0xa982: 84, - 0xa9b3: 84, - 0xa9b6: 84, - 0xa9b7: 84, - 0xa9b8: 84, - 0xa9b9: 84, - 0xa9bc: 84, - 0xa9bd: 84, - 0xa9e5: 84, - 0xaa29: 84, - 0xaa2a: 84, - 0xaa2b: 84, - 0xaa2c: 84, - 0xaa2d: 84, - 0xaa2e: 84, - 0xaa31: 84, - 0xaa32: 84, - 0xaa35: 84, - 0xaa36: 84, - 0xaa43: 84, - 0xaa4c: 84, - 0xaa7c: 84, - 0xaab0: 84, - 0xaab2: 84, - 0xaab3: 84, - 0xaab4: 84, - 0xaab7: 84, - 0xaab8: 84, - 0xaabe: 84, - 0xaabf: 84, - 0xaac1: 84, - 0xaaec: 84, - 0xaaed: 84, - 0xaaf6: 84, - 0xabe5: 84, - 0xabe8: 84, - 0xabed: 84, - 0xfb1e: 84, - 0xfe00: 84, - 0xfe01: 84, - 0xfe02: 84, - 0xfe03: 84, - 0xfe04: 84, - 0xfe05: 84, - 0xfe06: 84, - 0xfe07: 84, - 0xfe08: 84, - 0xfe09: 84, - 0xfe0a: 84, - 0xfe0b: 84, - 0xfe0c: 84, - 0xfe0d: 84, - 0xfe0e: 84, - 0xfe0f: 84, - 0xfe20: 84, - 0xfe21: 84, - 0xfe22: 84, - 0xfe23: 84, - 0xfe24: 84, - 0xfe25: 84, - 0xfe26: 84, - 0xfe27: 84, - 0xfe28: 84, - 0xfe29: 84, - 0xfe2a: 84, - 0xfe2b: 84, - 0xfe2c: 84, - 0xfe2d: 84, - 0xfe2e: 84, - 0xfe2f: 84, - 0xfeff: 84, - 0xfff9: 84, - 0xfffa: 84, - 0xfffb: 84, - 0x101fd: 84, - 0x102e0: 84, - 0x10376: 84, - 0x10377: 84, - 0x10378: 84, - 0x10379: 84, - 0x1037a: 84, - 0x10a01: 84, - 0x10a02: 84, - 0x10a03: 84, - 0x10a05: 84, - 0x10a06: 84, - 0x10a0c: 84, - 0x10a0d: 84, - 0x10a0e: 84, - 0x10a0f: 84, - 0x10a38: 84, - 0x10a39: 84, - 0x10a3a: 84, - 0x10a3f: 84, - 0x10ac0: 68, - 0x10ac1: 68, - 0x10ac2: 68, - 0x10ac3: 68, - 0x10ac4: 68, - 0x10ac5: 82, - 0x10ac7: 82, - 0x10ac9: 82, - 0x10aca: 82, - 0x10acd: 76, - 0x10ace: 82, - 0x10acf: 82, - 0x10ad0: 82, - 0x10ad1: 82, - 0x10ad2: 82, - 0x10ad3: 68, - 0x10ad4: 68, - 0x10ad5: 68, - 0x10ad6: 68, - 0x10ad7: 76, - 0x10ad8: 68, - 0x10ad9: 68, - 0x10ada: 68, - 0x10adb: 68, - 0x10adc: 68, - 0x10add: 82, - 0x10ade: 68, - 0x10adf: 68, - 0x10ae0: 68, - 0x10ae1: 82, - 0x10ae4: 82, - 0x10ae5: 84, - 0x10ae6: 84, - 0x10aeb: 68, - 0x10aec: 68, - 0x10aed: 68, - 0x10aee: 68, - 0x10aef: 82, - 0x10b80: 68, - 0x10b81: 82, - 0x10b82: 68, - 0x10b83: 82, - 0x10b84: 82, - 0x10b85: 82, - 0x10b86: 68, - 0x10b87: 68, - 0x10b88: 68, - 0x10b89: 82, - 0x10b8a: 68, - 0x10b8b: 68, - 0x10b8c: 82, - 0x10b8d: 68, - 0x10b8e: 82, - 0x10b8f: 82, - 0x10b90: 68, - 0x10b91: 82, - 0x10ba9: 82, - 0x10baa: 82, - 0x10bab: 82, - 0x10bac: 82, - 0x10bad: 68, - 0x10bae: 68, - 0x10d00: 76, - 0x10d01: 68, - 0x10d02: 68, - 0x10d03: 68, - 0x10d04: 68, - 0x10d05: 68, - 0x10d06: 68, - 0x10d07: 68, - 0x10d08: 68, - 0x10d09: 68, - 0x10d0a: 68, - 0x10d0b: 68, - 0x10d0c: 68, - 0x10d0d: 68, - 0x10d0e: 68, - 0x10d0f: 68, - 0x10d10: 68, - 0x10d11: 68, - 0x10d12: 68, - 0x10d13: 68, - 0x10d14: 68, - 0x10d15: 68, - 0x10d16: 68, - 0x10d17: 68, - 0x10d18: 68, - 0x10d19: 68, - 0x10d1a: 68, - 0x10d1b: 68, - 0x10d1c: 68, - 0x10d1d: 68, - 0x10d1e: 68, - 0x10d1f: 68, - 0x10d20: 68, - 0x10d21: 68, - 0x10d22: 82, - 0x10d23: 68, - 0x10d24: 84, - 0x10d25: 84, - 0x10d26: 84, - 0x10d27: 84, - 0x10eab: 84, - 0x10eac: 84, - 0x10efd: 84, - 0x10efe: 84, - 0x10eff: 84, - 0x10f30: 68, - 0x10f31: 68, - 0x10f32: 68, - 0x10f33: 82, - 0x10f34: 68, - 0x10f35: 68, - 0x10f36: 68, - 0x10f37: 68, - 0x10f38: 68, - 0x10f39: 68, - 0x10f3a: 68, - 0x10f3b: 68, - 0x10f3c: 68, - 0x10f3d: 68, - 0x10f3e: 68, - 0x10f3f: 68, - 0x10f40: 68, - 0x10f41: 68, - 0x10f42: 68, - 0x10f43: 68, - 0x10f44: 68, - 0x10f46: 84, - 0x10f47: 84, - 0x10f48: 84, - 0x10f49: 84, - 0x10f4a: 84, - 0x10f4b: 84, - 0x10f4c: 84, - 0x10f4d: 84, - 0x10f4e: 84, - 0x10f4f: 84, - 0x10f50: 84, - 0x10f51: 68, - 0x10f52: 68, - 0x10f53: 68, - 0x10f54: 82, - 0x10f70: 68, - 0x10f71: 68, - 0x10f72: 68, - 0x10f73: 68, - 0x10f74: 82, - 0x10f75: 82, - 0x10f76: 68, - 0x10f77: 68, - 0x10f78: 68, - 0x10f79: 68, - 0x10f7a: 68, - 0x10f7b: 68, - 0x10f7c: 68, - 0x10f7d: 68, - 0x10f7e: 68, - 0x10f7f: 68, - 0x10f80: 68, - 0x10f81: 68, - 0x10f82: 84, - 0x10f83: 84, - 0x10f84: 84, - 0x10f85: 84, - 0x10fb0: 68, - 0x10fb2: 68, - 0x10fb3: 68, - 0x10fb4: 82, - 0x10fb5: 82, - 0x10fb6: 82, - 0x10fb8: 68, - 0x10fb9: 82, - 0x10fba: 82, - 0x10fbb: 68, - 0x10fbc: 68, - 0x10fbd: 82, - 0x10fbe: 68, - 0x10fbf: 68, - 0x10fc1: 68, - 0x10fc2: 82, - 0x10fc3: 82, - 0x10fc4: 68, - 0x10fc9: 82, - 0x10fca: 68, - 0x10fcb: 76, - 0x11001: 84, - 0x11038: 84, - 0x11039: 84, - 0x1103a: 84, - 0x1103b: 84, - 0x1103c: 84, - 0x1103d: 84, - 0x1103e: 84, - 0x1103f: 84, - 0x11040: 84, - 0x11041: 84, - 0x11042: 84, - 0x11043: 84, - 0x11044: 84, - 0x11045: 84, - 0x11046: 84, - 0x11070: 84, - 0x11073: 84, - 0x11074: 84, - 0x1107f: 84, - 0x11080: 84, - 0x11081: 84, - 0x110b3: 84, - 0x110b4: 84, - 0x110b5: 84, - 0x110b6: 84, - 0x110b9: 84, - 0x110ba: 84, - 0x110c2: 84, - 0x11100: 84, - 0x11101: 84, - 0x11102: 84, - 0x11127: 84, - 0x11128: 84, - 0x11129: 84, - 0x1112a: 84, - 0x1112b: 84, - 0x1112d: 84, - 0x1112e: 84, - 0x1112f: 84, - 0x11130: 84, - 0x11131: 84, - 0x11132: 84, - 0x11133: 84, - 0x11134: 84, - 0x11173: 84, - 0x11180: 84, - 0x11181: 84, - 0x111b6: 84, - 0x111b7: 84, - 0x111b8: 84, - 0x111b9: 84, - 0x111ba: 84, - 0x111bb: 84, - 0x111bc: 84, - 0x111bd: 84, - 0x111be: 84, - 0x111c9: 84, - 0x111ca: 84, - 0x111cb: 84, - 0x111cc: 84, - 0x111cf: 84, - 0x1122f: 84, - 0x11230: 84, - 0x11231: 84, - 0x11234: 84, - 0x11236: 84, - 0x11237: 84, - 0x1123e: 84, - 0x11241: 84, - 0x112df: 84, - 0x112e3: 84, - 0x112e4: 84, - 0x112e5: 84, - 0x112e6: 84, - 0x112e7: 84, - 0x112e8: 84, - 0x112e9: 84, - 0x112ea: 84, - 0x11300: 84, - 0x11301: 84, - 0x1133b: 84, - 0x1133c: 84, - 0x11340: 84, - 0x11366: 84, - 0x11367: 84, - 0x11368: 84, - 0x11369: 84, - 0x1136a: 84, - 0x1136b: 84, - 0x1136c: 84, - 0x11370: 84, - 0x11371: 84, - 0x11372: 84, - 0x11373: 84, - 0x11374: 84, - 0x11438: 84, - 0x11439: 84, - 0x1143a: 84, - 0x1143b: 84, - 0x1143c: 84, - 0x1143d: 84, - 0x1143e: 84, - 0x1143f: 84, - 0x11442: 84, - 0x11443: 84, - 0x11444: 84, - 0x11446: 84, - 0x1145e: 84, - 0x114b3: 84, - 0x114b4: 84, - 0x114b5: 84, - 0x114b6: 84, - 0x114b7: 84, - 0x114b8: 84, - 0x114ba: 84, - 0x114bf: 84, - 0x114c0: 84, - 0x114c2: 84, - 0x114c3: 84, - 0x115b2: 84, - 0x115b3: 84, - 0x115b4: 84, - 0x115b5: 84, - 0x115bc: 84, - 0x115bd: 84, - 0x115bf: 84, - 0x115c0: 84, - 0x115dc: 84, - 0x115dd: 84, - 0x11633: 84, - 0x11634: 84, - 0x11635: 84, - 0x11636: 84, - 0x11637: 84, - 0x11638: 84, - 0x11639: 84, - 0x1163a: 84, - 0x1163d: 84, - 0x1163f: 84, - 0x11640: 84, - 0x116ab: 84, - 0x116ad: 84, - 0x116b0: 84, - 0x116b1: 84, - 0x116b2: 84, - 0x116b3: 84, - 0x116b4: 84, - 0x116b5: 84, - 0x116b7: 84, - 0x1171d: 84, - 0x1171e: 84, - 0x1171f: 84, - 0x11722: 84, - 0x11723: 84, - 0x11724: 84, - 0x11725: 84, - 0x11727: 84, - 0x11728: 84, - 0x11729: 84, - 0x1172a: 84, - 0x1172b: 84, - 0x1182f: 84, - 0x11830: 84, - 0x11831: 84, - 0x11832: 84, - 0x11833: 84, - 0x11834: 84, - 0x11835: 84, - 0x11836: 84, - 0x11837: 84, - 0x11839: 84, - 0x1183a: 84, - 0x1193b: 84, - 0x1193c: 84, - 0x1193e: 84, - 0x11943: 84, - 0x119d4: 84, - 0x119d5: 84, - 0x119d6: 84, - 0x119d7: 84, - 0x119da: 84, - 0x119db: 84, - 0x119e0: 84, - 0x11a01: 84, - 0x11a02: 84, - 0x11a03: 84, - 0x11a04: 84, - 0x11a05: 84, - 0x11a06: 84, - 0x11a07: 84, - 0x11a08: 84, - 0x11a09: 84, - 0x11a0a: 84, - 0x11a33: 84, - 0x11a34: 84, - 0x11a35: 84, - 0x11a36: 84, - 0x11a37: 84, - 0x11a38: 84, - 0x11a3b: 84, - 0x11a3c: 84, - 0x11a3d: 84, - 0x11a3e: 84, - 0x11a47: 84, - 0x11a51: 84, - 0x11a52: 84, - 0x11a53: 84, - 0x11a54: 84, - 0x11a55: 84, - 0x11a56: 84, - 0x11a59: 84, - 0x11a5a: 84, - 0x11a5b: 84, - 0x11a8a: 84, - 0x11a8b: 84, - 0x11a8c: 84, - 0x11a8d: 84, - 0x11a8e: 84, - 0x11a8f: 84, - 0x11a90: 84, - 0x11a91: 84, - 0x11a92: 84, - 0x11a93: 84, - 0x11a94: 84, - 0x11a95: 84, - 0x11a96: 84, - 0x11a98: 84, - 0x11a99: 84, - 0x11c30: 84, - 0x11c31: 84, - 0x11c32: 84, - 0x11c33: 84, - 0x11c34: 84, - 0x11c35: 84, - 0x11c36: 84, - 0x11c38: 84, - 0x11c39: 84, - 0x11c3a: 84, - 0x11c3b: 84, - 0x11c3c: 84, - 0x11c3d: 84, - 0x11c3f: 84, - 0x11c92: 84, - 0x11c93: 84, - 0x11c94: 84, - 0x11c95: 84, - 0x11c96: 84, - 0x11c97: 84, - 0x11c98: 84, - 0x11c99: 84, - 0x11c9a: 84, - 0x11c9b: 84, - 0x11c9c: 84, - 0x11c9d: 84, - 0x11c9e: 84, - 0x11c9f: 84, - 0x11ca0: 84, - 0x11ca1: 84, - 0x11ca2: 84, - 0x11ca3: 84, - 0x11ca4: 84, - 0x11ca5: 84, - 0x11ca6: 84, - 0x11ca7: 84, - 0x11caa: 84, - 0x11cab: 84, - 0x11cac: 84, - 0x11cad: 84, - 0x11cae: 84, - 0x11caf: 84, - 0x11cb0: 84, - 0x11cb2: 84, - 0x11cb3: 84, - 0x11cb5: 84, - 0x11cb6: 84, - 0x11d31: 84, - 0x11d32: 84, - 0x11d33: 84, - 0x11d34: 84, - 0x11d35: 84, - 0x11d36: 84, - 0x11d3a: 84, - 0x11d3c: 84, - 0x11d3d: 84, - 0x11d3f: 84, - 0x11d40: 84, - 0x11d41: 84, - 0x11d42: 84, - 0x11d43: 84, - 0x11d44: 84, - 0x11d45: 84, - 0x11d47: 84, - 0x11d90: 84, - 0x11d91: 84, - 0x11d95: 84, - 0x11d97: 84, - 0x11ef3: 84, - 0x11ef4: 84, - 0x11f00: 84, - 0x11f01: 84, - 0x11f36: 84, - 0x11f37: 84, - 0x11f38: 84, - 0x11f39: 84, - 0x11f3a: 84, - 0x11f40: 84, - 0x11f42: 84, - 0x13430: 84, - 0x13431: 84, - 0x13432: 84, - 0x13433: 84, - 0x13434: 84, - 0x13435: 84, - 0x13436: 84, - 0x13437: 84, - 0x13438: 84, - 0x13439: 84, - 0x1343a: 84, - 0x1343b: 84, - 0x1343c: 84, - 0x1343d: 84, - 0x1343e: 84, - 0x1343f: 84, - 0x13440: 84, - 0x13447: 84, - 0x13448: 84, - 0x13449: 84, - 0x1344a: 84, - 0x1344b: 84, - 0x1344c: 84, - 0x1344d: 84, - 0x1344e: 84, - 0x1344f: 84, - 0x13450: 84, - 0x13451: 84, - 0x13452: 84, - 0x13453: 84, - 0x13454: 84, - 0x13455: 84, - 0x16af0: 84, - 0x16af1: 84, - 0x16af2: 84, - 0x16af3: 84, - 0x16af4: 84, - 0x16b30: 84, - 0x16b31: 84, - 0x16b32: 84, - 0x16b33: 84, - 0x16b34: 84, - 0x16b35: 84, - 0x16b36: 84, - 0x16f4f: 84, - 0x16f8f: 84, - 0x16f90: 84, - 0x16f91: 84, - 0x16f92: 84, - 0x16fe4: 84, - 0x1bc9d: 84, - 0x1bc9e: 84, - 0x1bca0: 84, - 0x1bca1: 84, - 0x1bca2: 84, - 0x1bca3: 84, - 0x1cf00: 84, - 0x1cf01: 84, - 0x1cf02: 84, - 0x1cf03: 84, - 0x1cf04: 84, - 0x1cf05: 84, - 0x1cf06: 84, - 0x1cf07: 84, - 0x1cf08: 84, - 0x1cf09: 84, - 0x1cf0a: 84, - 0x1cf0b: 84, - 0x1cf0c: 84, - 0x1cf0d: 84, - 0x1cf0e: 84, - 0x1cf0f: 84, - 0x1cf10: 84, - 0x1cf11: 84, - 0x1cf12: 84, - 0x1cf13: 84, - 0x1cf14: 84, - 0x1cf15: 84, - 0x1cf16: 84, - 0x1cf17: 84, - 0x1cf18: 84, - 0x1cf19: 84, - 0x1cf1a: 84, - 0x1cf1b: 84, - 0x1cf1c: 84, - 0x1cf1d: 84, - 0x1cf1e: 84, - 0x1cf1f: 84, - 0x1cf20: 84, - 0x1cf21: 84, - 0x1cf22: 84, - 0x1cf23: 84, - 0x1cf24: 84, - 0x1cf25: 84, - 0x1cf26: 84, - 0x1cf27: 84, - 0x1cf28: 84, - 0x1cf29: 84, - 0x1cf2a: 84, - 0x1cf2b: 84, - 0x1cf2c: 84, - 0x1cf2d: 84, - 0x1cf30: 84, - 0x1cf31: 84, - 0x1cf32: 84, - 0x1cf33: 84, - 0x1cf34: 84, - 0x1cf35: 84, - 0x1cf36: 84, - 0x1cf37: 84, - 0x1cf38: 84, - 0x1cf39: 84, - 0x1cf3a: 84, - 0x1cf3b: 84, - 0x1cf3c: 84, - 0x1cf3d: 84, - 0x1cf3e: 84, - 0x1cf3f: 84, - 0x1cf40: 84, - 0x1cf41: 84, - 0x1cf42: 84, - 0x1cf43: 84, - 0x1cf44: 84, - 0x1cf45: 84, - 0x1cf46: 84, - 0x1d167: 84, - 0x1d168: 84, - 0x1d169: 84, - 0x1d173: 84, - 0x1d174: 84, - 0x1d175: 84, - 0x1d176: 84, - 0x1d177: 84, - 0x1d178: 84, - 0x1d179: 84, - 0x1d17a: 84, - 0x1d17b: 84, - 0x1d17c: 84, - 0x1d17d: 84, - 0x1d17e: 84, - 0x1d17f: 84, - 0x1d180: 84, - 0x1d181: 84, - 0x1d182: 84, - 0x1d185: 84, - 0x1d186: 84, - 0x1d187: 84, - 0x1d188: 84, - 0x1d189: 84, - 0x1d18a: 84, - 0x1d18b: 84, - 0x1d1aa: 84, - 0x1d1ab: 84, - 0x1d1ac: 84, - 0x1d1ad: 84, - 0x1d242: 84, - 0x1d243: 84, - 0x1d244: 84, - 0x1da00: 84, - 0x1da01: 84, - 0x1da02: 84, - 0x1da03: 84, - 0x1da04: 84, - 0x1da05: 84, - 0x1da06: 84, - 0x1da07: 84, - 0x1da08: 84, - 0x1da09: 84, - 0x1da0a: 84, - 0x1da0b: 84, - 0x1da0c: 84, - 0x1da0d: 84, - 0x1da0e: 84, - 0x1da0f: 84, - 0x1da10: 84, - 0x1da11: 84, - 0x1da12: 84, - 0x1da13: 84, - 0x1da14: 84, - 0x1da15: 84, - 0x1da16: 84, - 0x1da17: 84, - 0x1da18: 84, - 0x1da19: 84, - 0x1da1a: 84, - 0x1da1b: 84, - 0x1da1c: 84, - 0x1da1d: 84, - 0x1da1e: 84, - 0x1da1f: 84, - 0x1da20: 84, - 0x1da21: 84, - 0x1da22: 84, - 0x1da23: 84, - 0x1da24: 84, - 0x1da25: 84, - 0x1da26: 84, - 0x1da27: 84, - 0x1da28: 84, - 0x1da29: 84, - 0x1da2a: 84, - 0x1da2b: 84, - 0x1da2c: 84, - 0x1da2d: 84, - 0x1da2e: 84, - 0x1da2f: 84, - 0x1da30: 84, - 0x1da31: 84, - 0x1da32: 84, - 0x1da33: 84, - 0x1da34: 84, - 0x1da35: 84, - 0x1da36: 84, - 0x1da3b: 84, - 0x1da3c: 84, - 0x1da3d: 84, - 0x1da3e: 84, - 0x1da3f: 84, - 0x1da40: 84, - 0x1da41: 84, - 0x1da42: 84, - 0x1da43: 84, - 0x1da44: 84, - 0x1da45: 84, - 0x1da46: 84, - 0x1da47: 84, - 0x1da48: 84, - 0x1da49: 84, - 0x1da4a: 84, - 0x1da4b: 84, - 0x1da4c: 84, - 0x1da4d: 84, - 0x1da4e: 84, - 0x1da4f: 84, - 0x1da50: 84, - 0x1da51: 84, - 0x1da52: 84, - 0x1da53: 84, - 0x1da54: 84, - 0x1da55: 84, - 0x1da56: 84, - 0x1da57: 84, - 0x1da58: 84, - 0x1da59: 84, - 0x1da5a: 84, - 0x1da5b: 84, - 0x1da5c: 84, - 0x1da5d: 84, - 0x1da5e: 84, - 0x1da5f: 84, - 0x1da60: 84, - 0x1da61: 84, - 0x1da62: 84, - 0x1da63: 84, - 0x1da64: 84, - 0x1da65: 84, - 0x1da66: 84, - 0x1da67: 84, - 0x1da68: 84, - 0x1da69: 84, - 0x1da6a: 84, - 0x1da6b: 84, - 0x1da6c: 84, - 0x1da75: 84, - 0x1da84: 84, - 0x1da9b: 84, - 0x1da9c: 84, - 0x1da9d: 84, - 0x1da9e: 84, - 0x1da9f: 84, - 0x1daa1: 84, - 0x1daa2: 84, - 0x1daa3: 84, - 0x1daa4: 84, - 0x1daa5: 84, - 0x1daa6: 84, - 0x1daa7: 84, - 0x1daa8: 84, - 0x1daa9: 84, - 0x1daaa: 84, - 0x1daab: 84, - 0x1daac: 84, - 0x1daad: 84, - 0x1daae: 84, - 0x1daaf: 84, - 0x1e000: 84, - 0x1e001: 84, - 0x1e002: 84, - 0x1e003: 84, - 0x1e004: 84, - 0x1e005: 84, - 0x1e006: 84, - 0x1e008: 84, - 0x1e009: 84, - 0x1e00a: 84, - 0x1e00b: 84, - 0x1e00c: 84, - 0x1e00d: 84, - 0x1e00e: 84, - 0x1e00f: 84, - 0x1e010: 84, - 0x1e011: 84, - 0x1e012: 84, - 0x1e013: 84, - 0x1e014: 84, - 0x1e015: 84, - 0x1e016: 84, - 0x1e017: 84, - 0x1e018: 84, - 0x1e01b: 84, - 0x1e01c: 84, - 0x1e01d: 84, - 0x1e01e: 84, - 0x1e01f: 84, - 0x1e020: 84, - 0x1e021: 84, - 0x1e023: 84, - 0x1e024: 84, - 0x1e026: 84, - 0x1e027: 84, - 0x1e028: 84, - 0x1e029: 84, - 0x1e02a: 84, - 0x1e08f: 84, - 0x1e130: 84, - 0x1e131: 84, - 0x1e132: 84, - 0x1e133: 84, - 0x1e134: 84, - 0x1e135: 84, - 0x1e136: 84, - 0x1e2ae: 84, - 0x1e2ec: 84, - 0x1e2ed: 84, - 0x1e2ee: 84, - 0x1e2ef: 84, - 0x1e4ec: 84, - 0x1e4ed: 84, - 0x1e4ee: 84, - 0x1e4ef: 84, - 0x1e8d0: 84, - 0x1e8d1: 84, - 0x1e8d2: 84, - 0x1e8d3: 84, - 0x1e8d4: 84, - 0x1e8d5: 84, - 0x1e8d6: 84, - 0x1e900: 68, - 0x1e901: 68, - 0x1e902: 68, - 0x1e903: 68, - 0x1e904: 68, - 0x1e905: 68, - 0x1e906: 68, - 0x1e907: 68, - 0x1e908: 68, - 0x1e909: 68, - 0x1e90a: 68, - 0x1e90b: 68, - 0x1e90c: 68, - 0x1e90d: 68, - 0x1e90e: 68, - 0x1e90f: 68, - 0x1e910: 68, - 0x1e911: 68, - 0x1e912: 68, - 0x1e913: 68, - 0x1e914: 68, - 0x1e915: 68, - 0x1e916: 68, - 0x1e917: 68, - 0x1e918: 68, - 0x1e919: 68, - 0x1e91a: 68, - 0x1e91b: 68, - 0x1e91c: 68, - 0x1e91d: 68, - 0x1e91e: 68, - 0x1e91f: 68, - 0x1e920: 68, - 0x1e921: 68, - 0x1e922: 68, - 0x1e923: 68, - 0x1e924: 68, - 0x1e925: 68, - 0x1e926: 68, - 0x1e927: 68, - 0x1e928: 68, - 0x1e929: 68, - 0x1e92a: 68, - 0x1e92b: 68, - 0x1e92c: 68, - 0x1e92d: 68, - 0x1e92e: 68, - 0x1e92f: 68, - 0x1e930: 68, - 0x1e931: 68, - 0x1e932: 68, - 0x1e933: 68, - 0x1e934: 68, - 0x1e935: 68, - 0x1e936: 68, - 0x1e937: 68, - 0x1e938: 68, - 0x1e939: 68, - 0x1e93a: 68, - 0x1e93b: 68, - 0x1e93c: 68, - 0x1e93d: 68, - 0x1e93e: 68, - 0x1e93f: 68, - 0x1e940: 68, - 0x1e941: 68, - 0x1e942: 68, - 0x1e943: 68, - 0x1e944: 84, - 0x1e945: 84, - 0x1e946: 84, - 0x1e947: 84, - 0x1e948: 84, - 0x1e949: 84, - 0x1e94a: 84, - 0x1e94b: 84, - 0xe0001: 84, - 0xe0020: 84, - 0xe0021: 84, - 0xe0022: 84, - 0xe0023: 84, - 0xe0024: 84, - 0xe0025: 84, - 0xe0026: 84, - 0xe0027: 84, - 0xe0028: 84, - 0xe0029: 84, - 0xe002a: 84, - 0xe002b: 84, - 0xe002c: 84, - 0xe002d: 84, - 0xe002e: 84, - 0xe002f: 84, - 0xe0030: 84, - 0xe0031: 84, - 0xe0032: 84, - 0xe0033: 84, - 0xe0034: 84, - 0xe0035: 84, - 0xe0036: 84, - 0xe0037: 84, - 0xe0038: 84, - 0xe0039: 84, - 0xe003a: 84, - 0xe003b: 84, - 0xe003c: 84, - 0xe003d: 84, - 0xe003e: 84, - 0xe003f: 84, - 0xe0040: 84, - 0xe0041: 84, - 0xe0042: 84, - 0xe0043: 84, - 0xe0044: 84, - 0xe0045: 84, - 0xe0046: 84, - 0xe0047: 84, - 0xe0048: 84, - 0xe0049: 84, - 0xe004a: 84, - 0xe004b: 84, - 0xe004c: 84, - 0xe004d: 84, - 0xe004e: 84, - 0xe004f: 84, - 0xe0050: 84, - 0xe0051: 84, - 0xe0052: 84, - 0xe0053: 84, - 0xe0054: 84, - 0xe0055: 84, - 0xe0056: 84, - 0xe0057: 84, - 0xe0058: 84, - 0xe0059: 84, - 0xe005a: 84, - 0xe005b: 84, - 0xe005c: 84, - 0xe005d: 84, - 0xe005e: 84, - 0xe005f: 84, - 0xe0060: 84, - 0xe0061: 84, - 0xe0062: 84, - 0xe0063: 84, - 0xe0064: 84, - 0xe0065: 84, - 0xe0066: 84, - 0xe0067: 84, - 0xe0068: 84, - 0xe0069: 84, - 0xe006a: 84, - 0xe006b: 84, - 0xe006c: 84, - 0xe006d: 84, - 0xe006e: 84, - 0xe006f: 84, - 0xe0070: 84, - 0xe0071: 84, - 0xe0072: 84, - 0xe0073: 84, - 0xe0074: 84, - 0xe0075: 84, - 0xe0076: 84, - 0xe0077: 84, - 0xe0078: 84, - 0xe0079: 84, - 0xe007a: 84, - 0xe007b: 84, - 0xe007c: 84, - 0xe007d: 84, - 0xe007e: 84, - 0xe007f: 84, - 0xe0100: 84, - 0xe0101: 84, - 0xe0102: 84, - 0xe0103: 84, - 0xe0104: 84, - 0xe0105: 84, - 0xe0106: 84, - 0xe0107: 84, - 0xe0108: 84, - 0xe0109: 84, - 0xe010a: 84, - 0xe010b: 84, - 0xe010c: 84, - 0xe010d: 84, - 0xe010e: 84, - 0xe010f: 84, - 0xe0110: 84, - 0xe0111: 84, - 0xe0112: 84, - 0xe0113: 84, - 0xe0114: 84, - 0xe0115: 84, - 0xe0116: 84, - 0xe0117: 84, - 0xe0118: 84, - 0xe0119: 84, - 0xe011a: 84, - 0xe011b: 84, - 0xe011c: 84, - 0xe011d: 84, - 0xe011e: 84, - 0xe011f: 84, - 0xe0120: 84, - 0xe0121: 84, - 0xe0122: 84, - 0xe0123: 84, - 0xe0124: 84, - 0xe0125: 84, - 0xe0126: 84, - 0xe0127: 84, - 0xe0128: 84, - 0xe0129: 84, - 0xe012a: 84, - 0xe012b: 84, - 0xe012c: 84, - 0xe012d: 84, - 0xe012e: 84, - 0xe012f: 84, - 0xe0130: 84, - 0xe0131: 84, - 0xe0132: 84, - 0xe0133: 84, - 0xe0134: 84, - 0xe0135: 84, - 0xe0136: 84, - 0xe0137: 84, - 0xe0138: 84, - 0xe0139: 84, - 0xe013a: 84, - 0xe013b: 84, - 0xe013c: 84, - 0xe013d: 84, - 0xe013e: 84, - 0xe013f: 84, - 0xe0140: 84, - 0xe0141: 84, - 0xe0142: 84, - 0xe0143: 84, - 0xe0144: 84, - 0xe0145: 84, - 0xe0146: 84, - 0xe0147: 84, - 0xe0148: 84, - 0xe0149: 84, - 0xe014a: 84, - 0xe014b: 84, - 0xe014c: 84, - 0xe014d: 84, - 0xe014e: 84, - 0xe014f: 84, - 0xe0150: 84, - 0xe0151: 84, - 0xe0152: 84, - 0xe0153: 84, - 0xe0154: 84, - 0xe0155: 84, - 0xe0156: 84, - 0xe0157: 84, - 0xe0158: 84, - 0xe0159: 84, - 0xe015a: 84, - 0xe015b: 84, - 0xe015c: 84, - 0xe015d: 84, - 0xe015e: 84, - 0xe015f: 84, - 0xe0160: 84, - 0xe0161: 84, - 0xe0162: 84, - 0xe0163: 84, - 0xe0164: 84, - 0xe0165: 84, - 0xe0166: 84, - 0xe0167: 84, - 0xe0168: 84, - 0xe0169: 84, - 0xe016a: 84, - 0xe016b: 84, - 0xe016c: 84, - 0xe016d: 84, - 0xe016e: 84, - 0xe016f: 84, - 0xe0170: 84, - 0xe0171: 84, - 0xe0172: 84, - 0xe0173: 84, - 0xe0174: 84, - 0xe0175: 84, - 0xe0176: 84, - 0xe0177: 84, - 0xe0178: 84, - 0xe0179: 84, - 0xe017a: 84, - 0xe017b: 84, - 0xe017c: 84, - 0xe017d: 84, - 0xe017e: 84, - 0xe017f: 84, - 0xe0180: 84, - 0xe0181: 84, - 0xe0182: 84, - 0xe0183: 84, - 0xe0184: 84, - 0xe0185: 84, - 0xe0186: 84, - 0xe0187: 84, - 0xe0188: 84, - 0xe0189: 84, - 0xe018a: 84, - 0xe018b: 84, - 0xe018c: 84, - 0xe018d: 84, - 0xe018e: 84, - 0xe018f: 84, - 0xe0190: 84, - 0xe0191: 84, - 0xe0192: 84, - 0xe0193: 84, - 0xe0194: 84, - 0xe0195: 84, - 0xe0196: 84, - 0xe0197: 84, - 0xe0198: 84, - 0xe0199: 84, - 0xe019a: 84, - 0xe019b: 84, - 0xe019c: 84, - 0xe019d: 84, - 0xe019e: 84, - 0xe019f: 84, - 0xe01a0: 84, - 0xe01a1: 84, - 0xe01a2: 84, - 0xe01a3: 84, - 0xe01a4: 84, - 0xe01a5: 84, - 0xe01a6: 84, - 0xe01a7: 84, - 0xe01a8: 84, - 0xe01a9: 84, - 0xe01aa: 84, - 0xe01ab: 84, - 0xe01ac: 84, - 0xe01ad: 84, - 0xe01ae: 84, - 0xe01af: 84, - 0xe01b0: 84, - 0xe01b1: 84, - 0xe01b2: 84, - 0xe01b3: 84, - 0xe01b4: 84, - 0xe01b5: 84, - 0xe01b6: 84, - 0xe01b7: 84, - 0xe01b8: 84, - 0xe01b9: 84, - 0xe01ba: 84, - 0xe01bb: 84, - 0xe01bc: 84, - 0xe01bd: 84, - 0xe01be: 84, - 0xe01bf: 84, - 0xe01c0: 84, - 0xe01c1: 84, - 0xe01c2: 84, - 0xe01c3: 84, - 0xe01c4: 84, - 0xe01c5: 84, - 0xe01c6: 84, - 0xe01c7: 84, - 0xe01c8: 84, - 0xe01c9: 84, - 0xe01ca: 84, - 0xe01cb: 84, - 0xe01cc: 84, - 0xe01cd: 84, - 0xe01ce: 84, - 0xe01cf: 84, - 0xe01d0: 84, - 0xe01d1: 84, - 0xe01d2: 84, - 0xe01d3: 84, - 0xe01d4: 84, - 0xe01d5: 84, - 0xe01d6: 84, - 0xe01d7: 84, - 0xe01d8: 84, - 0xe01d9: 84, - 0xe01da: 84, - 0xe01db: 84, - 0xe01dc: 84, - 0xe01dd: 84, - 0xe01de: 84, - 0xe01df: 84, - 0xe01e0: 84, - 0xe01e1: 84, - 0xe01e2: 84, - 0xe01e3: 84, - 0xe01e4: 84, - 0xe01e5: 84, - 0xe01e6: 84, - 0xe01e7: 84, - 0xe01e8: 84, - 0xe01e9: 84, - 0xe01ea: 84, - 0xe01eb: 84, - 0xe01ec: 84, - 0xe01ed: 84, - 0xe01ee: 84, - 0xe01ef: 84, -} -codepoint_classes = { - 'PVALID': ( - 0x2d0000002e, - 0x300000003a, - 0x610000007b, - 0xdf000000f7, - 0xf800000100, - 0x10100000102, - 0x10300000104, - 0x10500000106, - 0x10700000108, - 0x1090000010a, - 0x10b0000010c, - 0x10d0000010e, - 0x10f00000110, - 0x11100000112, - 0x11300000114, - 0x11500000116, - 0x11700000118, - 0x1190000011a, - 0x11b0000011c, - 0x11d0000011e, - 0x11f00000120, - 0x12100000122, - 0x12300000124, - 0x12500000126, - 0x12700000128, - 0x1290000012a, - 0x12b0000012c, - 0x12d0000012e, - 0x12f00000130, - 0x13100000132, - 0x13500000136, - 0x13700000139, - 0x13a0000013b, - 0x13c0000013d, - 0x13e0000013f, - 0x14200000143, - 0x14400000145, - 0x14600000147, - 0x14800000149, - 0x14b0000014c, - 0x14d0000014e, - 0x14f00000150, - 0x15100000152, - 0x15300000154, - 0x15500000156, - 0x15700000158, - 0x1590000015a, - 0x15b0000015c, - 0x15d0000015e, - 0x15f00000160, - 0x16100000162, - 0x16300000164, - 0x16500000166, - 0x16700000168, - 0x1690000016a, - 0x16b0000016c, - 0x16d0000016e, - 0x16f00000170, - 0x17100000172, - 0x17300000174, - 0x17500000176, - 0x17700000178, - 0x17a0000017b, - 0x17c0000017d, - 0x17e0000017f, - 0x18000000181, - 0x18300000184, - 0x18500000186, - 0x18800000189, - 0x18c0000018e, - 0x19200000193, - 0x19500000196, - 0x1990000019c, - 0x19e0000019f, - 0x1a1000001a2, - 0x1a3000001a4, - 0x1a5000001a6, - 0x1a8000001a9, - 0x1aa000001ac, - 0x1ad000001ae, - 0x1b0000001b1, - 0x1b4000001b5, - 0x1b6000001b7, - 0x1b9000001bc, - 0x1bd000001c4, - 0x1ce000001cf, - 0x1d0000001d1, - 0x1d2000001d3, - 0x1d4000001d5, - 0x1d6000001d7, - 0x1d8000001d9, - 0x1da000001db, - 0x1dc000001de, - 0x1df000001e0, - 0x1e1000001e2, - 0x1e3000001e4, - 0x1e5000001e6, - 0x1e7000001e8, - 0x1e9000001ea, - 0x1eb000001ec, - 0x1ed000001ee, - 0x1ef000001f1, - 0x1f5000001f6, - 0x1f9000001fa, - 0x1fb000001fc, - 0x1fd000001fe, - 0x1ff00000200, - 0x20100000202, - 0x20300000204, - 0x20500000206, - 0x20700000208, - 0x2090000020a, - 0x20b0000020c, - 0x20d0000020e, - 0x20f00000210, - 0x21100000212, - 0x21300000214, - 0x21500000216, - 0x21700000218, - 0x2190000021a, - 0x21b0000021c, - 0x21d0000021e, - 0x21f00000220, - 0x22100000222, - 0x22300000224, - 0x22500000226, - 0x22700000228, - 0x2290000022a, - 0x22b0000022c, - 0x22d0000022e, - 0x22f00000230, - 0x23100000232, - 0x2330000023a, - 0x23c0000023d, - 0x23f00000241, - 0x24200000243, - 0x24700000248, - 0x2490000024a, - 0x24b0000024c, - 0x24d0000024e, - 0x24f000002b0, - 0x2b9000002c2, - 0x2c6000002d2, - 0x2ec000002ed, - 0x2ee000002ef, - 0x30000000340, - 0x34200000343, - 0x3460000034f, - 0x35000000370, - 0x37100000372, - 0x37300000374, - 0x37700000378, - 0x37b0000037e, - 0x39000000391, - 0x3ac000003cf, - 0x3d7000003d8, - 0x3d9000003da, - 0x3db000003dc, - 0x3dd000003de, - 0x3df000003e0, - 0x3e1000003e2, - 0x3e3000003e4, - 0x3e5000003e6, - 0x3e7000003e8, - 0x3e9000003ea, - 0x3eb000003ec, - 0x3ed000003ee, - 0x3ef000003f0, - 0x3f3000003f4, - 0x3f8000003f9, - 0x3fb000003fd, - 0x43000000460, - 0x46100000462, - 0x46300000464, - 0x46500000466, - 0x46700000468, - 0x4690000046a, - 0x46b0000046c, - 0x46d0000046e, - 0x46f00000470, - 0x47100000472, - 0x47300000474, - 0x47500000476, - 0x47700000478, - 0x4790000047a, - 0x47b0000047c, - 0x47d0000047e, - 0x47f00000480, - 0x48100000482, - 0x48300000488, - 0x48b0000048c, - 0x48d0000048e, - 0x48f00000490, - 0x49100000492, - 0x49300000494, - 0x49500000496, - 0x49700000498, - 0x4990000049a, - 0x49b0000049c, - 0x49d0000049e, - 0x49f000004a0, - 0x4a1000004a2, - 0x4a3000004a4, - 0x4a5000004a6, - 0x4a7000004a8, - 0x4a9000004aa, - 0x4ab000004ac, - 0x4ad000004ae, - 0x4af000004b0, - 0x4b1000004b2, - 0x4b3000004b4, - 0x4b5000004b6, - 0x4b7000004b8, - 0x4b9000004ba, - 0x4bb000004bc, - 0x4bd000004be, - 0x4bf000004c0, - 0x4c2000004c3, - 0x4c4000004c5, - 0x4c6000004c7, - 0x4c8000004c9, - 0x4ca000004cb, - 0x4cc000004cd, - 0x4ce000004d0, - 0x4d1000004d2, - 0x4d3000004d4, - 0x4d5000004d6, - 0x4d7000004d8, - 0x4d9000004da, - 0x4db000004dc, - 0x4dd000004de, - 0x4df000004e0, - 0x4e1000004e2, - 0x4e3000004e4, - 0x4e5000004e6, - 0x4e7000004e8, - 0x4e9000004ea, - 0x4eb000004ec, - 0x4ed000004ee, - 0x4ef000004f0, - 0x4f1000004f2, - 0x4f3000004f4, - 0x4f5000004f6, - 0x4f7000004f8, - 0x4f9000004fa, - 0x4fb000004fc, - 0x4fd000004fe, - 0x4ff00000500, - 0x50100000502, - 0x50300000504, - 0x50500000506, - 0x50700000508, - 0x5090000050a, - 0x50b0000050c, - 0x50d0000050e, - 0x50f00000510, - 0x51100000512, - 0x51300000514, - 0x51500000516, - 0x51700000518, - 0x5190000051a, - 0x51b0000051c, - 0x51d0000051e, - 0x51f00000520, - 0x52100000522, - 0x52300000524, - 0x52500000526, - 0x52700000528, - 0x5290000052a, - 0x52b0000052c, - 0x52d0000052e, - 0x52f00000530, - 0x5590000055a, - 0x56000000587, - 0x58800000589, - 0x591000005be, - 0x5bf000005c0, - 0x5c1000005c3, - 0x5c4000005c6, - 0x5c7000005c8, - 0x5d0000005eb, - 0x5ef000005f3, - 0x6100000061b, - 0x62000000640, - 0x64100000660, - 0x66e00000675, - 0x679000006d4, - 0x6d5000006dd, - 0x6df000006e9, - 0x6ea000006f0, - 0x6fa00000700, - 0x7100000074b, - 0x74d000007b2, - 0x7c0000007f6, - 0x7fd000007fe, - 0x8000000082e, - 0x8400000085c, - 0x8600000086b, - 0x87000000888, - 0x8890000088f, - 0x898000008e2, - 0x8e300000958, - 0x96000000964, - 0x96600000970, - 0x97100000984, - 0x9850000098d, - 0x98f00000991, - 0x993000009a9, - 0x9aa000009b1, - 0x9b2000009b3, - 0x9b6000009ba, - 0x9bc000009c5, - 0x9c7000009c9, - 0x9cb000009cf, - 0x9d7000009d8, - 0x9e0000009e4, - 0x9e6000009f2, - 0x9fc000009fd, - 0x9fe000009ff, - 0xa0100000a04, - 0xa0500000a0b, - 0xa0f00000a11, - 0xa1300000a29, - 0xa2a00000a31, - 0xa3200000a33, - 0xa3500000a36, - 0xa3800000a3a, - 0xa3c00000a3d, - 0xa3e00000a43, - 0xa4700000a49, - 0xa4b00000a4e, - 0xa5100000a52, - 0xa5c00000a5d, - 0xa6600000a76, - 0xa8100000a84, - 0xa8500000a8e, - 0xa8f00000a92, - 0xa9300000aa9, - 0xaaa00000ab1, - 0xab200000ab4, - 0xab500000aba, - 0xabc00000ac6, - 0xac700000aca, - 0xacb00000ace, - 0xad000000ad1, - 0xae000000ae4, - 0xae600000af0, - 0xaf900000b00, - 0xb0100000b04, - 0xb0500000b0d, - 0xb0f00000b11, - 0xb1300000b29, - 0xb2a00000b31, - 0xb3200000b34, - 0xb3500000b3a, - 0xb3c00000b45, - 0xb4700000b49, - 0xb4b00000b4e, - 0xb5500000b58, - 0xb5f00000b64, - 0xb6600000b70, - 0xb7100000b72, - 0xb8200000b84, - 0xb8500000b8b, - 0xb8e00000b91, - 0xb9200000b96, - 0xb9900000b9b, - 0xb9c00000b9d, - 0xb9e00000ba0, - 0xba300000ba5, - 0xba800000bab, - 0xbae00000bba, - 0xbbe00000bc3, - 0xbc600000bc9, - 0xbca00000bce, - 0xbd000000bd1, - 0xbd700000bd8, - 0xbe600000bf0, - 0xc0000000c0d, - 0xc0e00000c11, - 0xc1200000c29, - 0xc2a00000c3a, - 0xc3c00000c45, - 0xc4600000c49, - 0xc4a00000c4e, - 0xc5500000c57, - 0xc5800000c5b, - 0xc5d00000c5e, - 0xc6000000c64, - 0xc6600000c70, - 0xc8000000c84, - 0xc8500000c8d, - 0xc8e00000c91, - 0xc9200000ca9, - 0xcaa00000cb4, - 0xcb500000cba, - 0xcbc00000cc5, - 0xcc600000cc9, - 0xcca00000cce, - 0xcd500000cd7, - 0xcdd00000cdf, - 0xce000000ce4, - 0xce600000cf0, - 0xcf100000cf4, - 0xd0000000d0d, - 0xd0e00000d11, - 0xd1200000d45, - 0xd4600000d49, - 0xd4a00000d4f, - 0xd5400000d58, - 0xd5f00000d64, - 0xd6600000d70, - 0xd7a00000d80, - 0xd8100000d84, - 0xd8500000d97, - 0xd9a00000db2, - 0xdb300000dbc, - 0xdbd00000dbe, - 0xdc000000dc7, - 0xdca00000dcb, - 0xdcf00000dd5, - 0xdd600000dd7, - 0xdd800000de0, - 0xde600000df0, - 0xdf200000df4, - 0xe0100000e33, - 0xe3400000e3b, - 0xe4000000e4f, - 0xe5000000e5a, - 0xe8100000e83, - 0xe8400000e85, - 0xe8600000e8b, - 0xe8c00000ea4, - 0xea500000ea6, - 0xea700000eb3, - 0xeb400000ebe, - 0xec000000ec5, - 0xec600000ec7, - 0xec800000ecf, - 0xed000000eda, - 0xede00000ee0, - 0xf0000000f01, - 0xf0b00000f0c, - 0xf1800000f1a, - 0xf2000000f2a, - 0xf3500000f36, - 0xf3700000f38, - 0xf3900000f3a, - 0xf3e00000f43, - 0xf4400000f48, - 0xf4900000f4d, - 0xf4e00000f52, - 0xf5300000f57, - 0xf5800000f5c, - 0xf5d00000f69, - 0xf6a00000f6d, - 0xf7100000f73, - 0xf7400000f75, - 0xf7a00000f81, - 0xf8200000f85, - 0xf8600000f93, - 0xf9400000f98, - 0xf9900000f9d, - 0xf9e00000fa2, - 0xfa300000fa7, - 0xfa800000fac, - 0xfad00000fb9, - 0xfba00000fbd, - 0xfc600000fc7, - 0x10000000104a, - 0x10500000109e, - 0x10d0000010fb, - 0x10fd00001100, - 0x120000001249, - 0x124a0000124e, - 0x125000001257, - 0x125800001259, - 0x125a0000125e, - 0x126000001289, - 0x128a0000128e, - 0x1290000012b1, - 0x12b2000012b6, - 0x12b8000012bf, - 0x12c0000012c1, - 0x12c2000012c6, - 0x12c8000012d7, - 0x12d800001311, - 0x131200001316, - 0x13180000135b, - 0x135d00001360, - 0x138000001390, - 0x13a0000013f6, - 0x14010000166d, - 0x166f00001680, - 0x16810000169b, - 0x16a0000016eb, - 0x16f1000016f9, - 0x170000001716, - 0x171f00001735, - 0x174000001754, - 0x17600000176d, - 0x176e00001771, - 0x177200001774, - 0x1780000017b4, - 0x17b6000017d4, - 0x17d7000017d8, - 0x17dc000017de, - 0x17e0000017ea, - 0x18100000181a, - 0x182000001879, - 0x1880000018ab, - 0x18b0000018f6, - 0x19000000191f, - 0x19200000192c, - 0x19300000193c, - 0x19460000196e, - 0x197000001975, - 0x1980000019ac, - 0x19b0000019ca, - 0x19d0000019da, - 0x1a0000001a1c, - 0x1a2000001a5f, - 0x1a6000001a7d, - 0x1a7f00001a8a, - 0x1a9000001a9a, - 0x1aa700001aa8, - 0x1ab000001abe, - 0x1abf00001acf, - 0x1b0000001b4d, - 0x1b5000001b5a, - 0x1b6b00001b74, - 0x1b8000001bf4, - 0x1c0000001c38, - 0x1c4000001c4a, - 0x1c4d00001c7e, - 0x1cd000001cd3, - 0x1cd400001cfb, - 0x1d0000001d2c, - 0x1d2f00001d30, - 0x1d3b00001d3c, - 0x1d4e00001d4f, - 0x1d6b00001d78, - 0x1d7900001d9b, - 0x1dc000001e00, - 0x1e0100001e02, - 0x1e0300001e04, - 0x1e0500001e06, - 0x1e0700001e08, - 0x1e0900001e0a, - 0x1e0b00001e0c, - 0x1e0d00001e0e, - 0x1e0f00001e10, - 0x1e1100001e12, - 0x1e1300001e14, - 0x1e1500001e16, - 0x1e1700001e18, - 0x1e1900001e1a, - 0x1e1b00001e1c, - 0x1e1d00001e1e, - 0x1e1f00001e20, - 0x1e2100001e22, - 0x1e2300001e24, - 0x1e2500001e26, - 0x1e2700001e28, - 0x1e2900001e2a, - 0x1e2b00001e2c, - 0x1e2d00001e2e, - 0x1e2f00001e30, - 0x1e3100001e32, - 0x1e3300001e34, - 0x1e3500001e36, - 0x1e3700001e38, - 0x1e3900001e3a, - 0x1e3b00001e3c, - 0x1e3d00001e3e, - 0x1e3f00001e40, - 0x1e4100001e42, - 0x1e4300001e44, - 0x1e4500001e46, - 0x1e4700001e48, - 0x1e4900001e4a, - 0x1e4b00001e4c, - 0x1e4d00001e4e, - 0x1e4f00001e50, - 0x1e5100001e52, - 0x1e5300001e54, - 0x1e5500001e56, - 0x1e5700001e58, - 0x1e5900001e5a, - 0x1e5b00001e5c, - 0x1e5d00001e5e, - 0x1e5f00001e60, - 0x1e6100001e62, - 0x1e6300001e64, - 0x1e6500001e66, - 0x1e6700001e68, - 0x1e6900001e6a, - 0x1e6b00001e6c, - 0x1e6d00001e6e, - 0x1e6f00001e70, - 0x1e7100001e72, - 0x1e7300001e74, - 0x1e7500001e76, - 0x1e7700001e78, - 0x1e7900001e7a, - 0x1e7b00001e7c, - 0x1e7d00001e7e, - 0x1e7f00001e80, - 0x1e8100001e82, - 0x1e8300001e84, - 0x1e8500001e86, - 0x1e8700001e88, - 0x1e8900001e8a, - 0x1e8b00001e8c, - 0x1e8d00001e8e, - 0x1e8f00001e90, - 0x1e9100001e92, - 0x1e9300001e94, - 0x1e9500001e9a, - 0x1e9c00001e9e, - 0x1e9f00001ea0, - 0x1ea100001ea2, - 0x1ea300001ea4, - 0x1ea500001ea6, - 0x1ea700001ea8, - 0x1ea900001eaa, - 0x1eab00001eac, - 0x1ead00001eae, - 0x1eaf00001eb0, - 0x1eb100001eb2, - 0x1eb300001eb4, - 0x1eb500001eb6, - 0x1eb700001eb8, - 0x1eb900001eba, - 0x1ebb00001ebc, - 0x1ebd00001ebe, - 0x1ebf00001ec0, - 0x1ec100001ec2, - 0x1ec300001ec4, - 0x1ec500001ec6, - 0x1ec700001ec8, - 0x1ec900001eca, - 0x1ecb00001ecc, - 0x1ecd00001ece, - 0x1ecf00001ed0, - 0x1ed100001ed2, - 0x1ed300001ed4, - 0x1ed500001ed6, - 0x1ed700001ed8, - 0x1ed900001eda, - 0x1edb00001edc, - 0x1edd00001ede, - 0x1edf00001ee0, - 0x1ee100001ee2, - 0x1ee300001ee4, - 0x1ee500001ee6, - 0x1ee700001ee8, - 0x1ee900001eea, - 0x1eeb00001eec, - 0x1eed00001eee, - 0x1eef00001ef0, - 0x1ef100001ef2, - 0x1ef300001ef4, - 0x1ef500001ef6, - 0x1ef700001ef8, - 0x1ef900001efa, - 0x1efb00001efc, - 0x1efd00001efe, - 0x1eff00001f08, - 0x1f1000001f16, - 0x1f2000001f28, - 0x1f3000001f38, - 0x1f4000001f46, - 0x1f5000001f58, - 0x1f6000001f68, - 0x1f7000001f71, - 0x1f7200001f73, - 0x1f7400001f75, - 0x1f7600001f77, - 0x1f7800001f79, - 0x1f7a00001f7b, - 0x1f7c00001f7d, - 0x1fb000001fb2, - 0x1fb600001fb7, - 0x1fc600001fc7, - 0x1fd000001fd3, - 0x1fd600001fd8, - 0x1fe000001fe3, - 0x1fe400001fe8, - 0x1ff600001ff7, - 0x214e0000214f, - 0x218400002185, - 0x2c3000002c60, - 0x2c6100002c62, - 0x2c6500002c67, - 0x2c6800002c69, - 0x2c6a00002c6b, - 0x2c6c00002c6d, - 0x2c7100002c72, - 0x2c7300002c75, - 0x2c7600002c7c, - 0x2c8100002c82, - 0x2c8300002c84, - 0x2c8500002c86, - 0x2c8700002c88, - 0x2c8900002c8a, - 0x2c8b00002c8c, - 0x2c8d00002c8e, - 0x2c8f00002c90, - 0x2c9100002c92, - 0x2c9300002c94, - 0x2c9500002c96, - 0x2c9700002c98, - 0x2c9900002c9a, - 0x2c9b00002c9c, - 0x2c9d00002c9e, - 0x2c9f00002ca0, - 0x2ca100002ca2, - 0x2ca300002ca4, - 0x2ca500002ca6, - 0x2ca700002ca8, - 0x2ca900002caa, - 0x2cab00002cac, - 0x2cad00002cae, - 0x2caf00002cb0, - 0x2cb100002cb2, - 0x2cb300002cb4, - 0x2cb500002cb6, - 0x2cb700002cb8, - 0x2cb900002cba, - 0x2cbb00002cbc, - 0x2cbd00002cbe, - 0x2cbf00002cc0, - 0x2cc100002cc2, - 0x2cc300002cc4, - 0x2cc500002cc6, - 0x2cc700002cc8, - 0x2cc900002cca, - 0x2ccb00002ccc, - 0x2ccd00002cce, - 0x2ccf00002cd0, - 0x2cd100002cd2, - 0x2cd300002cd4, - 0x2cd500002cd6, - 0x2cd700002cd8, - 0x2cd900002cda, - 0x2cdb00002cdc, - 0x2cdd00002cde, - 0x2cdf00002ce0, - 0x2ce100002ce2, - 0x2ce300002ce5, - 0x2cec00002ced, - 0x2cee00002cf2, - 0x2cf300002cf4, - 0x2d0000002d26, - 0x2d2700002d28, - 0x2d2d00002d2e, - 0x2d3000002d68, - 0x2d7f00002d97, - 0x2da000002da7, - 0x2da800002daf, - 0x2db000002db7, - 0x2db800002dbf, - 0x2dc000002dc7, - 0x2dc800002dcf, - 0x2dd000002dd7, - 0x2dd800002ddf, - 0x2de000002e00, - 0x2e2f00002e30, - 0x300500003008, - 0x302a0000302e, - 0x303c0000303d, - 0x304100003097, - 0x30990000309b, - 0x309d0000309f, - 0x30a1000030fb, - 0x30fc000030ff, - 0x310500003130, - 0x31a0000031c0, - 0x31f000003200, - 0x340000004dc0, - 0x4e000000a48d, - 0xa4d00000a4fe, - 0xa5000000a60d, - 0xa6100000a62c, - 0xa6410000a642, - 0xa6430000a644, - 0xa6450000a646, - 0xa6470000a648, - 0xa6490000a64a, - 0xa64b0000a64c, - 0xa64d0000a64e, - 0xa64f0000a650, - 0xa6510000a652, - 0xa6530000a654, - 0xa6550000a656, - 0xa6570000a658, - 0xa6590000a65a, - 0xa65b0000a65c, - 0xa65d0000a65e, - 0xa65f0000a660, - 0xa6610000a662, - 0xa6630000a664, - 0xa6650000a666, - 0xa6670000a668, - 0xa6690000a66a, - 0xa66b0000a66c, - 0xa66d0000a670, - 0xa6740000a67e, - 0xa67f0000a680, - 0xa6810000a682, - 0xa6830000a684, - 0xa6850000a686, - 0xa6870000a688, - 0xa6890000a68a, - 0xa68b0000a68c, - 0xa68d0000a68e, - 0xa68f0000a690, - 0xa6910000a692, - 0xa6930000a694, - 0xa6950000a696, - 0xa6970000a698, - 0xa6990000a69a, - 0xa69b0000a69c, - 0xa69e0000a6e6, - 0xa6f00000a6f2, - 0xa7170000a720, - 0xa7230000a724, - 0xa7250000a726, - 0xa7270000a728, - 0xa7290000a72a, - 0xa72b0000a72c, - 0xa72d0000a72e, - 0xa72f0000a732, - 0xa7330000a734, - 0xa7350000a736, - 0xa7370000a738, - 0xa7390000a73a, - 0xa73b0000a73c, - 0xa73d0000a73e, - 0xa73f0000a740, - 0xa7410000a742, - 0xa7430000a744, - 0xa7450000a746, - 0xa7470000a748, - 0xa7490000a74a, - 0xa74b0000a74c, - 0xa74d0000a74e, - 0xa74f0000a750, - 0xa7510000a752, - 0xa7530000a754, - 0xa7550000a756, - 0xa7570000a758, - 0xa7590000a75a, - 0xa75b0000a75c, - 0xa75d0000a75e, - 0xa75f0000a760, - 0xa7610000a762, - 0xa7630000a764, - 0xa7650000a766, - 0xa7670000a768, - 0xa7690000a76a, - 0xa76b0000a76c, - 0xa76d0000a76e, - 0xa76f0000a770, - 0xa7710000a779, - 0xa77a0000a77b, - 0xa77c0000a77d, - 0xa77f0000a780, - 0xa7810000a782, - 0xa7830000a784, - 0xa7850000a786, - 0xa7870000a789, - 0xa78c0000a78d, - 0xa78e0000a790, - 0xa7910000a792, - 0xa7930000a796, - 0xa7970000a798, - 0xa7990000a79a, - 0xa79b0000a79c, - 0xa79d0000a79e, - 0xa79f0000a7a0, - 0xa7a10000a7a2, - 0xa7a30000a7a4, - 0xa7a50000a7a6, - 0xa7a70000a7a8, - 0xa7a90000a7aa, - 0xa7af0000a7b0, - 0xa7b50000a7b6, - 0xa7b70000a7b8, - 0xa7b90000a7ba, - 0xa7bb0000a7bc, - 0xa7bd0000a7be, - 0xa7bf0000a7c0, - 0xa7c10000a7c2, - 0xa7c30000a7c4, - 0xa7c80000a7c9, - 0xa7ca0000a7cb, - 0xa7d10000a7d2, - 0xa7d30000a7d4, - 0xa7d50000a7d6, - 0xa7d70000a7d8, - 0xa7d90000a7da, - 0xa7f60000a7f8, - 0xa7fa0000a828, - 0xa82c0000a82d, - 0xa8400000a874, - 0xa8800000a8c6, - 0xa8d00000a8da, - 0xa8e00000a8f8, - 0xa8fb0000a8fc, - 0xa8fd0000a92e, - 0xa9300000a954, - 0xa9800000a9c1, - 0xa9cf0000a9da, - 0xa9e00000a9ff, - 0xaa000000aa37, - 0xaa400000aa4e, - 0xaa500000aa5a, - 0xaa600000aa77, - 0xaa7a0000aac3, - 0xaadb0000aade, - 0xaae00000aaf0, - 0xaaf20000aaf7, - 0xab010000ab07, - 0xab090000ab0f, - 0xab110000ab17, - 0xab200000ab27, - 0xab280000ab2f, - 0xab300000ab5b, - 0xab600000ab69, - 0xabc00000abeb, - 0xabec0000abee, - 0xabf00000abfa, - 0xac000000d7a4, - 0xfa0e0000fa10, - 0xfa110000fa12, - 0xfa130000fa15, - 0xfa1f0000fa20, - 0xfa210000fa22, - 0xfa230000fa25, - 0xfa270000fa2a, - 0xfb1e0000fb1f, - 0xfe200000fe30, - 0xfe730000fe74, - 0x100000001000c, - 0x1000d00010027, - 0x100280001003b, - 0x1003c0001003e, - 0x1003f0001004e, - 0x100500001005e, - 0x10080000100fb, - 0x101fd000101fe, - 0x102800001029d, - 0x102a0000102d1, - 0x102e0000102e1, - 0x1030000010320, - 0x1032d00010341, - 0x103420001034a, - 0x103500001037b, - 0x103800001039e, - 0x103a0000103c4, - 0x103c8000103d0, - 0x104280001049e, - 0x104a0000104aa, - 0x104d8000104fc, - 0x1050000010528, - 0x1053000010564, - 0x10597000105a2, - 0x105a3000105b2, - 0x105b3000105ba, - 0x105bb000105bd, - 0x1060000010737, - 0x1074000010756, - 0x1076000010768, - 0x1078000010781, - 0x1080000010806, - 0x1080800010809, - 0x1080a00010836, - 0x1083700010839, - 0x1083c0001083d, - 0x1083f00010856, - 0x1086000010877, - 0x108800001089f, - 0x108e0000108f3, - 0x108f4000108f6, - 0x1090000010916, - 0x109200001093a, - 0x10980000109b8, - 0x109be000109c0, - 0x10a0000010a04, - 0x10a0500010a07, - 0x10a0c00010a14, - 0x10a1500010a18, - 0x10a1900010a36, - 0x10a3800010a3b, - 0x10a3f00010a40, - 0x10a6000010a7d, - 0x10a8000010a9d, - 0x10ac000010ac8, - 0x10ac900010ae7, - 0x10b0000010b36, - 0x10b4000010b56, - 0x10b6000010b73, - 0x10b8000010b92, - 0x10c0000010c49, - 0x10cc000010cf3, - 0x10d0000010d28, - 0x10d3000010d3a, - 0x10e8000010eaa, - 0x10eab00010ead, - 0x10eb000010eb2, - 0x10efd00010f1d, - 0x10f2700010f28, - 0x10f3000010f51, - 0x10f7000010f86, - 0x10fb000010fc5, - 0x10fe000010ff7, - 0x1100000011047, - 0x1106600011076, - 0x1107f000110bb, - 0x110c2000110c3, - 0x110d0000110e9, - 0x110f0000110fa, - 0x1110000011135, - 0x1113600011140, - 0x1114400011148, - 0x1115000011174, - 0x1117600011177, - 0x11180000111c5, - 0x111c9000111cd, - 0x111ce000111db, - 0x111dc000111dd, - 0x1120000011212, - 0x1121300011238, - 0x1123e00011242, - 0x1128000011287, - 0x1128800011289, - 0x1128a0001128e, - 0x1128f0001129e, - 0x1129f000112a9, - 0x112b0000112eb, - 0x112f0000112fa, - 0x1130000011304, - 0x113050001130d, - 0x1130f00011311, - 0x1131300011329, - 0x1132a00011331, - 0x1133200011334, - 0x113350001133a, - 0x1133b00011345, - 0x1134700011349, - 0x1134b0001134e, - 0x1135000011351, - 0x1135700011358, - 0x1135d00011364, - 0x113660001136d, - 0x1137000011375, - 0x114000001144b, - 0x114500001145a, - 0x1145e00011462, - 0x11480000114c6, - 0x114c7000114c8, - 0x114d0000114da, - 0x11580000115b6, - 0x115b8000115c1, - 0x115d8000115de, - 0x1160000011641, - 0x1164400011645, - 0x116500001165a, - 0x11680000116b9, - 0x116c0000116ca, - 0x117000001171b, - 0x1171d0001172c, - 0x117300001173a, - 0x1174000011747, - 0x118000001183b, - 0x118c0000118ea, - 0x118ff00011907, - 0x119090001190a, - 0x1190c00011914, - 0x1191500011917, - 0x1191800011936, - 0x1193700011939, - 0x1193b00011944, - 0x119500001195a, - 0x119a0000119a8, - 0x119aa000119d8, - 0x119da000119e2, - 0x119e3000119e5, - 0x11a0000011a3f, - 0x11a4700011a48, - 0x11a5000011a9a, - 0x11a9d00011a9e, - 0x11ab000011af9, - 0x11c0000011c09, - 0x11c0a00011c37, - 0x11c3800011c41, - 0x11c5000011c5a, - 0x11c7200011c90, - 0x11c9200011ca8, - 0x11ca900011cb7, - 0x11d0000011d07, - 0x11d0800011d0a, - 0x11d0b00011d37, - 0x11d3a00011d3b, - 0x11d3c00011d3e, - 0x11d3f00011d48, - 0x11d5000011d5a, - 0x11d6000011d66, - 0x11d6700011d69, - 0x11d6a00011d8f, - 0x11d9000011d92, - 0x11d9300011d99, - 0x11da000011daa, - 0x11ee000011ef7, - 0x11f0000011f11, - 0x11f1200011f3b, - 0x11f3e00011f43, - 0x11f5000011f5a, - 0x11fb000011fb1, - 0x120000001239a, - 0x1248000012544, - 0x12f9000012ff1, - 0x1300000013430, - 0x1344000013456, - 0x1440000014647, - 0x1680000016a39, - 0x16a4000016a5f, - 0x16a6000016a6a, - 0x16a7000016abf, - 0x16ac000016aca, - 0x16ad000016aee, - 0x16af000016af5, - 0x16b0000016b37, - 0x16b4000016b44, - 0x16b5000016b5a, - 0x16b6300016b78, - 0x16b7d00016b90, - 0x16e6000016e80, - 0x16f0000016f4b, - 0x16f4f00016f88, - 0x16f8f00016fa0, - 0x16fe000016fe2, - 0x16fe300016fe5, - 0x16ff000016ff2, - 0x17000000187f8, - 0x1880000018cd6, - 0x18d0000018d09, - 0x1aff00001aff4, - 0x1aff50001affc, - 0x1affd0001afff, - 0x1b0000001b123, - 0x1b1320001b133, - 0x1b1500001b153, - 0x1b1550001b156, - 0x1b1640001b168, - 0x1b1700001b2fc, - 0x1bc000001bc6b, - 0x1bc700001bc7d, - 0x1bc800001bc89, - 0x1bc900001bc9a, - 0x1bc9d0001bc9f, - 0x1cf000001cf2e, - 0x1cf300001cf47, - 0x1da000001da37, - 0x1da3b0001da6d, - 0x1da750001da76, - 0x1da840001da85, - 0x1da9b0001daa0, - 0x1daa10001dab0, - 0x1df000001df1f, - 0x1df250001df2b, - 0x1e0000001e007, - 0x1e0080001e019, - 0x1e01b0001e022, - 0x1e0230001e025, - 0x1e0260001e02b, - 0x1e08f0001e090, - 0x1e1000001e12d, - 0x1e1300001e13e, - 0x1e1400001e14a, - 0x1e14e0001e14f, - 0x1e2900001e2af, - 0x1e2c00001e2fa, - 0x1e4d00001e4fa, - 0x1e7e00001e7e7, - 0x1e7e80001e7ec, - 0x1e7ed0001e7ef, - 0x1e7f00001e7ff, - 0x1e8000001e8c5, - 0x1e8d00001e8d7, - 0x1e9220001e94c, - 0x1e9500001e95a, - 0x200000002a6e0, - 0x2a7000002b73a, - 0x2b7400002b81e, - 0x2b8200002cea2, - 0x2ceb00002ebe1, - 0x2ebf00002ee5e, - 0x300000003134b, - 0x31350000323b0, - ), - 'CONTEXTJ': ( - 0x200c0000200e, - ), - 'CONTEXTO': ( - 0xb7000000b8, - 0x37500000376, - 0x5f3000005f5, - 0x6600000066a, - 0x6f0000006fa, - 0x30fb000030fc, - ), -} diff --git a/.venv/lib/python3.12/site-packages/idna/intranges.py b/.venv/lib/python3.12/site-packages/idna/intranges.py deleted file mode 100644 index 6a43b04..0000000 --- a/.venv/lib/python3.12/site-packages/idna/intranges.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Given a list of integers, made up of (hopefully) a small number of long runs -of consecutive integers, compute a representation of the form -((start1, end1), (start2, end2) ...). Then answer the question "was x present -in the original list?" in time O(log(# runs)). -""" - -import bisect -from typing import List, Tuple - -def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: - """Represent a list of integers as a sequence of ranges: - ((start_0, end_0), (start_1, end_1), ...), such that the original - integers are exactly those x such that start_i <= x < end_i for some i. - - Ranges are encoded as single integers (start << 32 | end), not as tuples. - """ - - sorted_list = sorted(list_) - ranges = [] - last_write = -1 - for i in range(len(sorted_list)): - if i+1 < len(sorted_list): - if sorted_list[i] == sorted_list[i+1]-1: - continue - current_range = sorted_list[last_write+1:i+1] - ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) - last_write = i - - return tuple(ranges) - -def _encode_range(start: int, end: int) -> int: - return (start << 32) | end - -def _decode_range(r: int) -> Tuple[int, int]: - return (r >> 32), (r & ((1 << 32) - 1)) - - -def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: - """Determine if `int_` falls into one of the ranges in `ranges`.""" - tuple_ = _encode_range(int_, 0) - pos = bisect.bisect_left(ranges, tuple_) - # we could be immediately ahead of a tuple (start, end) - # with start < int_ <= end - if pos > 0: - left, right = _decode_range(ranges[pos-1]) - if left <= int_ < right: - return True - # or we could be immediately behind a tuple (int_, end) - if pos < len(ranges): - left, _ = _decode_range(ranges[pos]) - if left == int_: - return True - return False diff --git a/.venv/lib/python3.12/site-packages/idna/package_data.py b/.venv/lib/python3.12/site-packages/idna/package_data.py deleted file mode 100644 index ed81113..0000000 --- a/.venv/lib/python3.12/site-packages/idna/package_data.py +++ /dev/null @@ -1,2 +0,0 @@ -__version__ = '3.7' - diff --git a/.venv/lib/python3.12/site-packages/idna/py.typed b/.venv/lib/python3.12/site-packages/idna/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/idna/uts46data.py b/.venv/lib/python3.12/site-packages/idna/uts46data.py deleted file mode 100644 index 6a1eddb..0000000 --- a/.venv/lib/python3.12/site-packages/idna/uts46data.py +++ /dev/null @@ -1,8598 +0,0 @@ -# This file is automatically generated by tools/idna-data -# vim: set fileencoding=utf-8 : - -from typing import List, Tuple, Union - - -"""IDNA Mapping Table from UTS46.""" - - -__version__ = '15.1.0' -def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x0, '3'), - (0x1, '3'), - (0x2, '3'), - (0x3, '3'), - (0x4, '3'), - (0x5, '3'), - (0x6, '3'), - (0x7, '3'), - (0x8, '3'), - (0x9, '3'), - (0xA, '3'), - (0xB, '3'), - (0xC, '3'), - (0xD, '3'), - (0xE, '3'), - (0xF, '3'), - (0x10, '3'), - (0x11, '3'), - (0x12, '3'), - (0x13, '3'), - (0x14, '3'), - (0x15, '3'), - (0x16, '3'), - (0x17, '3'), - (0x18, '3'), - (0x19, '3'), - (0x1A, '3'), - (0x1B, '3'), - (0x1C, '3'), - (0x1D, '3'), - (0x1E, '3'), - (0x1F, '3'), - (0x20, '3'), - (0x21, '3'), - (0x22, '3'), - (0x23, '3'), - (0x24, '3'), - (0x25, '3'), - (0x26, '3'), - (0x27, '3'), - (0x28, '3'), - (0x29, '3'), - (0x2A, '3'), - (0x2B, '3'), - (0x2C, '3'), - (0x2D, 'V'), - (0x2E, 'V'), - (0x2F, '3'), - (0x30, 'V'), - (0x31, 'V'), - (0x32, 'V'), - (0x33, 'V'), - (0x34, 'V'), - (0x35, 'V'), - (0x36, 'V'), - (0x37, 'V'), - (0x38, 'V'), - (0x39, 'V'), - (0x3A, '3'), - (0x3B, '3'), - (0x3C, '3'), - (0x3D, '3'), - (0x3E, '3'), - (0x3F, '3'), - (0x40, '3'), - (0x41, 'M', 'a'), - (0x42, 'M', 'b'), - (0x43, 'M', 'c'), - (0x44, 'M', 'd'), - (0x45, 'M', 'e'), - (0x46, 'M', 'f'), - (0x47, 'M', 'g'), - (0x48, 'M', 'h'), - (0x49, 'M', 'i'), - (0x4A, 'M', 'j'), - (0x4B, 'M', 'k'), - (0x4C, 'M', 'l'), - (0x4D, 'M', 'm'), - (0x4E, 'M', 'n'), - (0x4F, 'M', 'o'), - (0x50, 'M', 'p'), - (0x51, 'M', 'q'), - (0x52, 'M', 'r'), - (0x53, 'M', 's'), - (0x54, 'M', 't'), - (0x55, 'M', 'u'), - (0x56, 'M', 'v'), - (0x57, 'M', 'w'), - (0x58, 'M', 'x'), - (0x59, 'M', 'y'), - (0x5A, 'M', 'z'), - (0x5B, '3'), - (0x5C, '3'), - (0x5D, '3'), - (0x5E, '3'), - (0x5F, '3'), - (0x60, '3'), - (0x61, 'V'), - (0x62, 'V'), - (0x63, 'V'), - ] - -def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x64, 'V'), - (0x65, 'V'), - (0x66, 'V'), - (0x67, 'V'), - (0x68, 'V'), - (0x69, 'V'), - (0x6A, 'V'), - (0x6B, 'V'), - (0x6C, 'V'), - (0x6D, 'V'), - (0x6E, 'V'), - (0x6F, 'V'), - (0x70, 'V'), - (0x71, 'V'), - (0x72, 'V'), - (0x73, 'V'), - (0x74, 'V'), - (0x75, 'V'), - (0x76, 'V'), - (0x77, 'V'), - (0x78, 'V'), - (0x79, 'V'), - (0x7A, 'V'), - (0x7B, '3'), - (0x7C, '3'), - (0x7D, '3'), - (0x7E, '3'), - (0x7F, '3'), - (0x80, 'X'), - (0x81, 'X'), - (0x82, 'X'), - (0x83, 'X'), - (0x84, 'X'), - (0x85, 'X'), - (0x86, 'X'), - (0x87, 'X'), - (0x88, 'X'), - (0x89, 'X'), - (0x8A, 'X'), - (0x8B, 'X'), - (0x8C, 'X'), - (0x8D, 'X'), - (0x8E, 'X'), - (0x8F, 'X'), - (0x90, 'X'), - (0x91, 'X'), - (0x92, 'X'), - (0x93, 'X'), - (0x94, 'X'), - (0x95, 'X'), - (0x96, 'X'), - (0x97, 'X'), - (0x98, 'X'), - (0x99, 'X'), - (0x9A, 'X'), - (0x9B, 'X'), - (0x9C, 'X'), - (0x9D, 'X'), - (0x9E, 'X'), - (0x9F, 'X'), - (0xA0, '3', ' '), - (0xA1, 'V'), - (0xA2, 'V'), - (0xA3, 'V'), - (0xA4, 'V'), - (0xA5, 'V'), - (0xA6, 'V'), - (0xA7, 'V'), - (0xA8, '3', ' ̈'), - (0xA9, 'V'), - (0xAA, 'M', 'a'), - (0xAB, 'V'), - (0xAC, 'V'), - (0xAD, 'I'), - (0xAE, 'V'), - (0xAF, '3', ' ̄'), - (0xB0, 'V'), - (0xB1, 'V'), - (0xB2, 'M', '2'), - (0xB3, 'M', '3'), - (0xB4, '3', ' ́'), - (0xB5, 'M', 'μ'), - (0xB6, 'V'), - (0xB7, 'V'), - (0xB8, '3', ' ̧'), - (0xB9, 'M', '1'), - (0xBA, 'M', 'o'), - (0xBB, 'V'), - (0xBC, 'M', '1⁄4'), - (0xBD, 'M', '1⁄2'), - (0xBE, 'M', '3⁄4'), - (0xBF, 'V'), - (0xC0, 'M', 'à'), - (0xC1, 'M', 'á'), - (0xC2, 'M', 'â'), - (0xC3, 'M', 'ã'), - (0xC4, 'M', 'ä'), - (0xC5, 'M', 'å'), - (0xC6, 'M', 'æ'), - (0xC7, 'M', 'ç'), - ] - -def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC8, 'M', 'è'), - (0xC9, 'M', 'é'), - (0xCA, 'M', 'ê'), - (0xCB, 'M', 'ë'), - (0xCC, 'M', 'ì'), - (0xCD, 'M', 'í'), - (0xCE, 'M', 'î'), - (0xCF, 'M', 'ï'), - (0xD0, 'M', 'ð'), - (0xD1, 'M', 'ñ'), - (0xD2, 'M', 'ò'), - (0xD3, 'M', 'ó'), - (0xD4, 'M', 'ô'), - (0xD5, 'M', 'õ'), - (0xD6, 'M', 'ö'), - (0xD7, 'V'), - (0xD8, 'M', 'ø'), - (0xD9, 'M', 'ù'), - (0xDA, 'M', 'ú'), - (0xDB, 'M', 'û'), - (0xDC, 'M', 'ü'), - (0xDD, 'M', 'ý'), - (0xDE, 'M', 'þ'), - (0xDF, 'D', 'ss'), - (0xE0, 'V'), - (0xE1, 'V'), - (0xE2, 'V'), - (0xE3, 'V'), - (0xE4, 'V'), - (0xE5, 'V'), - (0xE6, 'V'), - (0xE7, 'V'), - (0xE8, 'V'), - (0xE9, 'V'), - (0xEA, 'V'), - (0xEB, 'V'), - (0xEC, 'V'), - (0xED, 'V'), - (0xEE, 'V'), - (0xEF, 'V'), - (0xF0, 'V'), - (0xF1, 'V'), - (0xF2, 'V'), - (0xF3, 'V'), - (0xF4, 'V'), - (0xF5, 'V'), - (0xF6, 'V'), - (0xF7, 'V'), - (0xF8, 'V'), - (0xF9, 'V'), - (0xFA, 'V'), - (0xFB, 'V'), - (0xFC, 'V'), - (0xFD, 'V'), - (0xFE, 'V'), - (0xFF, 'V'), - (0x100, 'M', 'ā'), - (0x101, 'V'), - (0x102, 'M', 'ă'), - (0x103, 'V'), - (0x104, 'M', 'ą'), - (0x105, 'V'), - (0x106, 'M', 'ć'), - (0x107, 'V'), - (0x108, 'M', 'ĉ'), - (0x109, 'V'), - (0x10A, 'M', 'ċ'), - (0x10B, 'V'), - (0x10C, 'M', 'č'), - (0x10D, 'V'), - (0x10E, 'M', 'ď'), - (0x10F, 'V'), - (0x110, 'M', 'đ'), - (0x111, 'V'), - (0x112, 'M', 'ē'), - (0x113, 'V'), - (0x114, 'M', 'ĕ'), - (0x115, 'V'), - (0x116, 'M', 'ė'), - (0x117, 'V'), - (0x118, 'M', 'ę'), - (0x119, 'V'), - (0x11A, 'M', 'ě'), - (0x11B, 'V'), - (0x11C, 'M', 'ĝ'), - (0x11D, 'V'), - (0x11E, 'M', 'ğ'), - (0x11F, 'V'), - (0x120, 'M', 'ġ'), - (0x121, 'V'), - (0x122, 'M', 'ģ'), - (0x123, 'V'), - (0x124, 'M', 'ĥ'), - (0x125, 'V'), - (0x126, 'M', 'ħ'), - (0x127, 'V'), - (0x128, 'M', 'ĩ'), - (0x129, 'V'), - (0x12A, 'M', 'ī'), - (0x12B, 'V'), - ] - -def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x12C, 'M', 'ĭ'), - (0x12D, 'V'), - (0x12E, 'M', 'į'), - (0x12F, 'V'), - (0x130, 'M', 'i̇'), - (0x131, 'V'), - (0x132, 'M', 'ij'), - (0x134, 'M', 'ĵ'), - (0x135, 'V'), - (0x136, 'M', 'ķ'), - (0x137, 'V'), - (0x139, 'M', 'ĺ'), - (0x13A, 'V'), - (0x13B, 'M', 'ļ'), - (0x13C, 'V'), - (0x13D, 'M', 'ľ'), - (0x13E, 'V'), - (0x13F, 'M', 'l·'), - (0x141, 'M', 'ł'), - (0x142, 'V'), - (0x143, 'M', 'ń'), - (0x144, 'V'), - (0x145, 'M', 'ņ'), - (0x146, 'V'), - (0x147, 'M', 'ň'), - (0x148, 'V'), - (0x149, 'M', 'ʼn'), - (0x14A, 'M', 'ŋ'), - (0x14B, 'V'), - (0x14C, 'M', 'ō'), - (0x14D, 'V'), - (0x14E, 'M', 'ŏ'), - (0x14F, 'V'), - (0x150, 'M', 'ő'), - (0x151, 'V'), - (0x152, 'M', 'œ'), - (0x153, 'V'), - (0x154, 'M', 'ŕ'), - (0x155, 'V'), - (0x156, 'M', 'ŗ'), - (0x157, 'V'), - (0x158, 'M', 'ř'), - (0x159, 'V'), - (0x15A, 'M', 'ś'), - (0x15B, 'V'), - (0x15C, 'M', 'ŝ'), - (0x15D, 'V'), - (0x15E, 'M', 'ş'), - (0x15F, 'V'), - (0x160, 'M', 'š'), - (0x161, 'V'), - (0x162, 'M', 'ţ'), - (0x163, 'V'), - (0x164, 'M', 'ť'), - (0x165, 'V'), - (0x166, 'M', 'ŧ'), - (0x167, 'V'), - (0x168, 'M', 'ũ'), - (0x169, 'V'), - (0x16A, 'M', 'ū'), - (0x16B, 'V'), - (0x16C, 'M', 'ŭ'), - (0x16D, 'V'), - (0x16E, 'M', 'ů'), - (0x16F, 'V'), - (0x170, 'M', 'ű'), - (0x171, 'V'), - (0x172, 'M', 'ų'), - (0x173, 'V'), - (0x174, 'M', 'ŵ'), - (0x175, 'V'), - (0x176, 'M', 'ŷ'), - (0x177, 'V'), - (0x178, 'M', 'ÿ'), - (0x179, 'M', 'ź'), - (0x17A, 'V'), - (0x17B, 'M', 'ż'), - (0x17C, 'V'), - (0x17D, 'M', 'ž'), - (0x17E, 'V'), - (0x17F, 'M', 's'), - (0x180, 'V'), - (0x181, 'M', 'ɓ'), - (0x182, 'M', 'ƃ'), - (0x183, 'V'), - (0x184, 'M', 'ƅ'), - (0x185, 'V'), - (0x186, 'M', 'ɔ'), - (0x187, 'M', 'ƈ'), - (0x188, 'V'), - (0x189, 'M', 'ɖ'), - (0x18A, 'M', 'ɗ'), - (0x18B, 'M', 'ƌ'), - (0x18C, 'V'), - (0x18E, 'M', 'ǝ'), - (0x18F, 'M', 'ə'), - (0x190, 'M', 'ɛ'), - (0x191, 'M', 'ƒ'), - (0x192, 'V'), - (0x193, 'M', 'ɠ'), - ] - -def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x194, 'M', 'ɣ'), - (0x195, 'V'), - (0x196, 'M', 'ɩ'), - (0x197, 'M', 'ɨ'), - (0x198, 'M', 'ƙ'), - (0x199, 'V'), - (0x19C, 'M', 'ɯ'), - (0x19D, 'M', 'ɲ'), - (0x19E, 'V'), - (0x19F, 'M', 'ɵ'), - (0x1A0, 'M', 'ơ'), - (0x1A1, 'V'), - (0x1A2, 'M', 'ƣ'), - (0x1A3, 'V'), - (0x1A4, 'M', 'ƥ'), - (0x1A5, 'V'), - (0x1A6, 'M', 'ʀ'), - (0x1A7, 'M', 'ƨ'), - (0x1A8, 'V'), - (0x1A9, 'M', 'ʃ'), - (0x1AA, 'V'), - (0x1AC, 'M', 'ƭ'), - (0x1AD, 'V'), - (0x1AE, 'M', 'ʈ'), - (0x1AF, 'M', 'ư'), - (0x1B0, 'V'), - (0x1B1, 'M', 'ʊ'), - (0x1B2, 'M', 'ʋ'), - (0x1B3, 'M', 'ƴ'), - (0x1B4, 'V'), - (0x1B5, 'M', 'ƶ'), - (0x1B6, 'V'), - (0x1B7, 'M', 'ʒ'), - (0x1B8, 'M', 'ƹ'), - (0x1B9, 'V'), - (0x1BC, 'M', 'ƽ'), - (0x1BD, 'V'), - (0x1C4, 'M', 'dž'), - (0x1C7, 'M', 'lj'), - (0x1CA, 'M', 'nj'), - (0x1CD, 'M', 'ǎ'), - (0x1CE, 'V'), - (0x1CF, 'M', 'ǐ'), - (0x1D0, 'V'), - (0x1D1, 'M', 'ǒ'), - (0x1D2, 'V'), - (0x1D3, 'M', 'ǔ'), - (0x1D4, 'V'), - (0x1D5, 'M', 'ǖ'), - (0x1D6, 'V'), - (0x1D7, 'M', 'ǘ'), - (0x1D8, 'V'), - (0x1D9, 'M', 'ǚ'), - (0x1DA, 'V'), - (0x1DB, 'M', 'ǜ'), - (0x1DC, 'V'), - (0x1DE, 'M', 'ǟ'), - (0x1DF, 'V'), - (0x1E0, 'M', 'ǡ'), - (0x1E1, 'V'), - (0x1E2, 'M', 'ǣ'), - (0x1E3, 'V'), - (0x1E4, 'M', 'ǥ'), - (0x1E5, 'V'), - (0x1E6, 'M', 'ǧ'), - (0x1E7, 'V'), - (0x1E8, 'M', 'ǩ'), - (0x1E9, 'V'), - (0x1EA, 'M', 'ǫ'), - (0x1EB, 'V'), - (0x1EC, 'M', 'ǭ'), - (0x1ED, 'V'), - (0x1EE, 'M', 'ǯ'), - (0x1EF, 'V'), - (0x1F1, 'M', 'dz'), - (0x1F4, 'M', 'ǵ'), - (0x1F5, 'V'), - (0x1F6, 'M', 'ƕ'), - (0x1F7, 'M', 'ƿ'), - (0x1F8, 'M', 'ǹ'), - (0x1F9, 'V'), - (0x1FA, 'M', 'ǻ'), - (0x1FB, 'V'), - (0x1FC, 'M', 'ǽ'), - (0x1FD, 'V'), - (0x1FE, 'M', 'ǿ'), - (0x1FF, 'V'), - (0x200, 'M', 'ȁ'), - (0x201, 'V'), - (0x202, 'M', 'ȃ'), - (0x203, 'V'), - (0x204, 'M', 'ȅ'), - (0x205, 'V'), - (0x206, 'M', 'ȇ'), - (0x207, 'V'), - (0x208, 'M', 'ȉ'), - (0x209, 'V'), - (0x20A, 'M', 'ȋ'), - (0x20B, 'V'), - (0x20C, 'M', 'ȍ'), - ] - -def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x20D, 'V'), - (0x20E, 'M', 'ȏ'), - (0x20F, 'V'), - (0x210, 'M', 'ȑ'), - (0x211, 'V'), - (0x212, 'M', 'ȓ'), - (0x213, 'V'), - (0x214, 'M', 'ȕ'), - (0x215, 'V'), - (0x216, 'M', 'ȗ'), - (0x217, 'V'), - (0x218, 'M', 'ș'), - (0x219, 'V'), - (0x21A, 'M', 'ț'), - (0x21B, 'V'), - (0x21C, 'M', 'ȝ'), - (0x21D, 'V'), - (0x21E, 'M', 'ȟ'), - (0x21F, 'V'), - (0x220, 'M', 'ƞ'), - (0x221, 'V'), - (0x222, 'M', 'ȣ'), - (0x223, 'V'), - (0x224, 'M', 'ȥ'), - (0x225, 'V'), - (0x226, 'M', 'ȧ'), - (0x227, 'V'), - (0x228, 'M', 'ȩ'), - (0x229, 'V'), - (0x22A, 'M', 'ȫ'), - (0x22B, 'V'), - (0x22C, 'M', 'ȭ'), - (0x22D, 'V'), - (0x22E, 'M', 'ȯ'), - (0x22F, 'V'), - (0x230, 'M', 'ȱ'), - (0x231, 'V'), - (0x232, 'M', 'ȳ'), - (0x233, 'V'), - (0x23A, 'M', 'ⱥ'), - (0x23B, 'M', 'ȼ'), - (0x23C, 'V'), - (0x23D, 'M', 'ƚ'), - (0x23E, 'M', 'ⱦ'), - (0x23F, 'V'), - (0x241, 'M', 'ɂ'), - (0x242, 'V'), - (0x243, 'M', 'ƀ'), - (0x244, 'M', 'ʉ'), - (0x245, 'M', 'ʌ'), - (0x246, 'M', 'ɇ'), - (0x247, 'V'), - (0x248, 'M', 'ɉ'), - (0x249, 'V'), - (0x24A, 'M', 'ɋ'), - (0x24B, 'V'), - (0x24C, 'M', 'ɍ'), - (0x24D, 'V'), - (0x24E, 'M', 'ɏ'), - (0x24F, 'V'), - (0x2B0, 'M', 'h'), - (0x2B1, 'M', 'ɦ'), - (0x2B2, 'M', 'j'), - (0x2B3, 'M', 'r'), - (0x2B4, 'M', 'ɹ'), - (0x2B5, 'M', 'ɻ'), - (0x2B6, 'M', 'ʁ'), - (0x2B7, 'M', 'w'), - (0x2B8, 'M', 'y'), - (0x2B9, 'V'), - (0x2D8, '3', ' ̆'), - (0x2D9, '3', ' ̇'), - (0x2DA, '3', ' ̊'), - (0x2DB, '3', ' ̨'), - (0x2DC, '3', ' ̃'), - (0x2DD, '3', ' ̋'), - (0x2DE, 'V'), - (0x2E0, 'M', 'ɣ'), - (0x2E1, 'M', 'l'), - (0x2E2, 'M', 's'), - (0x2E3, 'M', 'x'), - (0x2E4, 'M', 'ʕ'), - (0x2E5, 'V'), - (0x340, 'M', '̀'), - (0x341, 'M', '́'), - (0x342, 'V'), - (0x343, 'M', '̓'), - (0x344, 'M', '̈́'), - (0x345, 'M', 'ι'), - (0x346, 'V'), - (0x34F, 'I'), - (0x350, 'V'), - (0x370, 'M', 'ͱ'), - (0x371, 'V'), - (0x372, 'M', 'ͳ'), - (0x373, 'V'), - (0x374, 'M', 'ʹ'), - (0x375, 'V'), - (0x376, 'M', 'ͷ'), - (0x377, 'V'), - ] - -def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x378, 'X'), - (0x37A, '3', ' ι'), - (0x37B, 'V'), - (0x37E, '3', ';'), - (0x37F, 'M', 'ϳ'), - (0x380, 'X'), - (0x384, '3', ' ́'), - (0x385, '3', ' ̈́'), - (0x386, 'M', 'ά'), - (0x387, 'M', '·'), - (0x388, 'M', 'έ'), - (0x389, 'M', 'ή'), - (0x38A, 'M', 'ί'), - (0x38B, 'X'), - (0x38C, 'M', 'ό'), - (0x38D, 'X'), - (0x38E, 'M', 'ύ'), - (0x38F, 'M', 'ώ'), - (0x390, 'V'), - (0x391, 'M', 'α'), - (0x392, 'M', 'β'), - (0x393, 'M', 'γ'), - (0x394, 'M', 'δ'), - (0x395, 'M', 'ε'), - (0x396, 'M', 'ζ'), - (0x397, 'M', 'η'), - (0x398, 'M', 'θ'), - (0x399, 'M', 'ι'), - (0x39A, 'M', 'κ'), - (0x39B, 'M', 'λ'), - (0x39C, 'M', 'μ'), - (0x39D, 'M', 'ν'), - (0x39E, 'M', 'ξ'), - (0x39F, 'M', 'ο'), - (0x3A0, 'M', 'π'), - (0x3A1, 'M', 'ρ'), - (0x3A2, 'X'), - (0x3A3, 'M', 'σ'), - (0x3A4, 'M', 'τ'), - (0x3A5, 'M', 'υ'), - (0x3A6, 'M', 'φ'), - (0x3A7, 'M', 'χ'), - (0x3A8, 'M', 'ψ'), - (0x3A9, 'M', 'ω'), - (0x3AA, 'M', 'ϊ'), - (0x3AB, 'M', 'ϋ'), - (0x3AC, 'V'), - (0x3C2, 'D', 'σ'), - (0x3C3, 'V'), - (0x3CF, 'M', 'ϗ'), - (0x3D0, 'M', 'β'), - (0x3D1, 'M', 'θ'), - (0x3D2, 'M', 'υ'), - (0x3D3, 'M', 'ύ'), - (0x3D4, 'M', 'ϋ'), - (0x3D5, 'M', 'φ'), - (0x3D6, 'M', 'π'), - (0x3D7, 'V'), - (0x3D8, 'M', 'ϙ'), - (0x3D9, 'V'), - (0x3DA, 'M', 'ϛ'), - (0x3DB, 'V'), - (0x3DC, 'M', 'ϝ'), - (0x3DD, 'V'), - (0x3DE, 'M', 'ϟ'), - (0x3DF, 'V'), - (0x3E0, 'M', 'ϡ'), - (0x3E1, 'V'), - (0x3E2, 'M', 'ϣ'), - (0x3E3, 'V'), - (0x3E4, 'M', 'ϥ'), - (0x3E5, 'V'), - (0x3E6, 'M', 'ϧ'), - (0x3E7, 'V'), - (0x3E8, 'M', 'ϩ'), - (0x3E9, 'V'), - (0x3EA, 'M', 'ϫ'), - (0x3EB, 'V'), - (0x3EC, 'M', 'ϭ'), - (0x3ED, 'V'), - (0x3EE, 'M', 'ϯ'), - (0x3EF, 'V'), - (0x3F0, 'M', 'κ'), - (0x3F1, 'M', 'ρ'), - (0x3F2, 'M', 'σ'), - (0x3F3, 'V'), - (0x3F4, 'M', 'θ'), - (0x3F5, 'M', 'ε'), - (0x3F6, 'V'), - (0x3F7, 'M', 'ϸ'), - (0x3F8, 'V'), - (0x3F9, 'M', 'σ'), - (0x3FA, 'M', 'ϻ'), - (0x3FB, 'V'), - (0x3FD, 'M', 'ͻ'), - (0x3FE, 'M', 'ͼ'), - (0x3FF, 'M', 'ͽ'), - (0x400, 'M', 'ѐ'), - (0x401, 'M', 'ё'), - (0x402, 'M', 'ђ'), - ] - -def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x403, 'M', 'ѓ'), - (0x404, 'M', 'є'), - (0x405, 'M', 'ѕ'), - (0x406, 'M', 'і'), - (0x407, 'M', 'ї'), - (0x408, 'M', 'ј'), - (0x409, 'M', 'љ'), - (0x40A, 'M', 'њ'), - (0x40B, 'M', 'ћ'), - (0x40C, 'M', 'ќ'), - (0x40D, 'M', 'ѝ'), - (0x40E, 'M', 'ў'), - (0x40F, 'M', 'џ'), - (0x410, 'M', 'а'), - (0x411, 'M', 'б'), - (0x412, 'M', 'в'), - (0x413, 'M', 'г'), - (0x414, 'M', 'д'), - (0x415, 'M', 'е'), - (0x416, 'M', 'ж'), - (0x417, 'M', 'з'), - (0x418, 'M', 'и'), - (0x419, 'M', 'й'), - (0x41A, 'M', 'к'), - (0x41B, 'M', 'л'), - (0x41C, 'M', 'м'), - (0x41D, 'M', 'н'), - (0x41E, 'M', 'о'), - (0x41F, 'M', 'п'), - (0x420, 'M', 'р'), - (0x421, 'M', 'с'), - (0x422, 'M', 'т'), - (0x423, 'M', 'у'), - (0x424, 'M', 'ф'), - (0x425, 'M', 'х'), - (0x426, 'M', 'ц'), - (0x427, 'M', 'ч'), - (0x428, 'M', 'ш'), - (0x429, 'M', 'щ'), - (0x42A, 'M', 'ъ'), - (0x42B, 'M', 'ы'), - (0x42C, 'M', 'ь'), - (0x42D, 'M', 'э'), - (0x42E, 'M', 'ю'), - (0x42F, 'M', 'я'), - (0x430, 'V'), - (0x460, 'M', 'ѡ'), - (0x461, 'V'), - (0x462, 'M', 'ѣ'), - (0x463, 'V'), - (0x464, 'M', 'ѥ'), - (0x465, 'V'), - (0x466, 'M', 'ѧ'), - (0x467, 'V'), - (0x468, 'M', 'ѩ'), - (0x469, 'V'), - (0x46A, 'M', 'ѫ'), - (0x46B, 'V'), - (0x46C, 'M', 'ѭ'), - (0x46D, 'V'), - (0x46E, 'M', 'ѯ'), - (0x46F, 'V'), - (0x470, 'M', 'ѱ'), - (0x471, 'V'), - (0x472, 'M', 'ѳ'), - (0x473, 'V'), - (0x474, 'M', 'ѵ'), - (0x475, 'V'), - (0x476, 'M', 'ѷ'), - (0x477, 'V'), - (0x478, 'M', 'ѹ'), - (0x479, 'V'), - (0x47A, 'M', 'ѻ'), - (0x47B, 'V'), - (0x47C, 'M', 'ѽ'), - (0x47D, 'V'), - (0x47E, 'M', 'ѿ'), - (0x47F, 'V'), - (0x480, 'M', 'ҁ'), - (0x481, 'V'), - (0x48A, 'M', 'ҋ'), - (0x48B, 'V'), - (0x48C, 'M', 'ҍ'), - (0x48D, 'V'), - (0x48E, 'M', 'ҏ'), - (0x48F, 'V'), - (0x490, 'M', 'ґ'), - (0x491, 'V'), - (0x492, 'M', 'ғ'), - (0x493, 'V'), - (0x494, 'M', 'ҕ'), - (0x495, 'V'), - (0x496, 'M', 'җ'), - (0x497, 'V'), - (0x498, 'M', 'ҙ'), - (0x499, 'V'), - (0x49A, 'M', 'қ'), - (0x49B, 'V'), - (0x49C, 'M', 'ҝ'), - (0x49D, 'V'), - ] - -def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x49E, 'M', 'ҟ'), - (0x49F, 'V'), - (0x4A0, 'M', 'ҡ'), - (0x4A1, 'V'), - (0x4A2, 'M', 'ң'), - (0x4A3, 'V'), - (0x4A4, 'M', 'ҥ'), - (0x4A5, 'V'), - (0x4A6, 'M', 'ҧ'), - (0x4A7, 'V'), - (0x4A8, 'M', 'ҩ'), - (0x4A9, 'V'), - (0x4AA, 'M', 'ҫ'), - (0x4AB, 'V'), - (0x4AC, 'M', 'ҭ'), - (0x4AD, 'V'), - (0x4AE, 'M', 'ү'), - (0x4AF, 'V'), - (0x4B0, 'M', 'ұ'), - (0x4B1, 'V'), - (0x4B2, 'M', 'ҳ'), - (0x4B3, 'V'), - (0x4B4, 'M', 'ҵ'), - (0x4B5, 'V'), - (0x4B6, 'M', 'ҷ'), - (0x4B7, 'V'), - (0x4B8, 'M', 'ҹ'), - (0x4B9, 'V'), - (0x4BA, 'M', 'һ'), - (0x4BB, 'V'), - (0x4BC, 'M', 'ҽ'), - (0x4BD, 'V'), - (0x4BE, 'M', 'ҿ'), - (0x4BF, 'V'), - (0x4C0, 'X'), - (0x4C1, 'M', 'ӂ'), - (0x4C2, 'V'), - (0x4C3, 'M', 'ӄ'), - (0x4C4, 'V'), - (0x4C5, 'M', 'ӆ'), - (0x4C6, 'V'), - (0x4C7, 'M', 'ӈ'), - (0x4C8, 'V'), - (0x4C9, 'M', 'ӊ'), - (0x4CA, 'V'), - (0x4CB, 'M', 'ӌ'), - (0x4CC, 'V'), - (0x4CD, 'M', 'ӎ'), - (0x4CE, 'V'), - (0x4D0, 'M', 'ӑ'), - (0x4D1, 'V'), - (0x4D2, 'M', 'ӓ'), - (0x4D3, 'V'), - (0x4D4, 'M', 'ӕ'), - (0x4D5, 'V'), - (0x4D6, 'M', 'ӗ'), - (0x4D7, 'V'), - (0x4D8, 'M', 'ә'), - (0x4D9, 'V'), - (0x4DA, 'M', 'ӛ'), - (0x4DB, 'V'), - (0x4DC, 'M', 'ӝ'), - (0x4DD, 'V'), - (0x4DE, 'M', 'ӟ'), - (0x4DF, 'V'), - (0x4E0, 'M', 'ӡ'), - (0x4E1, 'V'), - (0x4E2, 'M', 'ӣ'), - (0x4E3, 'V'), - (0x4E4, 'M', 'ӥ'), - (0x4E5, 'V'), - (0x4E6, 'M', 'ӧ'), - (0x4E7, 'V'), - (0x4E8, 'M', 'ө'), - (0x4E9, 'V'), - (0x4EA, 'M', 'ӫ'), - (0x4EB, 'V'), - (0x4EC, 'M', 'ӭ'), - (0x4ED, 'V'), - (0x4EE, 'M', 'ӯ'), - (0x4EF, 'V'), - (0x4F0, 'M', 'ӱ'), - (0x4F1, 'V'), - (0x4F2, 'M', 'ӳ'), - (0x4F3, 'V'), - (0x4F4, 'M', 'ӵ'), - (0x4F5, 'V'), - (0x4F6, 'M', 'ӷ'), - (0x4F7, 'V'), - (0x4F8, 'M', 'ӹ'), - (0x4F9, 'V'), - (0x4FA, 'M', 'ӻ'), - (0x4FB, 'V'), - (0x4FC, 'M', 'ӽ'), - (0x4FD, 'V'), - (0x4FE, 'M', 'ӿ'), - (0x4FF, 'V'), - (0x500, 'M', 'ԁ'), - (0x501, 'V'), - (0x502, 'M', 'ԃ'), - ] - -def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x503, 'V'), - (0x504, 'M', 'ԅ'), - (0x505, 'V'), - (0x506, 'M', 'ԇ'), - (0x507, 'V'), - (0x508, 'M', 'ԉ'), - (0x509, 'V'), - (0x50A, 'M', 'ԋ'), - (0x50B, 'V'), - (0x50C, 'M', 'ԍ'), - (0x50D, 'V'), - (0x50E, 'M', 'ԏ'), - (0x50F, 'V'), - (0x510, 'M', 'ԑ'), - (0x511, 'V'), - (0x512, 'M', 'ԓ'), - (0x513, 'V'), - (0x514, 'M', 'ԕ'), - (0x515, 'V'), - (0x516, 'M', 'ԗ'), - (0x517, 'V'), - (0x518, 'M', 'ԙ'), - (0x519, 'V'), - (0x51A, 'M', 'ԛ'), - (0x51B, 'V'), - (0x51C, 'M', 'ԝ'), - (0x51D, 'V'), - (0x51E, 'M', 'ԟ'), - (0x51F, 'V'), - (0x520, 'M', 'ԡ'), - (0x521, 'V'), - (0x522, 'M', 'ԣ'), - (0x523, 'V'), - (0x524, 'M', 'ԥ'), - (0x525, 'V'), - (0x526, 'M', 'ԧ'), - (0x527, 'V'), - (0x528, 'M', 'ԩ'), - (0x529, 'V'), - (0x52A, 'M', 'ԫ'), - (0x52B, 'V'), - (0x52C, 'M', 'ԭ'), - (0x52D, 'V'), - (0x52E, 'M', 'ԯ'), - (0x52F, 'V'), - (0x530, 'X'), - (0x531, 'M', 'ա'), - (0x532, 'M', 'բ'), - (0x533, 'M', 'գ'), - (0x534, 'M', 'դ'), - (0x535, 'M', 'ե'), - (0x536, 'M', 'զ'), - (0x537, 'M', 'է'), - (0x538, 'M', 'ը'), - (0x539, 'M', 'թ'), - (0x53A, 'M', 'ժ'), - (0x53B, 'M', 'ի'), - (0x53C, 'M', 'լ'), - (0x53D, 'M', 'խ'), - (0x53E, 'M', 'ծ'), - (0x53F, 'M', 'կ'), - (0x540, 'M', 'հ'), - (0x541, 'M', 'ձ'), - (0x542, 'M', 'ղ'), - (0x543, 'M', 'ճ'), - (0x544, 'M', 'մ'), - (0x545, 'M', 'յ'), - (0x546, 'M', 'ն'), - (0x547, 'M', 'շ'), - (0x548, 'M', 'ո'), - (0x549, 'M', 'չ'), - (0x54A, 'M', 'պ'), - (0x54B, 'M', 'ջ'), - (0x54C, 'M', 'ռ'), - (0x54D, 'M', 'ս'), - (0x54E, 'M', 'վ'), - (0x54F, 'M', 'տ'), - (0x550, 'M', 'ր'), - (0x551, 'M', 'ց'), - (0x552, 'M', 'ւ'), - (0x553, 'M', 'փ'), - (0x554, 'M', 'ք'), - (0x555, 'M', 'օ'), - (0x556, 'M', 'ֆ'), - (0x557, 'X'), - (0x559, 'V'), - (0x587, 'M', 'եւ'), - (0x588, 'V'), - (0x58B, 'X'), - (0x58D, 'V'), - (0x590, 'X'), - (0x591, 'V'), - (0x5C8, 'X'), - (0x5D0, 'V'), - (0x5EB, 'X'), - (0x5EF, 'V'), - (0x5F5, 'X'), - (0x606, 'V'), - (0x61C, 'X'), - (0x61D, 'V'), - ] - -def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x675, 'M', 'اٴ'), - (0x676, 'M', 'وٴ'), - (0x677, 'M', 'ۇٴ'), - (0x678, 'M', 'يٴ'), - (0x679, 'V'), - (0x6DD, 'X'), - (0x6DE, 'V'), - (0x70E, 'X'), - (0x710, 'V'), - (0x74B, 'X'), - (0x74D, 'V'), - (0x7B2, 'X'), - (0x7C0, 'V'), - (0x7FB, 'X'), - (0x7FD, 'V'), - (0x82E, 'X'), - (0x830, 'V'), - (0x83F, 'X'), - (0x840, 'V'), - (0x85C, 'X'), - (0x85E, 'V'), - (0x85F, 'X'), - (0x860, 'V'), - (0x86B, 'X'), - (0x870, 'V'), - (0x88F, 'X'), - (0x898, 'V'), - (0x8E2, 'X'), - (0x8E3, 'V'), - (0x958, 'M', 'क़'), - (0x959, 'M', 'ख़'), - (0x95A, 'M', 'ग़'), - (0x95B, 'M', 'ज़'), - (0x95C, 'M', 'ड़'), - (0x95D, 'M', 'ढ़'), - (0x95E, 'M', 'फ़'), - (0x95F, 'M', 'य़'), - (0x960, 'V'), - (0x984, 'X'), - (0x985, 'V'), - (0x98D, 'X'), - (0x98F, 'V'), - (0x991, 'X'), - (0x993, 'V'), - (0x9A9, 'X'), - (0x9AA, 'V'), - (0x9B1, 'X'), - (0x9B2, 'V'), - (0x9B3, 'X'), - (0x9B6, 'V'), - (0x9BA, 'X'), - (0x9BC, 'V'), - (0x9C5, 'X'), - (0x9C7, 'V'), - (0x9C9, 'X'), - (0x9CB, 'V'), - (0x9CF, 'X'), - (0x9D7, 'V'), - (0x9D8, 'X'), - (0x9DC, 'M', 'ড়'), - (0x9DD, 'M', 'ঢ়'), - (0x9DE, 'X'), - (0x9DF, 'M', 'য়'), - (0x9E0, 'V'), - (0x9E4, 'X'), - (0x9E6, 'V'), - (0x9FF, 'X'), - (0xA01, 'V'), - (0xA04, 'X'), - (0xA05, 'V'), - (0xA0B, 'X'), - (0xA0F, 'V'), - (0xA11, 'X'), - (0xA13, 'V'), - (0xA29, 'X'), - (0xA2A, 'V'), - (0xA31, 'X'), - (0xA32, 'V'), - (0xA33, 'M', 'ਲ਼'), - (0xA34, 'X'), - (0xA35, 'V'), - (0xA36, 'M', 'ਸ਼'), - (0xA37, 'X'), - (0xA38, 'V'), - (0xA3A, 'X'), - (0xA3C, 'V'), - (0xA3D, 'X'), - (0xA3E, 'V'), - (0xA43, 'X'), - (0xA47, 'V'), - (0xA49, 'X'), - (0xA4B, 'V'), - (0xA4E, 'X'), - (0xA51, 'V'), - (0xA52, 'X'), - (0xA59, 'M', 'ਖ਼'), - (0xA5A, 'M', 'ਗ਼'), - (0xA5B, 'M', 'ਜ਼'), - (0xA5C, 'V'), - (0xA5D, 'X'), - ] - -def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA5E, 'M', 'ਫ਼'), - (0xA5F, 'X'), - (0xA66, 'V'), - (0xA77, 'X'), - (0xA81, 'V'), - (0xA84, 'X'), - (0xA85, 'V'), - (0xA8E, 'X'), - (0xA8F, 'V'), - (0xA92, 'X'), - (0xA93, 'V'), - (0xAA9, 'X'), - (0xAAA, 'V'), - (0xAB1, 'X'), - (0xAB2, 'V'), - (0xAB4, 'X'), - (0xAB5, 'V'), - (0xABA, 'X'), - (0xABC, 'V'), - (0xAC6, 'X'), - (0xAC7, 'V'), - (0xACA, 'X'), - (0xACB, 'V'), - (0xACE, 'X'), - (0xAD0, 'V'), - (0xAD1, 'X'), - (0xAE0, 'V'), - (0xAE4, 'X'), - (0xAE6, 'V'), - (0xAF2, 'X'), - (0xAF9, 'V'), - (0xB00, 'X'), - (0xB01, 'V'), - (0xB04, 'X'), - (0xB05, 'V'), - (0xB0D, 'X'), - (0xB0F, 'V'), - (0xB11, 'X'), - (0xB13, 'V'), - (0xB29, 'X'), - (0xB2A, 'V'), - (0xB31, 'X'), - (0xB32, 'V'), - (0xB34, 'X'), - (0xB35, 'V'), - (0xB3A, 'X'), - (0xB3C, 'V'), - (0xB45, 'X'), - (0xB47, 'V'), - (0xB49, 'X'), - (0xB4B, 'V'), - (0xB4E, 'X'), - (0xB55, 'V'), - (0xB58, 'X'), - (0xB5C, 'M', 'ଡ଼'), - (0xB5D, 'M', 'ଢ଼'), - (0xB5E, 'X'), - (0xB5F, 'V'), - (0xB64, 'X'), - (0xB66, 'V'), - (0xB78, 'X'), - (0xB82, 'V'), - (0xB84, 'X'), - (0xB85, 'V'), - (0xB8B, 'X'), - (0xB8E, 'V'), - (0xB91, 'X'), - (0xB92, 'V'), - (0xB96, 'X'), - (0xB99, 'V'), - (0xB9B, 'X'), - (0xB9C, 'V'), - (0xB9D, 'X'), - (0xB9E, 'V'), - (0xBA0, 'X'), - (0xBA3, 'V'), - (0xBA5, 'X'), - (0xBA8, 'V'), - (0xBAB, 'X'), - (0xBAE, 'V'), - (0xBBA, 'X'), - (0xBBE, 'V'), - (0xBC3, 'X'), - (0xBC6, 'V'), - (0xBC9, 'X'), - (0xBCA, 'V'), - (0xBCE, 'X'), - (0xBD0, 'V'), - (0xBD1, 'X'), - (0xBD7, 'V'), - (0xBD8, 'X'), - (0xBE6, 'V'), - (0xBFB, 'X'), - (0xC00, 'V'), - (0xC0D, 'X'), - (0xC0E, 'V'), - (0xC11, 'X'), - (0xC12, 'V'), - (0xC29, 'X'), - (0xC2A, 'V'), - ] - -def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xC3A, 'X'), - (0xC3C, 'V'), - (0xC45, 'X'), - (0xC46, 'V'), - (0xC49, 'X'), - (0xC4A, 'V'), - (0xC4E, 'X'), - (0xC55, 'V'), - (0xC57, 'X'), - (0xC58, 'V'), - (0xC5B, 'X'), - (0xC5D, 'V'), - (0xC5E, 'X'), - (0xC60, 'V'), - (0xC64, 'X'), - (0xC66, 'V'), - (0xC70, 'X'), - (0xC77, 'V'), - (0xC8D, 'X'), - (0xC8E, 'V'), - (0xC91, 'X'), - (0xC92, 'V'), - (0xCA9, 'X'), - (0xCAA, 'V'), - (0xCB4, 'X'), - (0xCB5, 'V'), - (0xCBA, 'X'), - (0xCBC, 'V'), - (0xCC5, 'X'), - (0xCC6, 'V'), - (0xCC9, 'X'), - (0xCCA, 'V'), - (0xCCE, 'X'), - (0xCD5, 'V'), - (0xCD7, 'X'), - (0xCDD, 'V'), - (0xCDF, 'X'), - (0xCE0, 'V'), - (0xCE4, 'X'), - (0xCE6, 'V'), - (0xCF0, 'X'), - (0xCF1, 'V'), - (0xCF4, 'X'), - (0xD00, 'V'), - (0xD0D, 'X'), - (0xD0E, 'V'), - (0xD11, 'X'), - (0xD12, 'V'), - (0xD45, 'X'), - (0xD46, 'V'), - (0xD49, 'X'), - (0xD4A, 'V'), - (0xD50, 'X'), - (0xD54, 'V'), - (0xD64, 'X'), - (0xD66, 'V'), - (0xD80, 'X'), - (0xD81, 'V'), - (0xD84, 'X'), - (0xD85, 'V'), - (0xD97, 'X'), - (0xD9A, 'V'), - (0xDB2, 'X'), - (0xDB3, 'V'), - (0xDBC, 'X'), - (0xDBD, 'V'), - (0xDBE, 'X'), - (0xDC0, 'V'), - (0xDC7, 'X'), - (0xDCA, 'V'), - (0xDCB, 'X'), - (0xDCF, 'V'), - (0xDD5, 'X'), - (0xDD6, 'V'), - (0xDD7, 'X'), - (0xDD8, 'V'), - (0xDE0, 'X'), - (0xDE6, 'V'), - (0xDF0, 'X'), - (0xDF2, 'V'), - (0xDF5, 'X'), - (0xE01, 'V'), - (0xE33, 'M', 'ํา'), - (0xE34, 'V'), - (0xE3B, 'X'), - (0xE3F, 'V'), - (0xE5C, 'X'), - (0xE81, 'V'), - (0xE83, 'X'), - (0xE84, 'V'), - (0xE85, 'X'), - (0xE86, 'V'), - (0xE8B, 'X'), - (0xE8C, 'V'), - (0xEA4, 'X'), - (0xEA5, 'V'), - (0xEA6, 'X'), - (0xEA7, 'V'), - (0xEB3, 'M', 'ໍາ'), - (0xEB4, 'V'), - ] - -def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xEBE, 'X'), - (0xEC0, 'V'), - (0xEC5, 'X'), - (0xEC6, 'V'), - (0xEC7, 'X'), - (0xEC8, 'V'), - (0xECF, 'X'), - (0xED0, 'V'), - (0xEDA, 'X'), - (0xEDC, 'M', 'ຫນ'), - (0xEDD, 'M', 'ຫມ'), - (0xEDE, 'V'), - (0xEE0, 'X'), - (0xF00, 'V'), - (0xF0C, 'M', '་'), - (0xF0D, 'V'), - (0xF43, 'M', 'གྷ'), - (0xF44, 'V'), - (0xF48, 'X'), - (0xF49, 'V'), - (0xF4D, 'M', 'ཌྷ'), - (0xF4E, 'V'), - (0xF52, 'M', 'དྷ'), - (0xF53, 'V'), - (0xF57, 'M', 'བྷ'), - (0xF58, 'V'), - (0xF5C, 'M', 'ཛྷ'), - (0xF5D, 'V'), - (0xF69, 'M', 'ཀྵ'), - (0xF6A, 'V'), - (0xF6D, 'X'), - (0xF71, 'V'), - (0xF73, 'M', 'ཱི'), - (0xF74, 'V'), - (0xF75, 'M', 'ཱུ'), - (0xF76, 'M', 'ྲྀ'), - (0xF77, 'M', 'ྲཱྀ'), - (0xF78, 'M', 'ླྀ'), - (0xF79, 'M', 'ླཱྀ'), - (0xF7A, 'V'), - (0xF81, 'M', 'ཱྀ'), - (0xF82, 'V'), - (0xF93, 'M', 'ྒྷ'), - (0xF94, 'V'), - (0xF98, 'X'), - (0xF99, 'V'), - (0xF9D, 'M', 'ྜྷ'), - (0xF9E, 'V'), - (0xFA2, 'M', 'ྡྷ'), - (0xFA3, 'V'), - (0xFA7, 'M', 'ྦྷ'), - (0xFA8, 'V'), - (0xFAC, 'M', 'ྫྷ'), - (0xFAD, 'V'), - (0xFB9, 'M', 'ྐྵ'), - (0xFBA, 'V'), - (0xFBD, 'X'), - (0xFBE, 'V'), - (0xFCD, 'X'), - (0xFCE, 'V'), - (0xFDB, 'X'), - (0x1000, 'V'), - (0x10A0, 'X'), - (0x10C7, 'M', 'ⴧ'), - (0x10C8, 'X'), - (0x10CD, 'M', 'ⴭ'), - (0x10CE, 'X'), - (0x10D0, 'V'), - (0x10FC, 'M', 'ნ'), - (0x10FD, 'V'), - (0x115F, 'X'), - (0x1161, 'V'), - (0x1249, 'X'), - (0x124A, 'V'), - (0x124E, 'X'), - (0x1250, 'V'), - (0x1257, 'X'), - (0x1258, 'V'), - (0x1259, 'X'), - (0x125A, 'V'), - (0x125E, 'X'), - (0x1260, 'V'), - (0x1289, 'X'), - (0x128A, 'V'), - (0x128E, 'X'), - (0x1290, 'V'), - (0x12B1, 'X'), - (0x12B2, 'V'), - (0x12B6, 'X'), - (0x12B8, 'V'), - (0x12BF, 'X'), - (0x12C0, 'V'), - (0x12C1, 'X'), - (0x12C2, 'V'), - (0x12C6, 'X'), - (0x12C8, 'V'), - (0x12D7, 'X'), - (0x12D8, 'V'), - (0x1311, 'X'), - (0x1312, 'V'), - ] - -def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1316, 'X'), - (0x1318, 'V'), - (0x135B, 'X'), - (0x135D, 'V'), - (0x137D, 'X'), - (0x1380, 'V'), - (0x139A, 'X'), - (0x13A0, 'V'), - (0x13F6, 'X'), - (0x13F8, 'M', 'Ᏸ'), - (0x13F9, 'M', 'Ᏹ'), - (0x13FA, 'M', 'Ᏺ'), - (0x13FB, 'M', 'Ᏻ'), - (0x13FC, 'M', 'Ᏼ'), - (0x13FD, 'M', 'Ᏽ'), - (0x13FE, 'X'), - (0x1400, 'V'), - (0x1680, 'X'), - (0x1681, 'V'), - (0x169D, 'X'), - (0x16A0, 'V'), - (0x16F9, 'X'), - (0x1700, 'V'), - (0x1716, 'X'), - (0x171F, 'V'), - (0x1737, 'X'), - (0x1740, 'V'), - (0x1754, 'X'), - (0x1760, 'V'), - (0x176D, 'X'), - (0x176E, 'V'), - (0x1771, 'X'), - (0x1772, 'V'), - (0x1774, 'X'), - (0x1780, 'V'), - (0x17B4, 'X'), - (0x17B6, 'V'), - (0x17DE, 'X'), - (0x17E0, 'V'), - (0x17EA, 'X'), - (0x17F0, 'V'), - (0x17FA, 'X'), - (0x1800, 'V'), - (0x1806, 'X'), - (0x1807, 'V'), - (0x180B, 'I'), - (0x180E, 'X'), - (0x180F, 'I'), - (0x1810, 'V'), - (0x181A, 'X'), - (0x1820, 'V'), - (0x1879, 'X'), - (0x1880, 'V'), - (0x18AB, 'X'), - (0x18B0, 'V'), - (0x18F6, 'X'), - (0x1900, 'V'), - (0x191F, 'X'), - (0x1920, 'V'), - (0x192C, 'X'), - (0x1930, 'V'), - (0x193C, 'X'), - (0x1940, 'V'), - (0x1941, 'X'), - (0x1944, 'V'), - (0x196E, 'X'), - (0x1970, 'V'), - (0x1975, 'X'), - (0x1980, 'V'), - (0x19AC, 'X'), - (0x19B0, 'V'), - (0x19CA, 'X'), - (0x19D0, 'V'), - (0x19DB, 'X'), - (0x19DE, 'V'), - (0x1A1C, 'X'), - (0x1A1E, 'V'), - (0x1A5F, 'X'), - (0x1A60, 'V'), - (0x1A7D, 'X'), - (0x1A7F, 'V'), - (0x1A8A, 'X'), - (0x1A90, 'V'), - (0x1A9A, 'X'), - (0x1AA0, 'V'), - (0x1AAE, 'X'), - (0x1AB0, 'V'), - (0x1ACF, 'X'), - (0x1B00, 'V'), - (0x1B4D, 'X'), - (0x1B50, 'V'), - (0x1B7F, 'X'), - (0x1B80, 'V'), - (0x1BF4, 'X'), - (0x1BFC, 'V'), - (0x1C38, 'X'), - (0x1C3B, 'V'), - (0x1C4A, 'X'), - (0x1C4D, 'V'), - (0x1C80, 'M', 'в'), - ] - -def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1C81, 'M', 'д'), - (0x1C82, 'M', 'о'), - (0x1C83, 'M', 'с'), - (0x1C84, 'M', 'т'), - (0x1C86, 'M', 'ъ'), - (0x1C87, 'M', 'ѣ'), - (0x1C88, 'M', 'ꙋ'), - (0x1C89, 'X'), - (0x1C90, 'M', 'ა'), - (0x1C91, 'M', 'ბ'), - (0x1C92, 'M', 'გ'), - (0x1C93, 'M', 'დ'), - (0x1C94, 'M', 'ე'), - (0x1C95, 'M', 'ვ'), - (0x1C96, 'M', 'ზ'), - (0x1C97, 'M', 'თ'), - (0x1C98, 'M', 'ი'), - (0x1C99, 'M', 'კ'), - (0x1C9A, 'M', 'ლ'), - (0x1C9B, 'M', 'მ'), - (0x1C9C, 'M', 'ნ'), - (0x1C9D, 'M', 'ო'), - (0x1C9E, 'M', 'პ'), - (0x1C9F, 'M', 'ჟ'), - (0x1CA0, 'M', 'რ'), - (0x1CA1, 'M', 'ს'), - (0x1CA2, 'M', 'ტ'), - (0x1CA3, 'M', 'უ'), - (0x1CA4, 'M', 'ფ'), - (0x1CA5, 'M', 'ქ'), - (0x1CA6, 'M', 'ღ'), - (0x1CA7, 'M', 'ყ'), - (0x1CA8, 'M', 'შ'), - (0x1CA9, 'M', 'ჩ'), - (0x1CAA, 'M', 'ც'), - (0x1CAB, 'M', 'ძ'), - (0x1CAC, 'M', 'წ'), - (0x1CAD, 'M', 'ჭ'), - (0x1CAE, 'M', 'ხ'), - (0x1CAF, 'M', 'ჯ'), - (0x1CB0, 'M', 'ჰ'), - (0x1CB1, 'M', 'ჱ'), - (0x1CB2, 'M', 'ჲ'), - (0x1CB3, 'M', 'ჳ'), - (0x1CB4, 'M', 'ჴ'), - (0x1CB5, 'M', 'ჵ'), - (0x1CB6, 'M', 'ჶ'), - (0x1CB7, 'M', 'ჷ'), - (0x1CB8, 'M', 'ჸ'), - (0x1CB9, 'M', 'ჹ'), - (0x1CBA, 'M', 'ჺ'), - (0x1CBB, 'X'), - (0x1CBD, 'M', 'ჽ'), - (0x1CBE, 'M', 'ჾ'), - (0x1CBF, 'M', 'ჿ'), - (0x1CC0, 'V'), - (0x1CC8, 'X'), - (0x1CD0, 'V'), - (0x1CFB, 'X'), - (0x1D00, 'V'), - (0x1D2C, 'M', 'a'), - (0x1D2D, 'M', 'æ'), - (0x1D2E, 'M', 'b'), - (0x1D2F, 'V'), - (0x1D30, 'M', 'd'), - (0x1D31, 'M', 'e'), - (0x1D32, 'M', 'ǝ'), - (0x1D33, 'M', 'g'), - (0x1D34, 'M', 'h'), - (0x1D35, 'M', 'i'), - (0x1D36, 'M', 'j'), - (0x1D37, 'M', 'k'), - (0x1D38, 'M', 'l'), - (0x1D39, 'M', 'm'), - (0x1D3A, 'M', 'n'), - (0x1D3B, 'V'), - (0x1D3C, 'M', 'o'), - (0x1D3D, 'M', 'ȣ'), - (0x1D3E, 'M', 'p'), - (0x1D3F, 'M', 'r'), - (0x1D40, 'M', 't'), - (0x1D41, 'M', 'u'), - (0x1D42, 'M', 'w'), - (0x1D43, 'M', 'a'), - (0x1D44, 'M', 'ɐ'), - (0x1D45, 'M', 'ɑ'), - (0x1D46, 'M', 'ᴂ'), - (0x1D47, 'M', 'b'), - (0x1D48, 'M', 'd'), - (0x1D49, 'M', 'e'), - (0x1D4A, 'M', 'ə'), - (0x1D4B, 'M', 'ɛ'), - (0x1D4C, 'M', 'ɜ'), - (0x1D4D, 'M', 'g'), - (0x1D4E, 'V'), - (0x1D4F, 'M', 'k'), - (0x1D50, 'M', 'm'), - (0x1D51, 'M', 'ŋ'), - (0x1D52, 'M', 'o'), - (0x1D53, 'M', 'ɔ'), - ] - -def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D54, 'M', 'ᴖ'), - (0x1D55, 'M', 'ᴗ'), - (0x1D56, 'M', 'p'), - (0x1D57, 'M', 't'), - (0x1D58, 'M', 'u'), - (0x1D59, 'M', 'ᴝ'), - (0x1D5A, 'M', 'ɯ'), - (0x1D5B, 'M', 'v'), - (0x1D5C, 'M', 'ᴥ'), - (0x1D5D, 'M', 'β'), - (0x1D5E, 'M', 'γ'), - (0x1D5F, 'M', 'δ'), - (0x1D60, 'M', 'φ'), - (0x1D61, 'M', 'χ'), - (0x1D62, 'M', 'i'), - (0x1D63, 'M', 'r'), - (0x1D64, 'M', 'u'), - (0x1D65, 'M', 'v'), - (0x1D66, 'M', 'β'), - (0x1D67, 'M', 'γ'), - (0x1D68, 'M', 'ρ'), - (0x1D69, 'M', 'φ'), - (0x1D6A, 'M', 'χ'), - (0x1D6B, 'V'), - (0x1D78, 'M', 'н'), - (0x1D79, 'V'), - (0x1D9B, 'M', 'ɒ'), - (0x1D9C, 'M', 'c'), - (0x1D9D, 'M', 'ɕ'), - (0x1D9E, 'M', 'ð'), - (0x1D9F, 'M', 'ɜ'), - (0x1DA0, 'M', 'f'), - (0x1DA1, 'M', 'ɟ'), - (0x1DA2, 'M', 'ɡ'), - (0x1DA3, 'M', 'ɥ'), - (0x1DA4, 'M', 'ɨ'), - (0x1DA5, 'M', 'ɩ'), - (0x1DA6, 'M', 'ɪ'), - (0x1DA7, 'M', 'ᵻ'), - (0x1DA8, 'M', 'ʝ'), - (0x1DA9, 'M', 'ɭ'), - (0x1DAA, 'M', 'ᶅ'), - (0x1DAB, 'M', 'ʟ'), - (0x1DAC, 'M', 'ɱ'), - (0x1DAD, 'M', 'ɰ'), - (0x1DAE, 'M', 'ɲ'), - (0x1DAF, 'M', 'ɳ'), - (0x1DB0, 'M', 'ɴ'), - (0x1DB1, 'M', 'ɵ'), - (0x1DB2, 'M', 'ɸ'), - (0x1DB3, 'M', 'ʂ'), - (0x1DB4, 'M', 'ʃ'), - (0x1DB5, 'M', 'ƫ'), - (0x1DB6, 'M', 'ʉ'), - (0x1DB7, 'M', 'ʊ'), - (0x1DB8, 'M', 'ᴜ'), - (0x1DB9, 'M', 'ʋ'), - (0x1DBA, 'M', 'ʌ'), - (0x1DBB, 'M', 'z'), - (0x1DBC, 'M', 'ʐ'), - (0x1DBD, 'M', 'ʑ'), - (0x1DBE, 'M', 'ʒ'), - (0x1DBF, 'M', 'θ'), - (0x1DC0, 'V'), - (0x1E00, 'M', 'ḁ'), - (0x1E01, 'V'), - (0x1E02, 'M', 'ḃ'), - (0x1E03, 'V'), - (0x1E04, 'M', 'ḅ'), - (0x1E05, 'V'), - (0x1E06, 'M', 'ḇ'), - (0x1E07, 'V'), - (0x1E08, 'M', 'ḉ'), - (0x1E09, 'V'), - (0x1E0A, 'M', 'ḋ'), - (0x1E0B, 'V'), - (0x1E0C, 'M', 'ḍ'), - (0x1E0D, 'V'), - (0x1E0E, 'M', 'ḏ'), - (0x1E0F, 'V'), - (0x1E10, 'M', 'ḑ'), - (0x1E11, 'V'), - (0x1E12, 'M', 'ḓ'), - (0x1E13, 'V'), - (0x1E14, 'M', 'ḕ'), - (0x1E15, 'V'), - (0x1E16, 'M', 'ḗ'), - (0x1E17, 'V'), - (0x1E18, 'M', 'ḙ'), - (0x1E19, 'V'), - (0x1E1A, 'M', 'ḛ'), - (0x1E1B, 'V'), - (0x1E1C, 'M', 'ḝ'), - (0x1E1D, 'V'), - (0x1E1E, 'M', 'ḟ'), - (0x1E1F, 'V'), - (0x1E20, 'M', 'ḡ'), - (0x1E21, 'V'), - (0x1E22, 'M', 'ḣ'), - (0x1E23, 'V'), - ] - -def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E24, 'M', 'ḥ'), - (0x1E25, 'V'), - (0x1E26, 'M', 'ḧ'), - (0x1E27, 'V'), - (0x1E28, 'M', 'ḩ'), - (0x1E29, 'V'), - (0x1E2A, 'M', 'ḫ'), - (0x1E2B, 'V'), - (0x1E2C, 'M', 'ḭ'), - (0x1E2D, 'V'), - (0x1E2E, 'M', 'ḯ'), - (0x1E2F, 'V'), - (0x1E30, 'M', 'ḱ'), - (0x1E31, 'V'), - (0x1E32, 'M', 'ḳ'), - (0x1E33, 'V'), - (0x1E34, 'M', 'ḵ'), - (0x1E35, 'V'), - (0x1E36, 'M', 'ḷ'), - (0x1E37, 'V'), - (0x1E38, 'M', 'ḹ'), - (0x1E39, 'V'), - (0x1E3A, 'M', 'ḻ'), - (0x1E3B, 'V'), - (0x1E3C, 'M', 'ḽ'), - (0x1E3D, 'V'), - (0x1E3E, 'M', 'ḿ'), - (0x1E3F, 'V'), - (0x1E40, 'M', 'ṁ'), - (0x1E41, 'V'), - (0x1E42, 'M', 'ṃ'), - (0x1E43, 'V'), - (0x1E44, 'M', 'ṅ'), - (0x1E45, 'V'), - (0x1E46, 'M', 'ṇ'), - (0x1E47, 'V'), - (0x1E48, 'M', 'ṉ'), - (0x1E49, 'V'), - (0x1E4A, 'M', 'ṋ'), - (0x1E4B, 'V'), - (0x1E4C, 'M', 'ṍ'), - (0x1E4D, 'V'), - (0x1E4E, 'M', 'ṏ'), - (0x1E4F, 'V'), - (0x1E50, 'M', 'ṑ'), - (0x1E51, 'V'), - (0x1E52, 'M', 'ṓ'), - (0x1E53, 'V'), - (0x1E54, 'M', 'ṕ'), - (0x1E55, 'V'), - (0x1E56, 'M', 'ṗ'), - (0x1E57, 'V'), - (0x1E58, 'M', 'ṙ'), - (0x1E59, 'V'), - (0x1E5A, 'M', 'ṛ'), - (0x1E5B, 'V'), - (0x1E5C, 'M', 'ṝ'), - (0x1E5D, 'V'), - (0x1E5E, 'M', 'ṟ'), - (0x1E5F, 'V'), - (0x1E60, 'M', 'ṡ'), - (0x1E61, 'V'), - (0x1E62, 'M', 'ṣ'), - (0x1E63, 'V'), - (0x1E64, 'M', 'ṥ'), - (0x1E65, 'V'), - (0x1E66, 'M', 'ṧ'), - (0x1E67, 'V'), - (0x1E68, 'M', 'ṩ'), - (0x1E69, 'V'), - (0x1E6A, 'M', 'ṫ'), - (0x1E6B, 'V'), - (0x1E6C, 'M', 'ṭ'), - (0x1E6D, 'V'), - (0x1E6E, 'M', 'ṯ'), - (0x1E6F, 'V'), - (0x1E70, 'M', 'ṱ'), - (0x1E71, 'V'), - (0x1E72, 'M', 'ṳ'), - (0x1E73, 'V'), - (0x1E74, 'M', 'ṵ'), - (0x1E75, 'V'), - (0x1E76, 'M', 'ṷ'), - (0x1E77, 'V'), - (0x1E78, 'M', 'ṹ'), - (0x1E79, 'V'), - (0x1E7A, 'M', 'ṻ'), - (0x1E7B, 'V'), - (0x1E7C, 'M', 'ṽ'), - (0x1E7D, 'V'), - (0x1E7E, 'M', 'ṿ'), - (0x1E7F, 'V'), - (0x1E80, 'M', 'ẁ'), - (0x1E81, 'V'), - (0x1E82, 'M', 'ẃ'), - (0x1E83, 'V'), - (0x1E84, 'M', 'ẅ'), - (0x1E85, 'V'), - (0x1E86, 'M', 'ẇ'), - (0x1E87, 'V'), - ] - -def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E88, 'M', 'ẉ'), - (0x1E89, 'V'), - (0x1E8A, 'M', 'ẋ'), - (0x1E8B, 'V'), - (0x1E8C, 'M', 'ẍ'), - (0x1E8D, 'V'), - (0x1E8E, 'M', 'ẏ'), - (0x1E8F, 'V'), - (0x1E90, 'M', 'ẑ'), - (0x1E91, 'V'), - (0x1E92, 'M', 'ẓ'), - (0x1E93, 'V'), - (0x1E94, 'M', 'ẕ'), - (0x1E95, 'V'), - (0x1E9A, 'M', 'aʾ'), - (0x1E9B, 'M', 'ṡ'), - (0x1E9C, 'V'), - (0x1E9E, 'M', 'ß'), - (0x1E9F, 'V'), - (0x1EA0, 'M', 'ạ'), - (0x1EA1, 'V'), - (0x1EA2, 'M', 'ả'), - (0x1EA3, 'V'), - (0x1EA4, 'M', 'ấ'), - (0x1EA5, 'V'), - (0x1EA6, 'M', 'ầ'), - (0x1EA7, 'V'), - (0x1EA8, 'M', 'ẩ'), - (0x1EA9, 'V'), - (0x1EAA, 'M', 'ẫ'), - (0x1EAB, 'V'), - (0x1EAC, 'M', 'ậ'), - (0x1EAD, 'V'), - (0x1EAE, 'M', 'ắ'), - (0x1EAF, 'V'), - (0x1EB0, 'M', 'ằ'), - (0x1EB1, 'V'), - (0x1EB2, 'M', 'ẳ'), - (0x1EB3, 'V'), - (0x1EB4, 'M', 'ẵ'), - (0x1EB5, 'V'), - (0x1EB6, 'M', 'ặ'), - (0x1EB7, 'V'), - (0x1EB8, 'M', 'ẹ'), - (0x1EB9, 'V'), - (0x1EBA, 'M', 'ẻ'), - (0x1EBB, 'V'), - (0x1EBC, 'M', 'ẽ'), - (0x1EBD, 'V'), - (0x1EBE, 'M', 'ế'), - (0x1EBF, 'V'), - (0x1EC0, 'M', 'ề'), - (0x1EC1, 'V'), - (0x1EC2, 'M', 'ể'), - (0x1EC3, 'V'), - (0x1EC4, 'M', 'ễ'), - (0x1EC5, 'V'), - (0x1EC6, 'M', 'ệ'), - (0x1EC7, 'V'), - (0x1EC8, 'M', 'ỉ'), - (0x1EC9, 'V'), - (0x1ECA, 'M', 'ị'), - (0x1ECB, 'V'), - (0x1ECC, 'M', 'ọ'), - (0x1ECD, 'V'), - (0x1ECE, 'M', 'ỏ'), - (0x1ECF, 'V'), - (0x1ED0, 'M', 'ố'), - (0x1ED1, 'V'), - (0x1ED2, 'M', 'ồ'), - (0x1ED3, 'V'), - (0x1ED4, 'M', 'ổ'), - (0x1ED5, 'V'), - (0x1ED6, 'M', 'ỗ'), - (0x1ED7, 'V'), - (0x1ED8, 'M', 'ộ'), - (0x1ED9, 'V'), - (0x1EDA, 'M', 'ớ'), - (0x1EDB, 'V'), - (0x1EDC, 'M', 'ờ'), - (0x1EDD, 'V'), - (0x1EDE, 'M', 'ở'), - (0x1EDF, 'V'), - (0x1EE0, 'M', 'ỡ'), - (0x1EE1, 'V'), - (0x1EE2, 'M', 'ợ'), - (0x1EE3, 'V'), - (0x1EE4, 'M', 'ụ'), - (0x1EE5, 'V'), - (0x1EE6, 'M', 'ủ'), - (0x1EE7, 'V'), - (0x1EE8, 'M', 'ứ'), - (0x1EE9, 'V'), - (0x1EEA, 'M', 'ừ'), - (0x1EEB, 'V'), - (0x1EEC, 'M', 'ử'), - (0x1EED, 'V'), - (0x1EEE, 'M', 'ữ'), - (0x1EEF, 'V'), - (0x1EF0, 'M', 'ự'), - ] - -def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EF1, 'V'), - (0x1EF2, 'M', 'ỳ'), - (0x1EF3, 'V'), - (0x1EF4, 'M', 'ỵ'), - (0x1EF5, 'V'), - (0x1EF6, 'M', 'ỷ'), - (0x1EF7, 'V'), - (0x1EF8, 'M', 'ỹ'), - (0x1EF9, 'V'), - (0x1EFA, 'M', 'ỻ'), - (0x1EFB, 'V'), - (0x1EFC, 'M', 'ỽ'), - (0x1EFD, 'V'), - (0x1EFE, 'M', 'ỿ'), - (0x1EFF, 'V'), - (0x1F08, 'M', 'ἀ'), - (0x1F09, 'M', 'ἁ'), - (0x1F0A, 'M', 'ἂ'), - (0x1F0B, 'M', 'ἃ'), - (0x1F0C, 'M', 'ἄ'), - (0x1F0D, 'M', 'ἅ'), - (0x1F0E, 'M', 'ἆ'), - (0x1F0F, 'M', 'ἇ'), - (0x1F10, 'V'), - (0x1F16, 'X'), - (0x1F18, 'M', 'ἐ'), - (0x1F19, 'M', 'ἑ'), - (0x1F1A, 'M', 'ἒ'), - (0x1F1B, 'M', 'ἓ'), - (0x1F1C, 'M', 'ἔ'), - (0x1F1D, 'M', 'ἕ'), - (0x1F1E, 'X'), - (0x1F20, 'V'), - (0x1F28, 'M', 'ἠ'), - (0x1F29, 'M', 'ἡ'), - (0x1F2A, 'M', 'ἢ'), - (0x1F2B, 'M', 'ἣ'), - (0x1F2C, 'M', 'ἤ'), - (0x1F2D, 'M', 'ἥ'), - (0x1F2E, 'M', 'ἦ'), - (0x1F2F, 'M', 'ἧ'), - (0x1F30, 'V'), - (0x1F38, 'M', 'ἰ'), - (0x1F39, 'M', 'ἱ'), - (0x1F3A, 'M', 'ἲ'), - (0x1F3B, 'M', 'ἳ'), - (0x1F3C, 'M', 'ἴ'), - (0x1F3D, 'M', 'ἵ'), - (0x1F3E, 'M', 'ἶ'), - (0x1F3F, 'M', 'ἷ'), - (0x1F40, 'V'), - (0x1F46, 'X'), - (0x1F48, 'M', 'ὀ'), - (0x1F49, 'M', 'ὁ'), - (0x1F4A, 'M', 'ὂ'), - (0x1F4B, 'M', 'ὃ'), - (0x1F4C, 'M', 'ὄ'), - (0x1F4D, 'M', 'ὅ'), - (0x1F4E, 'X'), - (0x1F50, 'V'), - (0x1F58, 'X'), - (0x1F59, 'M', 'ὑ'), - (0x1F5A, 'X'), - (0x1F5B, 'M', 'ὓ'), - (0x1F5C, 'X'), - (0x1F5D, 'M', 'ὕ'), - (0x1F5E, 'X'), - (0x1F5F, 'M', 'ὗ'), - (0x1F60, 'V'), - (0x1F68, 'M', 'ὠ'), - (0x1F69, 'M', 'ὡ'), - (0x1F6A, 'M', 'ὢ'), - (0x1F6B, 'M', 'ὣ'), - (0x1F6C, 'M', 'ὤ'), - (0x1F6D, 'M', 'ὥ'), - (0x1F6E, 'M', 'ὦ'), - (0x1F6F, 'M', 'ὧ'), - (0x1F70, 'V'), - (0x1F71, 'M', 'ά'), - (0x1F72, 'V'), - (0x1F73, 'M', 'έ'), - (0x1F74, 'V'), - (0x1F75, 'M', 'ή'), - (0x1F76, 'V'), - (0x1F77, 'M', 'ί'), - (0x1F78, 'V'), - (0x1F79, 'M', 'ό'), - (0x1F7A, 'V'), - (0x1F7B, 'M', 'ύ'), - (0x1F7C, 'V'), - (0x1F7D, 'M', 'ώ'), - (0x1F7E, 'X'), - (0x1F80, 'M', 'ἀι'), - (0x1F81, 'M', 'ἁι'), - (0x1F82, 'M', 'ἂι'), - (0x1F83, 'M', 'ἃι'), - (0x1F84, 'M', 'ἄι'), - (0x1F85, 'M', 'ἅι'), - (0x1F86, 'M', 'ἆι'), - (0x1F87, 'M', 'ἇι'), - ] - -def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F88, 'M', 'ἀι'), - (0x1F89, 'M', 'ἁι'), - (0x1F8A, 'M', 'ἂι'), - (0x1F8B, 'M', 'ἃι'), - (0x1F8C, 'M', 'ἄι'), - (0x1F8D, 'M', 'ἅι'), - (0x1F8E, 'M', 'ἆι'), - (0x1F8F, 'M', 'ἇι'), - (0x1F90, 'M', 'ἠι'), - (0x1F91, 'M', 'ἡι'), - (0x1F92, 'M', 'ἢι'), - (0x1F93, 'M', 'ἣι'), - (0x1F94, 'M', 'ἤι'), - (0x1F95, 'M', 'ἥι'), - (0x1F96, 'M', 'ἦι'), - (0x1F97, 'M', 'ἧι'), - (0x1F98, 'M', 'ἠι'), - (0x1F99, 'M', 'ἡι'), - (0x1F9A, 'M', 'ἢι'), - (0x1F9B, 'M', 'ἣι'), - (0x1F9C, 'M', 'ἤι'), - (0x1F9D, 'M', 'ἥι'), - (0x1F9E, 'M', 'ἦι'), - (0x1F9F, 'M', 'ἧι'), - (0x1FA0, 'M', 'ὠι'), - (0x1FA1, 'M', 'ὡι'), - (0x1FA2, 'M', 'ὢι'), - (0x1FA3, 'M', 'ὣι'), - (0x1FA4, 'M', 'ὤι'), - (0x1FA5, 'M', 'ὥι'), - (0x1FA6, 'M', 'ὦι'), - (0x1FA7, 'M', 'ὧι'), - (0x1FA8, 'M', 'ὠι'), - (0x1FA9, 'M', 'ὡι'), - (0x1FAA, 'M', 'ὢι'), - (0x1FAB, 'M', 'ὣι'), - (0x1FAC, 'M', 'ὤι'), - (0x1FAD, 'M', 'ὥι'), - (0x1FAE, 'M', 'ὦι'), - (0x1FAF, 'M', 'ὧι'), - (0x1FB0, 'V'), - (0x1FB2, 'M', 'ὰι'), - (0x1FB3, 'M', 'αι'), - (0x1FB4, 'M', 'άι'), - (0x1FB5, 'X'), - (0x1FB6, 'V'), - (0x1FB7, 'M', 'ᾶι'), - (0x1FB8, 'M', 'ᾰ'), - (0x1FB9, 'M', 'ᾱ'), - (0x1FBA, 'M', 'ὰ'), - (0x1FBB, 'M', 'ά'), - (0x1FBC, 'M', 'αι'), - (0x1FBD, '3', ' ̓'), - (0x1FBE, 'M', 'ι'), - (0x1FBF, '3', ' ̓'), - (0x1FC0, '3', ' ͂'), - (0x1FC1, '3', ' ̈͂'), - (0x1FC2, 'M', 'ὴι'), - (0x1FC3, 'M', 'ηι'), - (0x1FC4, 'M', 'ήι'), - (0x1FC5, 'X'), - (0x1FC6, 'V'), - (0x1FC7, 'M', 'ῆι'), - (0x1FC8, 'M', 'ὲ'), - (0x1FC9, 'M', 'έ'), - (0x1FCA, 'M', 'ὴ'), - (0x1FCB, 'M', 'ή'), - (0x1FCC, 'M', 'ηι'), - (0x1FCD, '3', ' ̓̀'), - (0x1FCE, '3', ' ̓́'), - (0x1FCF, '3', ' ̓͂'), - (0x1FD0, 'V'), - (0x1FD3, 'M', 'ΐ'), - (0x1FD4, 'X'), - (0x1FD6, 'V'), - (0x1FD8, 'M', 'ῐ'), - (0x1FD9, 'M', 'ῑ'), - (0x1FDA, 'M', 'ὶ'), - (0x1FDB, 'M', 'ί'), - (0x1FDC, 'X'), - (0x1FDD, '3', ' ̔̀'), - (0x1FDE, '3', ' ̔́'), - (0x1FDF, '3', ' ̔͂'), - (0x1FE0, 'V'), - (0x1FE3, 'M', 'ΰ'), - (0x1FE4, 'V'), - (0x1FE8, 'M', 'ῠ'), - (0x1FE9, 'M', 'ῡ'), - (0x1FEA, 'M', 'ὺ'), - (0x1FEB, 'M', 'ύ'), - (0x1FEC, 'M', 'ῥ'), - (0x1FED, '3', ' ̈̀'), - (0x1FEE, '3', ' ̈́'), - (0x1FEF, '3', '`'), - (0x1FF0, 'X'), - (0x1FF2, 'M', 'ὼι'), - (0x1FF3, 'M', 'ωι'), - (0x1FF4, 'M', 'ώι'), - (0x1FF5, 'X'), - (0x1FF6, 'V'), - ] - -def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FF7, 'M', 'ῶι'), - (0x1FF8, 'M', 'ὸ'), - (0x1FF9, 'M', 'ό'), - (0x1FFA, 'M', 'ὼ'), - (0x1FFB, 'M', 'ώ'), - (0x1FFC, 'M', 'ωι'), - (0x1FFD, '3', ' ́'), - (0x1FFE, '3', ' ̔'), - (0x1FFF, 'X'), - (0x2000, '3', ' '), - (0x200B, 'I'), - (0x200C, 'D', ''), - (0x200E, 'X'), - (0x2010, 'V'), - (0x2011, 'M', '‐'), - (0x2012, 'V'), - (0x2017, '3', ' ̳'), - (0x2018, 'V'), - (0x2024, 'X'), - (0x2027, 'V'), - (0x2028, 'X'), - (0x202F, '3', ' '), - (0x2030, 'V'), - (0x2033, 'M', '′′'), - (0x2034, 'M', '′′′'), - (0x2035, 'V'), - (0x2036, 'M', '‵‵'), - (0x2037, 'M', '‵‵‵'), - (0x2038, 'V'), - (0x203C, '3', '!!'), - (0x203D, 'V'), - (0x203E, '3', ' ̅'), - (0x203F, 'V'), - (0x2047, '3', '??'), - (0x2048, '3', '?!'), - (0x2049, '3', '!?'), - (0x204A, 'V'), - (0x2057, 'M', '′′′′'), - (0x2058, 'V'), - (0x205F, '3', ' '), - (0x2060, 'I'), - (0x2061, 'X'), - (0x2064, 'I'), - (0x2065, 'X'), - (0x2070, 'M', '0'), - (0x2071, 'M', 'i'), - (0x2072, 'X'), - (0x2074, 'M', '4'), - (0x2075, 'M', '5'), - (0x2076, 'M', '6'), - (0x2077, 'M', '7'), - (0x2078, 'M', '8'), - (0x2079, 'M', '9'), - (0x207A, '3', '+'), - (0x207B, 'M', '−'), - (0x207C, '3', '='), - (0x207D, '3', '('), - (0x207E, '3', ')'), - (0x207F, 'M', 'n'), - (0x2080, 'M', '0'), - (0x2081, 'M', '1'), - (0x2082, 'M', '2'), - (0x2083, 'M', '3'), - (0x2084, 'M', '4'), - (0x2085, 'M', '5'), - (0x2086, 'M', '6'), - (0x2087, 'M', '7'), - (0x2088, 'M', '8'), - (0x2089, 'M', '9'), - (0x208A, '3', '+'), - (0x208B, 'M', '−'), - (0x208C, '3', '='), - (0x208D, '3', '('), - (0x208E, '3', ')'), - (0x208F, 'X'), - (0x2090, 'M', 'a'), - (0x2091, 'M', 'e'), - (0x2092, 'M', 'o'), - (0x2093, 'M', 'x'), - (0x2094, 'M', 'ə'), - (0x2095, 'M', 'h'), - (0x2096, 'M', 'k'), - (0x2097, 'M', 'l'), - (0x2098, 'M', 'm'), - (0x2099, 'M', 'n'), - (0x209A, 'M', 'p'), - (0x209B, 'M', 's'), - (0x209C, 'M', 't'), - (0x209D, 'X'), - (0x20A0, 'V'), - (0x20A8, 'M', 'rs'), - (0x20A9, 'V'), - (0x20C1, 'X'), - (0x20D0, 'V'), - (0x20F1, 'X'), - (0x2100, '3', 'a/c'), - (0x2101, '3', 'a/s'), - (0x2102, 'M', 'c'), - (0x2103, 'M', '°c'), - (0x2104, 'V'), - ] - -def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2105, '3', 'c/o'), - (0x2106, '3', 'c/u'), - (0x2107, 'M', 'ɛ'), - (0x2108, 'V'), - (0x2109, 'M', '°f'), - (0x210A, 'M', 'g'), - (0x210B, 'M', 'h'), - (0x210F, 'M', 'ħ'), - (0x2110, 'M', 'i'), - (0x2112, 'M', 'l'), - (0x2114, 'V'), - (0x2115, 'M', 'n'), - (0x2116, 'M', 'no'), - (0x2117, 'V'), - (0x2119, 'M', 'p'), - (0x211A, 'M', 'q'), - (0x211B, 'M', 'r'), - (0x211E, 'V'), - (0x2120, 'M', 'sm'), - (0x2121, 'M', 'tel'), - (0x2122, 'M', 'tm'), - (0x2123, 'V'), - (0x2124, 'M', 'z'), - (0x2125, 'V'), - (0x2126, 'M', 'ω'), - (0x2127, 'V'), - (0x2128, 'M', 'z'), - (0x2129, 'V'), - (0x212A, 'M', 'k'), - (0x212B, 'M', 'å'), - (0x212C, 'M', 'b'), - (0x212D, 'M', 'c'), - (0x212E, 'V'), - (0x212F, 'M', 'e'), - (0x2131, 'M', 'f'), - (0x2132, 'X'), - (0x2133, 'M', 'm'), - (0x2134, 'M', 'o'), - (0x2135, 'M', 'א'), - (0x2136, 'M', 'ב'), - (0x2137, 'M', 'ג'), - (0x2138, 'M', 'ד'), - (0x2139, 'M', 'i'), - (0x213A, 'V'), - (0x213B, 'M', 'fax'), - (0x213C, 'M', 'π'), - (0x213D, 'M', 'γ'), - (0x213F, 'M', 'π'), - (0x2140, 'M', '∑'), - (0x2141, 'V'), - (0x2145, 'M', 'd'), - (0x2147, 'M', 'e'), - (0x2148, 'M', 'i'), - (0x2149, 'M', 'j'), - (0x214A, 'V'), - (0x2150, 'M', '1⁄7'), - (0x2151, 'M', '1⁄9'), - (0x2152, 'M', '1⁄10'), - (0x2153, 'M', '1⁄3'), - (0x2154, 'M', '2⁄3'), - (0x2155, 'M', '1⁄5'), - (0x2156, 'M', '2⁄5'), - (0x2157, 'M', '3⁄5'), - (0x2158, 'M', '4⁄5'), - (0x2159, 'M', '1⁄6'), - (0x215A, 'M', '5⁄6'), - (0x215B, 'M', '1⁄8'), - (0x215C, 'M', '3⁄8'), - (0x215D, 'M', '5⁄8'), - (0x215E, 'M', '7⁄8'), - (0x215F, 'M', '1⁄'), - (0x2160, 'M', 'i'), - (0x2161, 'M', 'ii'), - (0x2162, 'M', 'iii'), - (0x2163, 'M', 'iv'), - (0x2164, 'M', 'v'), - (0x2165, 'M', 'vi'), - (0x2166, 'M', 'vii'), - (0x2167, 'M', 'viii'), - (0x2168, 'M', 'ix'), - (0x2169, 'M', 'x'), - (0x216A, 'M', 'xi'), - (0x216B, 'M', 'xii'), - (0x216C, 'M', 'l'), - (0x216D, 'M', 'c'), - (0x216E, 'M', 'd'), - (0x216F, 'M', 'm'), - (0x2170, 'M', 'i'), - (0x2171, 'M', 'ii'), - (0x2172, 'M', 'iii'), - (0x2173, 'M', 'iv'), - (0x2174, 'M', 'v'), - (0x2175, 'M', 'vi'), - (0x2176, 'M', 'vii'), - (0x2177, 'M', 'viii'), - (0x2178, 'M', 'ix'), - (0x2179, 'M', 'x'), - (0x217A, 'M', 'xi'), - (0x217B, 'M', 'xii'), - (0x217C, 'M', 'l'), - ] - -def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x217D, 'M', 'c'), - (0x217E, 'M', 'd'), - (0x217F, 'M', 'm'), - (0x2180, 'V'), - (0x2183, 'X'), - (0x2184, 'V'), - (0x2189, 'M', '0⁄3'), - (0x218A, 'V'), - (0x218C, 'X'), - (0x2190, 'V'), - (0x222C, 'M', '∫∫'), - (0x222D, 'M', '∫∫∫'), - (0x222E, 'V'), - (0x222F, 'M', '∮∮'), - (0x2230, 'M', '∮∮∮'), - (0x2231, 'V'), - (0x2329, 'M', '〈'), - (0x232A, 'M', '〉'), - (0x232B, 'V'), - (0x2427, 'X'), - (0x2440, 'V'), - (0x244B, 'X'), - (0x2460, 'M', '1'), - (0x2461, 'M', '2'), - (0x2462, 'M', '3'), - (0x2463, 'M', '4'), - (0x2464, 'M', '5'), - (0x2465, 'M', '6'), - (0x2466, 'M', '7'), - (0x2467, 'M', '8'), - (0x2468, 'M', '9'), - (0x2469, 'M', '10'), - (0x246A, 'M', '11'), - (0x246B, 'M', '12'), - (0x246C, 'M', '13'), - (0x246D, 'M', '14'), - (0x246E, 'M', '15'), - (0x246F, 'M', '16'), - (0x2470, 'M', '17'), - (0x2471, 'M', '18'), - (0x2472, 'M', '19'), - (0x2473, 'M', '20'), - (0x2474, '3', '(1)'), - (0x2475, '3', '(2)'), - (0x2476, '3', '(3)'), - (0x2477, '3', '(4)'), - (0x2478, '3', '(5)'), - (0x2479, '3', '(6)'), - (0x247A, '3', '(7)'), - (0x247B, '3', '(8)'), - (0x247C, '3', '(9)'), - (0x247D, '3', '(10)'), - (0x247E, '3', '(11)'), - (0x247F, '3', '(12)'), - (0x2480, '3', '(13)'), - (0x2481, '3', '(14)'), - (0x2482, '3', '(15)'), - (0x2483, '3', '(16)'), - (0x2484, '3', '(17)'), - (0x2485, '3', '(18)'), - (0x2486, '3', '(19)'), - (0x2487, '3', '(20)'), - (0x2488, 'X'), - (0x249C, '3', '(a)'), - (0x249D, '3', '(b)'), - (0x249E, '3', '(c)'), - (0x249F, '3', '(d)'), - (0x24A0, '3', '(e)'), - (0x24A1, '3', '(f)'), - (0x24A2, '3', '(g)'), - (0x24A3, '3', '(h)'), - (0x24A4, '3', '(i)'), - (0x24A5, '3', '(j)'), - (0x24A6, '3', '(k)'), - (0x24A7, '3', '(l)'), - (0x24A8, '3', '(m)'), - (0x24A9, '3', '(n)'), - (0x24AA, '3', '(o)'), - (0x24AB, '3', '(p)'), - (0x24AC, '3', '(q)'), - (0x24AD, '3', '(r)'), - (0x24AE, '3', '(s)'), - (0x24AF, '3', '(t)'), - (0x24B0, '3', '(u)'), - (0x24B1, '3', '(v)'), - (0x24B2, '3', '(w)'), - (0x24B3, '3', '(x)'), - (0x24B4, '3', '(y)'), - (0x24B5, '3', '(z)'), - (0x24B6, 'M', 'a'), - (0x24B7, 'M', 'b'), - (0x24B8, 'M', 'c'), - (0x24B9, 'M', 'd'), - (0x24BA, 'M', 'e'), - (0x24BB, 'M', 'f'), - (0x24BC, 'M', 'g'), - (0x24BD, 'M', 'h'), - (0x24BE, 'M', 'i'), - (0x24BF, 'M', 'j'), - (0x24C0, 'M', 'k'), - ] - -def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x24C1, 'M', 'l'), - (0x24C2, 'M', 'm'), - (0x24C3, 'M', 'n'), - (0x24C4, 'M', 'o'), - (0x24C5, 'M', 'p'), - (0x24C6, 'M', 'q'), - (0x24C7, 'M', 'r'), - (0x24C8, 'M', 's'), - (0x24C9, 'M', 't'), - (0x24CA, 'M', 'u'), - (0x24CB, 'M', 'v'), - (0x24CC, 'M', 'w'), - (0x24CD, 'M', 'x'), - (0x24CE, 'M', 'y'), - (0x24CF, 'M', 'z'), - (0x24D0, 'M', 'a'), - (0x24D1, 'M', 'b'), - (0x24D2, 'M', 'c'), - (0x24D3, 'M', 'd'), - (0x24D4, 'M', 'e'), - (0x24D5, 'M', 'f'), - (0x24D6, 'M', 'g'), - (0x24D7, 'M', 'h'), - (0x24D8, 'M', 'i'), - (0x24D9, 'M', 'j'), - (0x24DA, 'M', 'k'), - (0x24DB, 'M', 'l'), - (0x24DC, 'M', 'm'), - (0x24DD, 'M', 'n'), - (0x24DE, 'M', 'o'), - (0x24DF, 'M', 'p'), - (0x24E0, 'M', 'q'), - (0x24E1, 'M', 'r'), - (0x24E2, 'M', 's'), - (0x24E3, 'M', 't'), - (0x24E4, 'M', 'u'), - (0x24E5, 'M', 'v'), - (0x24E6, 'M', 'w'), - (0x24E7, 'M', 'x'), - (0x24E8, 'M', 'y'), - (0x24E9, 'M', 'z'), - (0x24EA, 'M', '0'), - (0x24EB, 'V'), - (0x2A0C, 'M', '∫∫∫∫'), - (0x2A0D, 'V'), - (0x2A74, '3', '::='), - (0x2A75, '3', '=='), - (0x2A76, '3', '==='), - (0x2A77, 'V'), - (0x2ADC, 'M', '⫝̸'), - (0x2ADD, 'V'), - (0x2B74, 'X'), - (0x2B76, 'V'), - (0x2B96, 'X'), - (0x2B97, 'V'), - (0x2C00, 'M', 'ⰰ'), - (0x2C01, 'M', 'ⰱ'), - (0x2C02, 'M', 'ⰲ'), - (0x2C03, 'M', 'ⰳ'), - (0x2C04, 'M', 'ⰴ'), - (0x2C05, 'M', 'ⰵ'), - (0x2C06, 'M', 'ⰶ'), - (0x2C07, 'M', 'ⰷ'), - (0x2C08, 'M', 'ⰸ'), - (0x2C09, 'M', 'ⰹ'), - (0x2C0A, 'M', 'ⰺ'), - (0x2C0B, 'M', 'ⰻ'), - (0x2C0C, 'M', 'ⰼ'), - (0x2C0D, 'M', 'ⰽ'), - (0x2C0E, 'M', 'ⰾ'), - (0x2C0F, 'M', 'ⰿ'), - (0x2C10, 'M', 'ⱀ'), - (0x2C11, 'M', 'ⱁ'), - (0x2C12, 'M', 'ⱂ'), - (0x2C13, 'M', 'ⱃ'), - (0x2C14, 'M', 'ⱄ'), - (0x2C15, 'M', 'ⱅ'), - (0x2C16, 'M', 'ⱆ'), - (0x2C17, 'M', 'ⱇ'), - (0x2C18, 'M', 'ⱈ'), - (0x2C19, 'M', 'ⱉ'), - (0x2C1A, 'M', 'ⱊ'), - (0x2C1B, 'M', 'ⱋ'), - (0x2C1C, 'M', 'ⱌ'), - (0x2C1D, 'M', 'ⱍ'), - (0x2C1E, 'M', 'ⱎ'), - (0x2C1F, 'M', 'ⱏ'), - (0x2C20, 'M', 'ⱐ'), - (0x2C21, 'M', 'ⱑ'), - (0x2C22, 'M', 'ⱒ'), - (0x2C23, 'M', 'ⱓ'), - (0x2C24, 'M', 'ⱔ'), - (0x2C25, 'M', 'ⱕ'), - (0x2C26, 'M', 'ⱖ'), - (0x2C27, 'M', 'ⱗ'), - (0x2C28, 'M', 'ⱘ'), - (0x2C29, 'M', 'ⱙ'), - (0x2C2A, 'M', 'ⱚ'), - (0x2C2B, 'M', 'ⱛ'), - (0x2C2C, 'M', 'ⱜ'), - ] - -def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2C2D, 'M', 'ⱝ'), - (0x2C2E, 'M', 'ⱞ'), - (0x2C2F, 'M', 'ⱟ'), - (0x2C30, 'V'), - (0x2C60, 'M', 'ⱡ'), - (0x2C61, 'V'), - (0x2C62, 'M', 'ɫ'), - (0x2C63, 'M', 'ᵽ'), - (0x2C64, 'M', 'ɽ'), - (0x2C65, 'V'), - (0x2C67, 'M', 'ⱨ'), - (0x2C68, 'V'), - (0x2C69, 'M', 'ⱪ'), - (0x2C6A, 'V'), - (0x2C6B, 'M', 'ⱬ'), - (0x2C6C, 'V'), - (0x2C6D, 'M', 'ɑ'), - (0x2C6E, 'M', 'ɱ'), - (0x2C6F, 'M', 'ɐ'), - (0x2C70, 'M', 'ɒ'), - (0x2C71, 'V'), - (0x2C72, 'M', 'ⱳ'), - (0x2C73, 'V'), - (0x2C75, 'M', 'ⱶ'), - (0x2C76, 'V'), - (0x2C7C, 'M', 'j'), - (0x2C7D, 'M', 'v'), - (0x2C7E, 'M', 'ȿ'), - (0x2C7F, 'M', 'ɀ'), - (0x2C80, 'M', 'ⲁ'), - (0x2C81, 'V'), - (0x2C82, 'M', 'ⲃ'), - (0x2C83, 'V'), - (0x2C84, 'M', 'ⲅ'), - (0x2C85, 'V'), - (0x2C86, 'M', 'ⲇ'), - (0x2C87, 'V'), - (0x2C88, 'M', 'ⲉ'), - (0x2C89, 'V'), - (0x2C8A, 'M', 'ⲋ'), - (0x2C8B, 'V'), - (0x2C8C, 'M', 'ⲍ'), - (0x2C8D, 'V'), - (0x2C8E, 'M', 'ⲏ'), - (0x2C8F, 'V'), - (0x2C90, 'M', 'ⲑ'), - (0x2C91, 'V'), - (0x2C92, 'M', 'ⲓ'), - (0x2C93, 'V'), - (0x2C94, 'M', 'ⲕ'), - (0x2C95, 'V'), - (0x2C96, 'M', 'ⲗ'), - (0x2C97, 'V'), - (0x2C98, 'M', 'ⲙ'), - (0x2C99, 'V'), - (0x2C9A, 'M', 'ⲛ'), - (0x2C9B, 'V'), - (0x2C9C, 'M', 'ⲝ'), - (0x2C9D, 'V'), - (0x2C9E, 'M', 'ⲟ'), - (0x2C9F, 'V'), - (0x2CA0, 'M', 'ⲡ'), - (0x2CA1, 'V'), - (0x2CA2, 'M', 'ⲣ'), - (0x2CA3, 'V'), - (0x2CA4, 'M', 'ⲥ'), - (0x2CA5, 'V'), - (0x2CA6, 'M', 'ⲧ'), - (0x2CA7, 'V'), - (0x2CA8, 'M', 'ⲩ'), - (0x2CA9, 'V'), - (0x2CAA, 'M', 'ⲫ'), - (0x2CAB, 'V'), - (0x2CAC, 'M', 'ⲭ'), - (0x2CAD, 'V'), - (0x2CAE, 'M', 'ⲯ'), - (0x2CAF, 'V'), - (0x2CB0, 'M', 'ⲱ'), - (0x2CB1, 'V'), - (0x2CB2, 'M', 'ⲳ'), - (0x2CB3, 'V'), - (0x2CB4, 'M', 'ⲵ'), - (0x2CB5, 'V'), - (0x2CB6, 'M', 'ⲷ'), - (0x2CB7, 'V'), - (0x2CB8, 'M', 'ⲹ'), - (0x2CB9, 'V'), - (0x2CBA, 'M', 'ⲻ'), - (0x2CBB, 'V'), - (0x2CBC, 'M', 'ⲽ'), - (0x2CBD, 'V'), - (0x2CBE, 'M', 'ⲿ'), - (0x2CBF, 'V'), - (0x2CC0, 'M', 'ⳁ'), - (0x2CC1, 'V'), - (0x2CC2, 'M', 'ⳃ'), - (0x2CC3, 'V'), - (0x2CC4, 'M', 'ⳅ'), - (0x2CC5, 'V'), - (0x2CC6, 'M', 'ⳇ'), - ] - -def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2CC7, 'V'), - (0x2CC8, 'M', 'ⳉ'), - (0x2CC9, 'V'), - (0x2CCA, 'M', 'ⳋ'), - (0x2CCB, 'V'), - (0x2CCC, 'M', 'ⳍ'), - (0x2CCD, 'V'), - (0x2CCE, 'M', 'ⳏ'), - (0x2CCF, 'V'), - (0x2CD0, 'M', 'ⳑ'), - (0x2CD1, 'V'), - (0x2CD2, 'M', 'ⳓ'), - (0x2CD3, 'V'), - (0x2CD4, 'M', 'ⳕ'), - (0x2CD5, 'V'), - (0x2CD6, 'M', 'ⳗ'), - (0x2CD7, 'V'), - (0x2CD8, 'M', 'ⳙ'), - (0x2CD9, 'V'), - (0x2CDA, 'M', 'ⳛ'), - (0x2CDB, 'V'), - (0x2CDC, 'M', 'ⳝ'), - (0x2CDD, 'V'), - (0x2CDE, 'M', 'ⳟ'), - (0x2CDF, 'V'), - (0x2CE0, 'M', 'ⳡ'), - (0x2CE1, 'V'), - (0x2CE2, 'M', 'ⳣ'), - (0x2CE3, 'V'), - (0x2CEB, 'M', 'ⳬ'), - (0x2CEC, 'V'), - (0x2CED, 'M', 'ⳮ'), - (0x2CEE, 'V'), - (0x2CF2, 'M', 'ⳳ'), - (0x2CF3, 'V'), - (0x2CF4, 'X'), - (0x2CF9, 'V'), - (0x2D26, 'X'), - (0x2D27, 'V'), - (0x2D28, 'X'), - (0x2D2D, 'V'), - (0x2D2E, 'X'), - (0x2D30, 'V'), - (0x2D68, 'X'), - (0x2D6F, 'M', 'ⵡ'), - (0x2D70, 'V'), - (0x2D71, 'X'), - (0x2D7F, 'V'), - (0x2D97, 'X'), - (0x2DA0, 'V'), - (0x2DA7, 'X'), - (0x2DA8, 'V'), - (0x2DAF, 'X'), - (0x2DB0, 'V'), - (0x2DB7, 'X'), - (0x2DB8, 'V'), - (0x2DBF, 'X'), - (0x2DC0, 'V'), - (0x2DC7, 'X'), - (0x2DC8, 'V'), - (0x2DCF, 'X'), - (0x2DD0, 'V'), - (0x2DD7, 'X'), - (0x2DD8, 'V'), - (0x2DDF, 'X'), - (0x2DE0, 'V'), - (0x2E5E, 'X'), - (0x2E80, 'V'), - (0x2E9A, 'X'), - (0x2E9B, 'V'), - (0x2E9F, 'M', '母'), - (0x2EA0, 'V'), - (0x2EF3, 'M', '龟'), - (0x2EF4, 'X'), - (0x2F00, 'M', '一'), - (0x2F01, 'M', '丨'), - (0x2F02, 'M', '丶'), - (0x2F03, 'M', '丿'), - (0x2F04, 'M', '乙'), - (0x2F05, 'M', '亅'), - (0x2F06, 'M', '二'), - (0x2F07, 'M', '亠'), - (0x2F08, 'M', '人'), - (0x2F09, 'M', '儿'), - (0x2F0A, 'M', '入'), - (0x2F0B, 'M', '八'), - (0x2F0C, 'M', '冂'), - (0x2F0D, 'M', '冖'), - (0x2F0E, 'M', '冫'), - (0x2F0F, 'M', '几'), - (0x2F10, 'M', '凵'), - (0x2F11, 'M', '刀'), - (0x2F12, 'M', '力'), - (0x2F13, 'M', '勹'), - (0x2F14, 'M', '匕'), - (0x2F15, 'M', '匚'), - (0x2F16, 'M', '匸'), - (0x2F17, 'M', '十'), - (0x2F18, 'M', '卜'), - (0x2F19, 'M', '卩'), - ] - -def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F1A, 'M', '厂'), - (0x2F1B, 'M', '厶'), - (0x2F1C, 'M', '又'), - (0x2F1D, 'M', '口'), - (0x2F1E, 'M', '囗'), - (0x2F1F, 'M', '土'), - (0x2F20, 'M', '士'), - (0x2F21, 'M', '夂'), - (0x2F22, 'M', '夊'), - (0x2F23, 'M', '夕'), - (0x2F24, 'M', '大'), - (0x2F25, 'M', '女'), - (0x2F26, 'M', '子'), - (0x2F27, 'M', '宀'), - (0x2F28, 'M', '寸'), - (0x2F29, 'M', '小'), - (0x2F2A, 'M', '尢'), - (0x2F2B, 'M', '尸'), - (0x2F2C, 'M', '屮'), - (0x2F2D, 'M', '山'), - (0x2F2E, 'M', '巛'), - (0x2F2F, 'M', '工'), - (0x2F30, 'M', '己'), - (0x2F31, 'M', '巾'), - (0x2F32, 'M', '干'), - (0x2F33, 'M', '幺'), - (0x2F34, 'M', '广'), - (0x2F35, 'M', '廴'), - (0x2F36, 'M', '廾'), - (0x2F37, 'M', '弋'), - (0x2F38, 'M', '弓'), - (0x2F39, 'M', '彐'), - (0x2F3A, 'M', '彡'), - (0x2F3B, 'M', '彳'), - (0x2F3C, 'M', '心'), - (0x2F3D, 'M', '戈'), - (0x2F3E, 'M', '戶'), - (0x2F3F, 'M', '手'), - (0x2F40, 'M', '支'), - (0x2F41, 'M', '攴'), - (0x2F42, 'M', '文'), - (0x2F43, 'M', '斗'), - (0x2F44, 'M', '斤'), - (0x2F45, 'M', '方'), - (0x2F46, 'M', '无'), - (0x2F47, 'M', '日'), - (0x2F48, 'M', '曰'), - (0x2F49, 'M', '月'), - (0x2F4A, 'M', '木'), - (0x2F4B, 'M', '欠'), - (0x2F4C, 'M', '止'), - (0x2F4D, 'M', '歹'), - (0x2F4E, 'M', '殳'), - (0x2F4F, 'M', '毋'), - (0x2F50, 'M', '比'), - (0x2F51, 'M', '毛'), - (0x2F52, 'M', '氏'), - (0x2F53, 'M', '气'), - (0x2F54, 'M', '水'), - (0x2F55, 'M', '火'), - (0x2F56, 'M', '爪'), - (0x2F57, 'M', '父'), - (0x2F58, 'M', '爻'), - (0x2F59, 'M', '爿'), - (0x2F5A, 'M', '片'), - (0x2F5B, 'M', '牙'), - (0x2F5C, 'M', '牛'), - (0x2F5D, 'M', '犬'), - (0x2F5E, 'M', '玄'), - (0x2F5F, 'M', '玉'), - (0x2F60, 'M', '瓜'), - (0x2F61, 'M', '瓦'), - (0x2F62, 'M', '甘'), - (0x2F63, 'M', '生'), - (0x2F64, 'M', '用'), - (0x2F65, 'M', '田'), - (0x2F66, 'M', '疋'), - (0x2F67, 'M', '疒'), - (0x2F68, 'M', '癶'), - (0x2F69, 'M', '白'), - (0x2F6A, 'M', '皮'), - (0x2F6B, 'M', '皿'), - (0x2F6C, 'M', '目'), - (0x2F6D, 'M', '矛'), - (0x2F6E, 'M', '矢'), - (0x2F6F, 'M', '石'), - (0x2F70, 'M', '示'), - (0x2F71, 'M', '禸'), - (0x2F72, 'M', '禾'), - (0x2F73, 'M', '穴'), - (0x2F74, 'M', '立'), - (0x2F75, 'M', '竹'), - (0x2F76, 'M', '米'), - (0x2F77, 'M', '糸'), - (0x2F78, 'M', '缶'), - (0x2F79, 'M', '网'), - (0x2F7A, 'M', '羊'), - (0x2F7B, 'M', '羽'), - (0x2F7C, 'M', '老'), - (0x2F7D, 'M', '而'), - ] - -def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F7E, 'M', '耒'), - (0x2F7F, 'M', '耳'), - (0x2F80, 'M', '聿'), - (0x2F81, 'M', '肉'), - (0x2F82, 'M', '臣'), - (0x2F83, 'M', '自'), - (0x2F84, 'M', '至'), - (0x2F85, 'M', '臼'), - (0x2F86, 'M', '舌'), - (0x2F87, 'M', '舛'), - (0x2F88, 'M', '舟'), - (0x2F89, 'M', '艮'), - (0x2F8A, 'M', '色'), - (0x2F8B, 'M', '艸'), - (0x2F8C, 'M', '虍'), - (0x2F8D, 'M', '虫'), - (0x2F8E, 'M', '血'), - (0x2F8F, 'M', '行'), - (0x2F90, 'M', '衣'), - (0x2F91, 'M', '襾'), - (0x2F92, 'M', '見'), - (0x2F93, 'M', '角'), - (0x2F94, 'M', '言'), - (0x2F95, 'M', '谷'), - (0x2F96, 'M', '豆'), - (0x2F97, 'M', '豕'), - (0x2F98, 'M', '豸'), - (0x2F99, 'M', '貝'), - (0x2F9A, 'M', '赤'), - (0x2F9B, 'M', '走'), - (0x2F9C, 'M', '足'), - (0x2F9D, 'M', '身'), - (0x2F9E, 'M', '車'), - (0x2F9F, 'M', '辛'), - (0x2FA0, 'M', '辰'), - (0x2FA1, 'M', '辵'), - (0x2FA2, 'M', '邑'), - (0x2FA3, 'M', '酉'), - (0x2FA4, 'M', '釆'), - (0x2FA5, 'M', '里'), - (0x2FA6, 'M', '金'), - (0x2FA7, 'M', '長'), - (0x2FA8, 'M', '門'), - (0x2FA9, 'M', '阜'), - (0x2FAA, 'M', '隶'), - (0x2FAB, 'M', '隹'), - (0x2FAC, 'M', '雨'), - (0x2FAD, 'M', '靑'), - (0x2FAE, 'M', '非'), - (0x2FAF, 'M', '面'), - (0x2FB0, 'M', '革'), - (0x2FB1, 'M', '韋'), - (0x2FB2, 'M', '韭'), - (0x2FB3, 'M', '音'), - (0x2FB4, 'M', '頁'), - (0x2FB5, 'M', '風'), - (0x2FB6, 'M', '飛'), - (0x2FB7, 'M', '食'), - (0x2FB8, 'M', '首'), - (0x2FB9, 'M', '香'), - (0x2FBA, 'M', '馬'), - (0x2FBB, 'M', '骨'), - (0x2FBC, 'M', '高'), - (0x2FBD, 'M', '髟'), - (0x2FBE, 'M', '鬥'), - (0x2FBF, 'M', '鬯'), - (0x2FC0, 'M', '鬲'), - (0x2FC1, 'M', '鬼'), - (0x2FC2, 'M', '魚'), - (0x2FC3, 'M', '鳥'), - (0x2FC4, 'M', '鹵'), - (0x2FC5, 'M', '鹿'), - (0x2FC6, 'M', '麥'), - (0x2FC7, 'M', '麻'), - (0x2FC8, 'M', '黃'), - (0x2FC9, 'M', '黍'), - (0x2FCA, 'M', '黑'), - (0x2FCB, 'M', '黹'), - (0x2FCC, 'M', '黽'), - (0x2FCD, 'M', '鼎'), - (0x2FCE, 'M', '鼓'), - (0x2FCF, 'M', '鼠'), - (0x2FD0, 'M', '鼻'), - (0x2FD1, 'M', '齊'), - (0x2FD2, 'M', '齒'), - (0x2FD3, 'M', '龍'), - (0x2FD4, 'M', '龜'), - (0x2FD5, 'M', '龠'), - (0x2FD6, 'X'), - (0x3000, '3', ' '), - (0x3001, 'V'), - (0x3002, 'M', '.'), - (0x3003, 'V'), - (0x3036, 'M', '〒'), - (0x3037, 'V'), - (0x3038, 'M', '十'), - (0x3039, 'M', '卄'), - (0x303A, 'M', '卅'), - (0x303B, 'V'), - (0x3040, 'X'), - ] - -def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3041, 'V'), - (0x3097, 'X'), - (0x3099, 'V'), - (0x309B, '3', ' ゙'), - (0x309C, '3', ' ゚'), - (0x309D, 'V'), - (0x309F, 'M', 'より'), - (0x30A0, 'V'), - (0x30FF, 'M', 'コト'), - (0x3100, 'X'), - (0x3105, 'V'), - (0x3130, 'X'), - (0x3131, 'M', 'ᄀ'), - (0x3132, 'M', 'ᄁ'), - (0x3133, 'M', 'ᆪ'), - (0x3134, 'M', 'ᄂ'), - (0x3135, 'M', 'ᆬ'), - (0x3136, 'M', 'ᆭ'), - (0x3137, 'M', 'ᄃ'), - (0x3138, 'M', 'ᄄ'), - (0x3139, 'M', 'ᄅ'), - (0x313A, 'M', 'ᆰ'), - (0x313B, 'M', 'ᆱ'), - (0x313C, 'M', 'ᆲ'), - (0x313D, 'M', 'ᆳ'), - (0x313E, 'M', 'ᆴ'), - (0x313F, 'M', 'ᆵ'), - (0x3140, 'M', 'ᄚ'), - (0x3141, 'M', 'ᄆ'), - (0x3142, 'M', 'ᄇ'), - (0x3143, 'M', 'ᄈ'), - (0x3144, 'M', 'ᄡ'), - (0x3145, 'M', 'ᄉ'), - (0x3146, 'M', 'ᄊ'), - (0x3147, 'M', 'ᄋ'), - (0x3148, 'M', 'ᄌ'), - (0x3149, 'M', 'ᄍ'), - (0x314A, 'M', 'ᄎ'), - (0x314B, 'M', 'ᄏ'), - (0x314C, 'M', 'ᄐ'), - (0x314D, 'M', 'ᄑ'), - (0x314E, 'M', 'ᄒ'), - (0x314F, 'M', 'ᅡ'), - (0x3150, 'M', 'ᅢ'), - (0x3151, 'M', 'ᅣ'), - (0x3152, 'M', 'ᅤ'), - (0x3153, 'M', 'ᅥ'), - (0x3154, 'M', 'ᅦ'), - (0x3155, 'M', 'ᅧ'), - (0x3156, 'M', 'ᅨ'), - (0x3157, 'M', 'ᅩ'), - (0x3158, 'M', 'ᅪ'), - (0x3159, 'M', 'ᅫ'), - (0x315A, 'M', 'ᅬ'), - (0x315B, 'M', 'ᅭ'), - (0x315C, 'M', 'ᅮ'), - (0x315D, 'M', 'ᅯ'), - (0x315E, 'M', 'ᅰ'), - (0x315F, 'M', 'ᅱ'), - (0x3160, 'M', 'ᅲ'), - (0x3161, 'M', 'ᅳ'), - (0x3162, 'M', 'ᅴ'), - (0x3163, 'M', 'ᅵ'), - (0x3164, 'X'), - (0x3165, 'M', 'ᄔ'), - (0x3166, 'M', 'ᄕ'), - (0x3167, 'M', 'ᇇ'), - (0x3168, 'M', 'ᇈ'), - (0x3169, 'M', 'ᇌ'), - (0x316A, 'M', 'ᇎ'), - (0x316B, 'M', 'ᇓ'), - (0x316C, 'M', 'ᇗ'), - (0x316D, 'M', 'ᇙ'), - (0x316E, 'M', 'ᄜ'), - (0x316F, 'M', 'ᇝ'), - (0x3170, 'M', 'ᇟ'), - (0x3171, 'M', 'ᄝ'), - (0x3172, 'M', 'ᄞ'), - (0x3173, 'M', 'ᄠ'), - (0x3174, 'M', 'ᄢ'), - (0x3175, 'M', 'ᄣ'), - (0x3176, 'M', 'ᄧ'), - (0x3177, 'M', 'ᄩ'), - (0x3178, 'M', 'ᄫ'), - (0x3179, 'M', 'ᄬ'), - (0x317A, 'M', 'ᄭ'), - (0x317B, 'M', 'ᄮ'), - (0x317C, 'M', 'ᄯ'), - (0x317D, 'M', 'ᄲ'), - (0x317E, 'M', 'ᄶ'), - (0x317F, 'M', 'ᅀ'), - (0x3180, 'M', 'ᅇ'), - (0x3181, 'M', 'ᅌ'), - (0x3182, 'M', 'ᇱ'), - (0x3183, 'M', 'ᇲ'), - (0x3184, 'M', 'ᅗ'), - (0x3185, 'M', 'ᅘ'), - (0x3186, 'M', 'ᅙ'), - (0x3187, 'M', 'ᆄ'), - (0x3188, 'M', 'ᆅ'), - ] - -def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3189, 'M', 'ᆈ'), - (0x318A, 'M', 'ᆑ'), - (0x318B, 'M', 'ᆒ'), - (0x318C, 'M', 'ᆔ'), - (0x318D, 'M', 'ᆞ'), - (0x318E, 'M', 'ᆡ'), - (0x318F, 'X'), - (0x3190, 'V'), - (0x3192, 'M', '一'), - (0x3193, 'M', '二'), - (0x3194, 'M', '三'), - (0x3195, 'M', '四'), - (0x3196, 'M', '上'), - (0x3197, 'M', '中'), - (0x3198, 'M', '下'), - (0x3199, 'M', '甲'), - (0x319A, 'M', '乙'), - (0x319B, 'M', '丙'), - (0x319C, 'M', '丁'), - (0x319D, 'M', '天'), - (0x319E, 'M', '地'), - (0x319F, 'M', '人'), - (0x31A0, 'V'), - (0x31E4, 'X'), - (0x31F0, 'V'), - (0x3200, '3', '(ᄀ)'), - (0x3201, '3', '(ᄂ)'), - (0x3202, '3', '(ᄃ)'), - (0x3203, '3', '(ᄅ)'), - (0x3204, '3', '(ᄆ)'), - (0x3205, '3', '(ᄇ)'), - (0x3206, '3', '(ᄉ)'), - (0x3207, '3', '(ᄋ)'), - (0x3208, '3', '(ᄌ)'), - (0x3209, '3', '(ᄎ)'), - (0x320A, '3', '(ᄏ)'), - (0x320B, '3', '(ᄐ)'), - (0x320C, '3', '(ᄑ)'), - (0x320D, '3', '(ᄒ)'), - (0x320E, '3', '(가)'), - (0x320F, '3', '(나)'), - (0x3210, '3', '(다)'), - (0x3211, '3', '(라)'), - (0x3212, '3', '(마)'), - (0x3213, '3', '(바)'), - (0x3214, '3', '(사)'), - (0x3215, '3', '(아)'), - (0x3216, '3', '(자)'), - (0x3217, '3', '(차)'), - (0x3218, '3', '(카)'), - (0x3219, '3', '(타)'), - (0x321A, '3', '(파)'), - (0x321B, '3', '(하)'), - (0x321C, '3', '(주)'), - (0x321D, '3', '(오전)'), - (0x321E, '3', '(오후)'), - (0x321F, 'X'), - (0x3220, '3', '(一)'), - (0x3221, '3', '(二)'), - (0x3222, '3', '(三)'), - (0x3223, '3', '(四)'), - (0x3224, '3', '(五)'), - (0x3225, '3', '(六)'), - (0x3226, '3', '(七)'), - (0x3227, '3', '(八)'), - (0x3228, '3', '(九)'), - (0x3229, '3', '(十)'), - (0x322A, '3', '(月)'), - (0x322B, '3', '(火)'), - (0x322C, '3', '(水)'), - (0x322D, '3', '(木)'), - (0x322E, '3', '(金)'), - (0x322F, '3', '(土)'), - (0x3230, '3', '(日)'), - (0x3231, '3', '(株)'), - (0x3232, '3', '(有)'), - (0x3233, '3', '(社)'), - (0x3234, '3', '(名)'), - (0x3235, '3', '(特)'), - (0x3236, '3', '(財)'), - (0x3237, '3', '(祝)'), - (0x3238, '3', '(労)'), - (0x3239, '3', '(代)'), - (0x323A, '3', '(呼)'), - (0x323B, '3', '(学)'), - (0x323C, '3', '(監)'), - (0x323D, '3', '(企)'), - (0x323E, '3', '(資)'), - (0x323F, '3', '(協)'), - (0x3240, '3', '(祭)'), - (0x3241, '3', '(休)'), - (0x3242, '3', '(自)'), - (0x3243, '3', '(至)'), - (0x3244, 'M', '問'), - (0x3245, 'M', '幼'), - (0x3246, 'M', '文'), - (0x3247, 'M', '箏'), - (0x3248, 'V'), - (0x3250, 'M', 'pte'), - (0x3251, 'M', '21'), - ] - -def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x3252, 'M', '22'), - (0x3253, 'M', '23'), - (0x3254, 'M', '24'), - (0x3255, 'M', '25'), - (0x3256, 'M', '26'), - (0x3257, 'M', '27'), - (0x3258, 'M', '28'), - (0x3259, 'M', '29'), - (0x325A, 'M', '30'), - (0x325B, 'M', '31'), - (0x325C, 'M', '32'), - (0x325D, 'M', '33'), - (0x325E, 'M', '34'), - (0x325F, 'M', '35'), - (0x3260, 'M', 'ᄀ'), - (0x3261, 'M', 'ᄂ'), - (0x3262, 'M', 'ᄃ'), - (0x3263, 'M', 'ᄅ'), - (0x3264, 'M', 'ᄆ'), - (0x3265, 'M', 'ᄇ'), - (0x3266, 'M', 'ᄉ'), - (0x3267, 'M', 'ᄋ'), - (0x3268, 'M', 'ᄌ'), - (0x3269, 'M', 'ᄎ'), - (0x326A, 'M', 'ᄏ'), - (0x326B, 'M', 'ᄐ'), - (0x326C, 'M', 'ᄑ'), - (0x326D, 'M', 'ᄒ'), - (0x326E, 'M', '가'), - (0x326F, 'M', '나'), - (0x3270, 'M', '다'), - (0x3271, 'M', '라'), - (0x3272, 'M', '마'), - (0x3273, 'M', '바'), - (0x3274, 'M', '사'), - (0x3275, 'M', '아'), - (0x3276, 'M', '자'), - (0x3277, 'M', '차'), - (0x3278, 'M', '카'), - (0x3279, 'M', '타'), - (0x327A, 'M', '파'), - (0x327B, 'M', '하'), - (0x327C, 'M', '참고'), - (0x327D, 'M', '주의'), - (0x327E, 'M', '우'), - (0x327F, 'V'), - (0x3280, 'M', '一'), - (0x3281, 'M', '二'), - (0x3282, 'M', '三'), - (0x3283, 'M', '四'), - (0x3284, 'M', '五'), - (0x3285, 'M', '六'), - (0x3286, 'M', '七'), - (0x3287, 'M', '八'), - (0x3288, 'M', '九'), - (0x3289, 'M', '十'), - (0x328A, 'M', '月'), - (0x328B, 'M', '火'), - (0x328C, 'M', '水'), - (0x328D, 'M', '木'), - (0x328E, 'M', '金'), - (0x328F, 'M', '土'), - (0x3290, 'M', '日'), - (0x3291, 'M', '株'), - (0x3292, 'M', '有'), - (0x3293, 'M', '社'), - (0x3294, 'M', '名'), - (0x3295, 'M', '特'), - (0x3296, 'M', '財'), - (0x3297, 'M', '祝'), - (0x3298, 'M', '労'), - (0x3299, 'M', '秘'), - (0x329A, 'M', '男'), - (0x329B, 'M', '女'), - (0x329C, 'M', '適'), - (0x329D, 'M', '優'), - (0x329E, 'M', '印'), - (0x329F, 'M', '注'), - (0x32A0, 'M', '項'), - (0x32A1, 'M', '休'), - (0x32A2, 'M', '写'), - (0x32A3, 'M', '正'), - (0x32A4, 'M', '上'), - (0x32A5, 'M', '中'), - (0x32A6, 'M', '下'), - (0x32A7, 'M', '左'), - (0x32A8, 'M', '右'), - (0x32A9, 'M', '医'), - (0x32AA, 'M', '宗'), - (0x32AB, 'M', '学'), - (0x32AC, 'M', '監'), - (0x32AD, 'M', '企'), - (0x32AE, 'M', '資'), - (0x32AF, 'M', '協'), - (0x32B0, 'M', '夜'), - (0x32B1, 'M', '36'), - (0x32B2, 'M', '37'), - (0x32B3, 'M', '38'), - (0x32B4, 'M', '39'), - (0x32B5, 'M', '40'), - ] - -def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x32B6, 'M', '41'), - (0x32B7, 'M', '42'), - (0x32B8, 'M', '43'), - (0x32B9, 'M', '44'), - (0x32BA, 'M', '45'), - (0x32BB, 'M', '46'), - (0x32BC, 'M', '47'), - (0x32BD, 'M', '48'), - (0x32BE, 'M', '49'), - (0x32BF, 'M', '50'), - (0x32C0, 'M', '1月'), - (0x32C1, 'M', '2月'), - (0x32C2, 'M', '3月'), - (0x32C3, 'M', '4月'), - (0x32C4, 'M', '5月'), - (0x32C5, 'M', '6月'), - (0x32C6, 'M', '7月'), - (0x32C7, 'M', '8月'), - (0x32C8, 'M', '9月'), - (0x32C9, 'M', '10月'), - (0x32CA, 'M', '11月'), - (0x32CB, 'M', '12月'), - (0x32CC, 'M', 'hg'), - (0x32CD, 'M', 'erg'), - (0x32CE, 'M', 'ev'), - (0x32CF, 'M', 'ltd'), - (0x32D0, 'M', 'ア'), - (0x32D1, 'M', 'イ'), - (0x32D2, 'M', 'ウ'), - (0x32D3, 'M', 'エ'), - (0x32D4, 'M', 'オ'), - (0x32D5, 'M', 'カ'), - (0x32D6, 'M', 'キ'), - (0x32D7, 'M', 'ク'), - (0x32D8, 'M', 'ケ'), - (0x32D9, 'M', 'コ'), - (0x32DA, 'M', 'サ'), - (0x32DB, 'M', 'シ'), - (0x32DC, 'M', 'ス'), - (0x32DD, 'M', 'セ'), - (0x32DE, 'M', 'ソ'), - (0x32DF, 'M', 'タ'), - (0x32E0, 'M', 'チ'), - (0x32E1, 'M', 'ツ'), - (0x32E2, 'M', 'テ'), - (0x32E3, 'M', 'ト'), - (0x32E4, 'M', 'ナ'), - (0x32E5, 'M', 'ニ'), - (0x32E6, 'M', 'ヌ'), - (0x32E7, 'M', 'ネ'), - (0x32E8, 'M', 'ノ'), - (0x32E9, 'M', 'ハ'), - (0x32EA, 'M', 'ヒ'), - (0x32EB, 'M', 'フ'), - (0x32EC, 'M', 'ヘ'), - (0x32ED, 'M', 'ホ'), - (0x32EE, 'M', 'マ'), - (0x32EF, 'M', 'ミ'), - (0x32F0, 'M', 'ム'), - (0x32F1, 'M', 'メ'), - (0x32F2, 'M', 'モ'), - (0x32F3, 'M', 'ヤ'), - (0x32F4, 'M', 'ユ'), - (0x32F5, 'M', 'ヨ'), - (0x32F6, 'M', 'ラ'), - (0x32F7, 'M', 'リ'), - (0x32F8, 'M', 'ル'), - (0x32F9, 'M', 'レ'), - (0x32FA, 'M', 'ロ'), - (0x32FB, 'M', 'ワ'), - (0x32FC, 'M', 'ヰ'), - (0x32FD, 'M', 'ヱ'), - (0x32FE, 'M', 'ヲ'), - (0x32FF, 'M', '令和'), - (0x3300, 'M', 'アパート'), - (0x3301, 'M', 'アルファ'), - (0x3302, 'M', 'アンペア'), - (0x3303, 'M', 'アール'), - (0x3304, 'M', 'イニング'), - (0x3305, 'M', 'インチ'), - (0x3306, 'M', 'ウォン'), - (0x3307, 'M', 'エスクード'), - (0x3308, 'M', 'エーカー'), - (0x3309, 'M', 'オンス'), - (0x330A, 'M', 'オーム'), - (0x330B, 'M', 'カイリ'), - (0x330C, 'M', 'カラット'), - (0x330D, 'M', 'カロリー'), - (0x330E, 'M', 'ガロン'), - (0x330F, 'M', 'ガンマ'), - (0x3310, 'M', 'ギガ'), - (0x3311, 'M', 'ギニー'), - (0x3312, 'M', 'キュリー'), - (0x3313, 'M', 'ギルダー'), - (0x3314, 'M', 'キロ'), - (0x3315, 'M', 'キログラム'), - (0x3316, 'M', 'キロメートル'), - (0x3317, 'M', 'キロワット'), - (0x3318, 'M', 'グラム'), - (0x3319, 'M', 'グラムトン'), - ] - -def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x331A, 'M', 'クルゼイロ'), - (0x331B, 'M', 'クローネ'), - (0x331C, 'M', 'ケース'), - (0x331D, 'M', 'コルナ'), - (0x331E, 'M', 'コーポ'), - (0x331F, 'M', 'サイクル'), - (0x3320, 'M', 'サンチーム'), - (0x3321, 'M', 'シリング'), - (0x3322, 'M', 'センチ'), - (0x3323, 'M', 'セント'), - (0x3324, 'M', 'ダース'), - (0x3325, 'M', 'デシ'), - (0x3326, 'M', 'ドル'), - (0x3327, 'M', 'トン'), - (0x3328, 'M', 'ナノ'), - (0x3329, 'M', 'ノット'), - (0x332A, 'M', 'ハイツ'), - (0x332B, 'M', 'パーセント'), - (0x332C, 'M', 'パーツ'), - (0x332D, 'M', 'バーレル'), - (0x332E, 'M', 'ピアストル'), - (0x332F, 'M', 'ピクル'), - (0x3330, 'M', 'ピコ'), - (0x3331, 'M', 'ビル'), - (0x3332, 'M', 'ファラッド'), - (0x3333, 'M', 'フィート'), - (0x3334, 'M', 'ブッシェル'), - (0x3335, 'M', 'フラン'), - (0x3336, 'M', 'ヘクタール'), - (0x3337, 'M', 'ペソ'), - (0x3338, 'M', 'ペニヒ'), - (0x3339, 'M', 'ヘルツ'), - (0x333A, 'M', 'ペンス'), - (0x333B, 'M', 'ページ'), - (0x333C, 'M', 'ベータ'), - (0x333D, 'M', 'ポイント'), - (0x333E, 'M', 'ボルト'), - (0x333F, 'M', 'ホン'), - (0x3340, 'M', 'ポンド'), - (0x3341, 'M', 'ホール'), - (0x3342, 'M', 'ホーン'), - (0x3343, 'M', 'マイクロ'), - (0x3344, 'M', 'マイル'), - (0x3345, 'M', 'マッハ'), - (0x3346, 'M', 'マルク'), - (0x3347, 'M', 'マンション'), - (0x3348, 'M', 'ミクロン'), - (0x3349, 'M', 'ミリ'), - (0x334A, 'M', 'ミリバール'), - (0x334B, 'M', 'メガ'), - (0x334C, 'M', 'メガトン'), - (0x334D, 'M', 'メートル'), - (0x334E, 'M', 'ヤード'), - (0x334F, 'M', 'ヤール'), - (0x3350, 'M', 'ユアン'), - (0x3351, 'M', 'リットル'), - (0x3352, 'M', 'リラ'), - (0x3353, 'M', 'ルピー'), - (0x3354, 'M', 'ルーブル'), - (0x3355, 'M', 'レム'), - (0x3356, 'M', 'レントゲン'), - (0x3357, 'M', 'ワット'), - (0x3358, 'M', '0点'), - (0x3359, 'M', '1点'), - (0x335A, 'M', '2点'), - (0x335B, 'M', '3点'), - (0x335C, 'M', '4点'), - (0x335D, 'M', '5点'), - (0x335E, 'M', '6点'), - (0x335F, 'M', '7点'), - (0x3360, 'M', '8点'), - (0x3361, 'M', '9点'), - (0x3362, 'M', '10点'), - (0x3363, 'M', '11点'), - (0x3364, 'M', '12点'), - (0x3365, 'M', '13点'), - (0x3366, 'M', '14点'), - (0x3367, 'M', '15点'), - (0x3368, 'M', '16点'), - (0x3369, 'M', '17点'), - (0x336A, 'M', '18点'), - (0x336B, 'M', '19点'), - (0x336C, 'M', '20点'), - (0x336D, 'M', '21点'), - (0x336E, 'M', '22点'), - (0x336F, 'M', '23点'), - (0x3370, 'M', '24点'), - (0x3371, 'M', 'hpa'), - (0x3372, 'M', 'da'), - (0x3373, 'M', 'au'), - (0x3374, 'M', 'bar'), - (0x3375, 'M', 'ov'), - (0x3376, 'M', 'pc'), - (0x3377, 'M', 'dm'), - (0x3378, 'M', 'dm2'), - (0x3379, 'M', 'dm3'), - (0x337A, 'M', 'iu'), - (0x337B, 'M', '平成'), - (0x337C, 'M', '昭和'), - (0x337D, 'M', '大正'), - ] - -def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x337E, 'M', '明治'), - (0x337F, 'M', '株式会社'), - (0x3380, 'M', 'pa'), - (0x3381, 'M', 'na'), - (0x3382, 'M', 'μa'), - (0x3383, 'M', 'ma'), - (0x3384, 'M', 'ka'), - (0x3385, 'M', 'kb'), - (0x3386, 'M', 'mb'), - (0x3387, 'M', 'gb'), - (0x3388, 'M', 'cal'), - (0x3389, 'M', 'kcal'), - (0x338A, 'M', 'pf'), - (0x338B, 'M', 'nf'), - (0x338C, 'M', 'μf'), - (0x338D, 'M', 'μg'), - (0x338E, 'M', 'mg'), - (0x338F, 'M', 'kg'), - (0x3390, 'M', 'hz'), - (0x3391, 'M', 'khz'), - (0x3392, 'M', 'mhz'), - (0x3393, 'M', 'ghz'), - (0x3394, 'M', 'thz'), - (0x3395, 'M', 'μl'), - (0x3396, 'M', 'ml'), - (0x3397, 'M', 'dl'), - (0x3398, 'M', 'kl'), - (0x3399, 'M', 'fm'), - (0x339A, 'M', 'nm'), - (0x339B, 'M', 'μm'), - (0x339C, 'M', 'mm'), - (0x339D, 'M', 'cm'), - (0x339E, 'M', 'km'), - (0x339F, 'M', 'mm2'), - (0x33A0, 'M', 'cm2'), - (0x33A1, 'M', 'm2'), - (0x33A2, 'M', 'km2'), - (0x33A3, 'M', 'mm3'), - (0x33A4, 'M', 'cm3'), - (0x33A5, 'M', 'm3'), - (0x33A6, 'M', 'km3'), - (0x33A7, 'M', 'm∕s'), - (0x33A8, 'M', 'm∕s2'), - (0x33A9, 'M', 'pa'), - (0x33AA, 'M', 'kpa'), - (0x33AB, 'M', 'mpa'), - (0x33AC, 'M', 'gpa'), - (0x33AD, 'M', 'rad'), - (0x33AE, 'M', 'rad∕s'), - (0x33AF, 'M', 'rad∕s2'), - (0x33B0, 'M', 'ps'), - (0x33B1, 'M', 'ns'), - (0x33B2, 'M', 'μs'), - (0x33B3, 'M', 'ms'), - (0x33B4, 'M', 'pv'), - (0x33B5, 'M', 'nv'), - (0x33B6, 'M', 'μv'), - (0x33B7, 'M', 'mv'), - (0x33B8, 'M', 'kv'), - (0x33B9, 'M', 'mv'), - (0x33BA, 'M', 'pw'), - (0x33BB, 'M', 'nw'), - (0x33BC, 'M', 'μw'), - (0x33BD, 'M', 'mw'), - (0x33BE, 'M', 'kw'), - (0x33BF, 'M', 'mw'), - (0x33C0, 'M', 'kω'), - (0x33C1, 'M', 'mω'), - (0x33C2, 'X'), - (0x33C3, 'M', 'bq'), - (0x33C4, 'M', 'cc'), - (0x33C5, 'M', 'cd'), - (0x33C6, 'M', 'c∕kg'), - (0x33C7, 'X'), - (0x33C8, 'M', 'db'), - (0x33C9, 'M', 'gy'), - (0x33CA, 'M', 'ha'), - (0x33CB, 'M', 'hp'), - (0x33CC, 'M', 'in'), - (0x33CD, 'M', 'kk'), - (0x33CE, 'M', 'km'), - (0x33CF, 'M', 'kt'), - (0x33D0, 'M', 'lm'), - (0x33D1, 'M', 'ln'), - (0x33D2, 'M', 'log'), - (0x33D3, 'M', 'lx'), - (0x33D4, 'M', 'mb'), - (0x33D5, 'M', 'mil'), - (0x33D6, 'M', 'mol'), - (0x33D7, 'M', 'ph'), - (0x33D8, 'X'), - (0x33D9, 'M', 'ppm'), - (0x33DA, 'M', 'pr'), - (0x33DB, 'M', 'sr'), - (0x33DC, 'M', 'sv'), - (0x33DD, 'M', 'wb'), - (0x33DE, 'M', 'v∕m'), - (0x33DF, 'M', 'a∕m'), - (0x33E0, 'M', '1日'), - (0x33E1, 'M', '2日'), - ] - -def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x33E2, 'M', '3日'), - (0x33E3, 'M', '4日'), - (0x33E4, 'M', '5日'), - (0x33E5, 'M', '6日'), - (0x33E6, 'M', '7日'), - (0x33E7, 'M', '8日'), - (0x33E8, 'M', '9日'), - (0x33E9, 'M', '10日'), - (0x33EA, 'M', '11日'), - (0x33EB, 'M', '12日'), - (0x33EC, 'M', '13日'), - (0x33ED, 'M', '14日'), - (0x33EE, 'M', '15日'), - (0x33EF, 'M', '16日'), - (0x33F0, 'M', '17日'), - (0x33F1, 'M', '18日'), - (0x33F2, 'M', '19日'), - (0x33F3, 'M', '20日'), - (0x33F4, 'M', '21日'), - (0x33F5, 'M', '22日'), - (0x33F6, 'M', '23日'), - (0x33F7, 'M', '24日'), - (0x33F8, 'M', '25日'), - (0x33F9, 'M', '26日'), - (0x33FA, 'M', '27日'), - (0x33FB, 'M', '28日'), - (0x33FC, 'M', '29日'), - (0x33FD, 'M', '30日'), - (0x33FE, 'M', '31日'), - (0x33FF, 'M', 'gal'), - (0x3400, 'V'), - (0xA48D, 'X'), - (0xA490, 'V'), - (0xA4C7, 'X'), - (0xA4D0, 'V'), - (0xA62C, 'X'), - (0xA640, 'M', 'ꙁ'), - (0xA641, 'V'), - (0xA642, 'M', 'ꙃ'), - (0xA643, 'V'), - (0xA644, 'M', 'ꙅ'), - (0xA645, 'V'), - (0xA646, 'M', 'ꙇ'), - (0xA647, 'V'), - (0xA648, 'M', 'ꙉ'), - (0xA649, 'V'), - (0xA64A, 'M', 'ꙋ'), - (0xA64B, 'V'), - (0xA64C, 'M', 'ꙍ'), - (0xA64D, 'V'), - (0xA64E, 'M', 'ꙏ'), - (0xA64F, 'V'), - (0xA650, 'M', 'ꙑ'), - (0xA651, 'V'), - (0xA652, 'M', 'ꙓ'), - (0xA653, 'V'), - (0xA654, 'M', 'ꙕ'), - (0xA655, 'V'), - (0xA656, 'M', 'ꙗ'), - (0xA657, 'V'), - (0xA658, 'M', 'ꙙ'), - (0xA659, 'V'), - (0xA65A, 'M', 'ꙛ'), - (0xA65B, 'V'), - (0xA65C, 'M', 'ꙝ'), - (0xA65D, 'V'), - (0xA65E, 'M', 'ꙟ'), - (0xA65F, 'V'), - (0xA660, 'M', 'ꙡ'), - (0xA661, 'V'), - (0xA662, 'M', 'ꙣ'), - (0xA663, 'V'), - (0xA664, 'M', 'ꙥ'), - (0xA665, 'V'), - (0xA666, 'M', 'ꙧ'), - (0xA667, 'V'), - (0xA668, 'M', 'ꙩ'), - (0xA669, 'V'), - (0xA66A, 'M', 'ꙫ'), - (0xA66B, 'V'), - (0xA66C, 'M', 'ꙭ'), - (0xA66D, 'V'), - (0xA680, 'M', 'ꚁ'), - (0xA681, 'V'), - (0xA682, 'M', 'ꚃ'), - (0xA683, 'V'), - (0xA684, 'M', 'ꚅ'), - (0xA685, 'V'), - (0xA686, 'M', 'ꚇ'), - (0xA687, 'V'), - (0xA688, 'M', 'ꚉ'), - (0xA689, 'V'), - (0xA68A, 'M', 'ꚋ'), - (0xA68B, 'V'), - (0xA68C, 'M', 'ꚍ'), - (0xA68D, 'V'), - (0xA68E, 'M', 'ꚏ'), - (0xA68F, 'V'), - (0xA690, 'M', 'ꚑ'), - (0xA691, 'V'), - ] - -def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA692, 'M', 'ꚓ'), - (0xA693, 'V'), - (0xA694, 'M', 'ꚕ'), - (0xA695, 'V'), - (0xA696, 'M', 'ꚗ'), - (0xA697, 'V'), - (0xA698, 'M', 'ꚙ'), - (0xA699, 'V'), - (0xA69A, 'M', 'ꚛ'), - (0xA69B, 'V'), - (0xA69C, 'M', 'ъ'), - (0xA69D, 'M', 'ь'), - (0xA69E, 'V'), - (0xA6F8, 'X'), - (0xA700, 'V'), - (0xA722, 'M', 'ꜣ'), - (0xA723, 'V'), - (0xA724, 'M', 'ꜥ'), - (0xA725, 'V'), - (0xA726, 'M', 'ꜧ'), - (0xA727, 'V'), - (0xA728, 'M', 'ꜩ'), - (0xA729, 'V'), - (0xA72A, 'M', 'ꜫ'), - (0xA72B, 'V'), - (0xA72C, 'M', 'ꜭ'), - (0xA72D, 'V'), - (0xA72E, 'M', 'ꜯ'), - (0xA72F, 'V'), - (0xA732, 'M', 'ꜳ'), - (0xA733, 'V'), - (0xA734, 'M', 'ꜵ'), - (0xA735, 'V'), - (0xA736, 'M', 'ꜷ'), - (0xA737, 'V'), - (0xA738, 'M', 'ꜹ'), - (0xA739, 'V'), - (0xA73A, 'M', 'ꜻ'), - (0xA73B, 'V'), - (0xA73C, 'M', 'ꜽ'), - (0xA73D, 'V'), - (0xA73E, 'M', 'ꜿ'), - (0xA73F, 'V'), - (0xA740, 'M', 'ꝁ'), - (0xA741, 'V'), - (0xA742, 'M', 'ꝃ'), - (0xA743, 'V'), - (0xA744, 'M', 'ꝅ'), - (0xA745, 'V'), - (0xA746, 'M', 'ꝇ'), - (0xA747, 'V'), - (0xA748, 'M', 'ꝉ'), - (0xA749, 'V'), - (0xA74A, 'M', 'ꝋ'), - (0xA74B, 'V'), - (0xA74C, 'M', 'ꝍ'), - (0xA74D, 'V'), - (0xA74E, 'M', 'ꝏ'), - (0xA74F, 'V'), - (0xA750, 'M', 'ꝑ'), - (0xA751, 'V'), - (0xA752, 'M', 'ꝓ'), - (0xA753, 'V'), - (0xA754, 'M', 'ꝕ'), - (0xA755, 'V'), - (0xA756, 'M', 'ꝗ'), - (0xA757, 'V'), - (0xA758, 'M', 'ꝙ'), - (0xA759, 'V'), - (0xA75A, 'M', 'ꝛ'), - (0xA75B, 'V'), - (0xA75C, 'M', 'ꝝ'), - (0xA75D, 'V'), - (0xA75E, 'M', 'ꝟ'), - (0xA75F, 'V'), - (0xA760, 'M', 'ꝡ'), - (0xA761, 'V'), - (0xA762, 'M', 'ꝣ'), - (0xA763, 'V'), - (0xA764, 'M', 'ꝥ'), - (0xA765, 'V'), - (0xA766, 'M', 'ꝧ'), - (0xA767, 'V'), - (0xA768, 'M', 'ꝩ'), - (0xA769, 'V'), - (0xA76A, 'M', 'ꝫ'), - (0xA76B, 'V'), - (0xA76C, 'M', 'ꝭ'), - (0xA76D, 'V'), - (0xA76E, 'M', 'ꝯ'), - (0xA76F, 'V'), - (0xA770, 'M', 'ꝯ'), - (0xA771, 'V'), - (0xA779, 'M', 'ꝺ'), - (0xA77A, 'V'), - (0xA77B, 'M', 'ꝼ'), - (0xA77C, 'V'), - (0xA77D, 'M', 'ᵹ'), - (0xA77E, 'M', 'ꝿ'), - (0xA77F, 'V'), - ] - -def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA780, 'M', 'ꞁ'), - (0xA781, 'V'), - (0xA782, 'M', 'ꞃ'), - (0xA783, 'V'), - (0xA784, 'M', 'ꞅ'), - (0xA785, 'V'), - (0xA786, 'M', 'ꞇ'), - (0xA787, 'V'), - (0xA78B, 'M', 'ꞌ'), - (0xA78C, 'V'), - (0xA78D, 'M', 'ɥ'), - (0xA78E, 'V'), - (0xA790, 'M', 'ꞑ'), - (0xA791, 'V'), - (0xA792, 'M', 'ꞓ'), - (0xA793, 'V'), - (0xA796, 'M', 'ꞗ'), - (0xA797, 'V'), - (0xA798, 'M', 'ꞙ'), - (0xA799, 'V'), - (0xA79A, 'M', 'ꞛ'), - (0xA79B, 'V'), - (0xA79C, 'M', 'ꞝ'), - (0xA79D, 'V'), - (0xA79E, 'M', 'ꞟ'), - (0xA79F, 'V'), - (0xA7A0, 'M', 'ꞡ'), - (0xA7A1, 'V'), - (0xA7A2, 'M', 'ꞣ'), - (0xA7A3, 'V'), - (0xA7A4, 'M', 'ꞥ'), - (0xA7A5, 'V'), - (0xA7A6, 'M', 'ꞧ'), - (0xA7A7, 'V'), - (0xA7A8, 'M', 'ꞩ'), - (0xA7A9, 'V'), - (0xA7AA, 'M', 'ɦ'), - (0xA7AB, 'M', 'ɜ'), - (0xA7AC, 'M', 'ɡ'), - (0xA7AD, 'M', 'ɬ'), - (0xA7AE, 'M', 'ɪ'), - (0xA7AF, 'V'), - (0xA7B0, 'M', 'ʞ'), - (0xA7B1, 'M', 'ʇ'), - (0xA7B2, 'M', 'ʝ'), - (0xA7B3, 'M', 'ꭓ'), - (0xA7B4, 'M', 'ꞵ'), - (0xA7B5, 'V'), - (0xA7B6, 'M', 'ꞷ'), - (0xA7B7, 'V'), - (0xA7B8, 'M', 'ꞹ'), - (0xA7B9, 'V'), - (0xA7BA, 'M', 'ꞻ'), - (0xA7BB, 'V'), - (0xA7BC, 'M', 'ꞽ'), - (0xA7BD, 'V'), - (0xA7BE, 'M', 'ꞿ'), - (0xA7BF, 'V'), - (0xA7C0, 'M', 'ꟁ'), - (0xA7C1, 'V'), - (0xA7C2, 'M', 'ꟃ'), - (0xA7C3, 'V'), - (0xA7C4, 'M', 'ꞔ'), - (0xA7C5, 'M', 'ʂ'), - (0xA7C6, 'M', 'ᶎ'), - (0xA7C7, 'M', 'ꟈ'), - (0xA7C8, 'V'), - (0xA7C9, 'M', 'ꟊ'), - (0xA7CA, 'V'), - (0xA7CB, 'X'), - (0xA7D0, 'M', 'ꟑ'), - (0xA7D1, 'V'), - (0xA7D2, 'X'), - (0xA7D3, 'V'), - (0xA7D4, 'X'), - (0xA7D5, 'V'), - (0xA7D6, 'M', 'ꟗ'), - (0xA7D7, 'V'), - (0xA7D8, 'M', 'ꟙ'), - (0xA7D9, 'V'), - (0xA7DA, 'X'), - (0xA7F2, 'M', 'c'), - (0xA7F3, 'M', 'f'), - (0xA7F4, 'M', 'q'), - (0xA7F5, 'M', 'ꟶ'), - (0xA7F6, 'V'), - (0xA7F8, 'M', 'ħ'), - (0xA7F9, 'M', 'œ'), - (0xA7FA, 'V'), - (0xA82D, 'X'), - (0xA830, 'V'), - (0xA83A, 'X'), - (0xA840, 'V'), - (0xA878, 'X'), - (0xA880, 'V'), - (0xA8C6, 'X'), - (0xA8CE, 'V'), - (0xA8DA, 'X'), - (0xA8E0, 'V'), - (0xA954, 'X'), - ] - -def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xA95F, 'V'), - (0xA97D, 'X'), - (0xA980, 'V'), - (0xA9CE, 'X'), - (0xA9CF, 'V'), - (0xA9DA, 'X'), - (0xA9DE, 'V'), - (0xA9FF, 'X'), - (0xAA00, 'V'), - (0xAA37, 'X'), - (0xAA40, 'V'), - (0xAA4E, 'X'), - (0xAA50, 'V'), - (0xAA5A, 'X'), - (0xAA5C, 'V'), - (0xAAC3, 'X'), - (0xAADB, 'V'), - (0xAAF7, 'X'), - (0xAB01, 'V'), - (0xAB07, 'X'), - (0xAB09, 'V'), - (0xAB0F, 'X'), - (0xAB11, 'V'), - (0xAB17, 'X'), - (0xAB20, 'V'), - (0xAB27, 'X'), - (0xAB28, 'V'), - (0xAB2F, 'X'), - (0xAB30, 'V'), - (0xAB5C, 'M', 'ꜧ'), - (0xAB5D, 'M', 'ꬷ'), - (0xAB5E, 'M', 'ɫ'), - (0xAB5F, 'M', 'ꭒ'), - (0xAB60, 'V'), - (0xAB69, 'M', 'ʍ'), - (0xAB6A, 'V'), - (0xAB6C, 'X'), - (0xAB70, 'M', 'Ꭰ'), - (0xAB71, 'M', 'Ꭱ'), - (0xAB72, 'M', 'Ꭲ'), - (0xAB73, 'M', 'Ꭳ'), - (0xAB74, 'M', 'Ꭴ'), - (0xAB75, 'M', 'Ꭵ'), - (0xAB76, 'M', 'Ꭶ'), - (0xAB77, 'M', 'Ꭷ'), - (0xAB78, 'M', 'Ꭸ'), - (0xAB79, 'M', 'Ꭹ'), - (0xAB7A, 'M', 'Ꭺ'), - (0xAB7B, 'M', 'Ꭻ'), - (0xAB7C, 'M', 'Ꭼ'), - (0xAB7D, 'M', 'Ꭽ'), - (0xAB7E, 'M', 'Ꭾ'), - (0xAB7F, 'M', 'Ꭿ'), - (0xAB80, 'M', 'Ꮀ'), - (0xAB81, 'M', 'Ꮁ'), - (0xAB82, 'M', 'Ꮂ'), - (0xAB83, 'M', 'Ꮃ'), - (0xAB84, 'M', 'Ꮄ'), - (0xAB85, 'M', 'Ꮅ'), - (0xAB86, 'M', 'Ꮆ'), - (0xAB87, 'M', 'Ꮇ'), - (0xAB88, 'M', 'Ꮈ'), - (0xAB89, 'M', 'Ꮉ'), - (0xAB8A, 'M', 'Ꮊ'), - (0xAB8B, 'M', 'Ꮋ'), - (0xAB8C, 'M', 'Ꮌ'), - (0xAB8D, 'M', 'Ꮍ'), - (0xAB8E, 'M', 'Ꮎ'), - (0xAB8F, 'M', 'Ꮏ'), - (0xAB90, 'M', 'Ꮐ'), - (0xAB91, 'M', 'Ꮑ'), - (0xAB92, 'M', 'Ꮒ'), - (0xAB93, 'M', 'Ꮓ'), - (0xAB94, 'M', 'Ꮔ'), - (0xAB95, 'M', 'Ꮕ'), - (0xAB96, 'M', 'Ꮖ'), - (0xAB97, 'M', 'Ꮗ'), - (0xAB98, 'M', 'Ꮘ'), - (0xAB99, 'M', 'Ꮙ'), - (0xAB9A, 'M', 'Ꮚ'), - (0xAB9B, 'M', 'Ꮛ'), - (0xAB9C, 'M', 'Ꮜ'), - (0xAB9D, 'M', 'Ꮝ'), - (0xAB9E, 'M', 'Ꮞ'), - (0xAB9F, 'M', 'Ꮟ'), - (0xABA0, 'M', 'Ꮠ'), - (0xABA1, 'M', 'Ꮡ'), - (0xABA2, 'M', 'Ꮢ'), - (0xABA3, 'M', 'Ꮣ'), - (0xABA4, 'M', 'Ꮤ'), - (0xABA5, 'M', 'Ꮥ'), - (0xABA6, 'M', 'Ꮦ'), - (0xABA7, 'M', 'Ꮧ'), - (0xABA8, 'M', 'Ꮨ'), - (0xABA9, 'M', 'Ꮩ'), - (0xABAA, 'M', 'Ꮪ'), - (0xABAB, 'M', 'Ꮫ'), - (0xABAC, 'M', 'Ꮬ'), - (0xABAD, 'M', 'Ꮭ'), - (0xABAE, 'M', 'Ꮮ'), - ] - -def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xABAF, 'M', 'Ꮯ'), - (0xABB0, 'M', 'Ꮰ'), - (0xABB1, 'M', 'Ꮱ'), - (0xABB2, 'M', 'Ꮲ'), - (0xABB3, 'M', 'Ꮳ'), - (0xABB4, 'M', 'Ꮴ'), - (0xABB5, 'M', 'Ꮵ'), - (0xABB6, 'M', 'Ꮶ'), - (0xABB7, 'M', 'Ꮷ'), - (0xABB8, 'M', 'Ꮸ'), - (0xABB9, 'M', 'Ꮹ'), - (0xABBA, 'M', 'Ꮺ'), - (0xABBB, 'M', 'Ꮻ'), - (0xABBC, 'M', 'Ꮼ'), - (0xABBD, 'M', 'Ꮽ'), - (0xABBE, 'M', 'Ꮾ'), - (0xABBF, 'M', 'Ꮿ'), - (0xABC0, 'V'), - (0xABEE, 'X'), - (0xABF0, 'V'), - (0xABFA, 'X'), - (0xAC00, 'V'), - (0xD7A4, 'X'), - (0xD7B0, 'V'), - (0xD7C7, 'X'), - (0xD7CB, 'V'), - (0xD7FC, 'X'), - (0xF900, 'M', '豈'), - (0xF901, 'M', '更'), - (0xF902, 'M', '車'), - (0xF903, 'M', '賈'), - (0xF904, 'M', '滑'), - (0xF905, 'M', '串'), - (0xF906, 'M', '句'), - (0xF907, 'M', '龜'), - (0xF909, 'M', '契'), - (0xF90A, 'M', '金'), - (0xF90B, 'M', '喇'), - (0xF90C, 'M', '奈'), - (0xF90D, 'M', '懶'), - (0xF90E, 'M', '癩'), - (0xF90F, 'M', '羅'), - (0xF910, 'M', '蘿'), - (0xF911, 'M', '螺'), - (0xF912, 'M', '裸'), - (0xF913, 'M', '邏'), - (0xF914, 'M', '樂'), - (0xF915, 'M', '洛'), - (0xF916, 'M', '烙'), - (0xF917, 'M', '珞'), - (0xF918, 'M', '落'), - (0xF919, 'M', '酪'), - (0xF91A, 'M', '駱'), - (0xF91B, 'M', '亂'), - (0xF91C, 'M', '卵'), - (0xF91D, 'M', '欄'), - (0xF91E, 'M', '爛'), - (0xF91F, 'M', '蘭'), - (0xF920, 'M', '鸞'), - (0xF921, 'M', '嵐'), - (0xF922, 'M', '濫'), - (0xF923, 'M', '藍'), - (0xF924, 'M', '襤'), - (0xF925, 'M', '拉'), - (0xF926, 'M', '臘'), - (0xF927, 'M', '蠟'), - (0xF928, 'M', '廊'), - (0xF929, 'M', '朗'), - (0xF92A, 'M', '浪'), - (0xF92B, 'M', '狼'), - (0xF92C, 'M', '郎'), - (0xF92D, 'M', '來'), - (0xF92E, 'M', '冷'), - (0xF92F, 'M', '勞'), - (0xF930, 'M', '擄'), - (0xF931, 'M', '櫓'), - (0xF932, 'M', '爐'), - (0xF933, 'M', '盧'), - (0xF934, 'M', '老'), - (0xF935, 'M', '蘆'), - (0xF936, 'M', '虜'), - (0xF937, 'M', '路'), - (0xF938, 'M', '露'), - (0xF939, 'M', '魯'), - (0xF93A, 'M', '鷺'), - (0xF93B, 'M', '碌'), - (0xF93C, 'M', '祿'), - (0xF93D, 'M', '綠'), - (0xF93E, 'M', '菉'), - (0xF93F, 'M', '錄'), - (0xF940, 'M', '鹿'), - (0xF941, 'M', '論'), - (0xF942, 'M', '壟'), - (0xF943, 'M', '弄'), - (0xF944, 'M', '籠'), - (0xF945, 'M', '聾'), - (0xF946, 'M', '牢'), - (0xF947, 'M', '磊'), - (0xF948, 'M', '賂'), - (0xF949, 'M', '雷'), - ] - -def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF94A, 'M', '壘'), - (0xF94B, 'M', '屢'), - (0xF94C, 'M', '樓'), - (0xF94D, 'M', '淚'), - (0xF94E, 'M', '漏'), - (0xF94F, 'M', '累'), - (0xF950, 'M', '縷'), - (0xF951, 'M', '陋'), - (0xF952, 'M', '勒'), - (0xF953, 'M', '肋'), - (0xF954, 'M', '凜'), - (0xF955, 'M', '凌'), - (0xF956, 'M', '稜'), - (0xF957, 'M', '綾'), - (0xF958, 'M', '菱'), - (0xF959, 'M', '陵'), - (0xF95A, 'M', '讀'), - (0xF95B, 'M', '拏'), - (0xF95C, 'M', '樂'), - (0xF95D, 'M', '諾'), - (0xF95E, 'M', '丹'), - (0xF95F, 'M', '寧'), - (0xF960, 'M', '怒'), - (0xF961, 'M', '率'), - (0xF962, 'M', '異'), - (0xF963, 'M', '北'), - (0xF964, 'M', '磻'), - (0xF965, 'M', '便'), - (0xF966, 'M', '復'), - (0xF967, 'M', '不'), - (0xF968, 'M', '泌'), - (0xF969, 'M', '數'), - (0xF96A, 'M', '索'), - (0xF96B, 'M', '參'), - (0xF96C, 'M', '塞'), - (0xF96D, 'M', '省'), - (0xF96E, 'M', '葉'), - (0xF96F, 'M', '說'), - (0xF970, 'M', '殺'), - (0xF971, 'M', '辰'), - (0xF972, 'M', '沈'), - (0xF973, 'M', '拾'), - (0xF974, 'M', '若'), - (0xF975, 'M', '掠'), - (0xF976, 'M', '略'), - (0xF977, 'M', '亮'), - (0xF978, 'M', '兩'), - (0xF979, 'M', '凉'), - (0xF97A, 'M', '梁'), - (0xF97B, 'M', '糧'), - (0xF97C, 'M', '良'), - (0xF97D, 'M', '諒'), - (0xF97E, 'M', '量'), - (0xF97F, 'M', '勵'), - (0xF980, 'M', '呂'), - (0xF981, 'M', '女'), - (0xF982, 'M', '廬'), - (0xF983, 'M', '旅'), - (0xF984, 'M', '濾'), - (0xF985, 'M', '礪'), - (0xF986, 'M', '閭'), - (0xF987, 'M', '驪'), - (0xF988, 'M', '麗'), - (0xF989, 'M', '黎'), - (0xF98A, 'M', '力'), - (0xF98B, 'M', '曆'), - (0xF98C, 'M', '歷'), - (0xF98D, 'M', '轢'), - (0xF98E, 'M', '年'), - (0xF98F, 'M', '憐'), - (0xF990, 'M', '戀'), - (0xF991, 'M', '撚'), - (0xF992, 'M', '漣'), - (0xF993, 'M', '煉'), - (0xF994, 'M', '璉'), - (0xF995, 'M', '秊'), - (0xF996, 'M', '練'), - (0xF997, 'M', '聯'), - (0xF998, 'M', '輦'), - (0xF999, 'M', '蓮'), - (0xF99A, 'M', '連'), - (0xF99B, 'M', '鍊'), - (0xF99C, 'M', '列'), - (0xF99D, 'M', '劣'), - (0xF99E, 'M', '咽'), - (0xF99F, 'M', '烈'), - (0xF9A0, 'M', '裂'), - (0xF9A1, 'M', '說'), - (0xF9A2, 'M', '廉'), - (0xF9A3, 'M', '念'), - (0xF9A4, 'M', '捻'), - (0xF9A5, 'M', '殮'), - (0xF9A6, 'M', '簾'), - (0xF9A7, 'M', '獵'), - (0xF9A8, 'M', '令'), - (0xF9A9, 'M', '囹'), - (0xF9AA, 'M', '寧'), - (0xF9AB, 'M', '嶺'), - (0xF9AC, 'M', '怜'), - (0xF9AD, 'M', '玲'), - ] - -def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xF9AE, 'M', '瑩'), - (0xF9AF, 'M', '羚'), - (0xF9B0, 'M', '聆'), - (0xF9B1, 'M', '鈴'), - (0xF9B2, 'M', '零'), - (0xF9B3, 'M', '靈'), - (0xF9B4, 'M', '領'), - (0xF9B5, 'M', '例'), - (0xF9B6, 'M', '禮'), - (0xF9B7, 'M', '醴'), - (0xF9B8, 'M', '隸'), - (0xF9B9, 'M', '惡'), - (0xF9BA, 'M', '了'), - (0xF9BB, 'M', '僚'), - (0xF9BC, 'M', '寮'), - (0xF9BD, 'M', '尿'), - (0xF9BE, 'M', '料'), - (0xF9BF, 'M', '樂'), - (0xF9C0, 'M', '燎'), - (0xF9C1, 'M', '療'), - (0xF9C2, 'M', '蓼'), - (0xF9C3, 'M', '遼'), - (0xF9C4, 'M', '龍'), - (0xF9C5, 'M', '暈'), - (0xF9C6, 'M', '阮'), - (0xF9C7, 'M', '劉'), - (0xF9C8, 'M', '杻'), - (0xF9C9, 'M', '柳'), - (0xF9CA, 'M', '流'), - (0xF9CB, 'M', '溜'), - (0xF9CC, 'M', '琉'), - (0xF9CD, 'M', '留'), - (0xF9CE, 'M', '硫'), - (0xF9CF, 'M', '紐'), - (0xF9D0, 'M', '類'), - (0xF9D1, 'M', '六'), - (0xF9D2, 'M', '戮'), - (0xF9D3, 'M', '陸'), - (0xF9D4, 'M', '倫'), - (0xF9D5, 'M', '崙'), - (0xF9D6, 'M', '淪'), - (0xF9D7, 'M', '輪'), - (0xF9D8, 'M', '律'), - (0xF9D9, 'M', '慄'), - (0xF9DA, 'M', '栗'), - (0xF9DB, 'M', '率'), - (0xF9DC, 'M', '隆'), - (0xF9DD, 'M', '利'), - (0xF9DE, 'M', '吏'), - (0xF9DF, 'M', '履'), - (0xF9E0, 'M', '易'), - (0xF9E1, 'M', '李'), - (0xF9E2, 'M', '梨'), - (0xF9E3, 'M', '泥'), - (0xF9E4, 'M', '理'), - (0xF9E5, 'M', '痢'), - (0xF9E6, 'M', '罹'), - (0xF9E7, 'M', '裏'), - (0xF9E8, 'M', '裡'), - (0xF9E9, 'M', '里'), - (0xF9EA, 'M', '離'), - (0xF9EB, 'M', '匿'), - (0xF9EC, 'M', '溺'), - (0xF9ED, 'M', '吝'), - (0xF9EE, 'M', '燐'), - (0xF9EF, 'M', '璘'), - (0xF9F0, 'M', '藺'), - (0xF9F1, 'M', '隣'), - (0xF9F2, 'M', '鱗'), - (0xF9F3, 'M', '麟'), - (0xF9F4, 'M', '林'), - (0xF9F5, 'M', '淋'), - (0xF9F6, 'M', '臨'), - (0xF9F7, 'M', '立'), - (0xF9F8, 'M', '笠'), - (0xF9F9, 'M', '粒'), - (0xF9FA, 'M', '狀'), - (0xF9FB, 'M', '炙'), - (0xF9FC, 'M', '識'), - (0xF9FD, 'M', '什'), - (0xF9FE, 'M', '茶'), - (0xF9FF, 'M', '刺'), - (0xFA00, 'M', '切'), - (0xFA01, 'M', '度'), - (0xFA02, 'M', '拓'), - (0xFA03, 'M', '糖'), - (0xFA04, 'M', '宅'), - (0xFA05, 'M', '洞'), - (0xFA06, 'M', '暴'), - (0xFA07, 'M', '輻'), - (0xFA08, 'M', '行'), - (0xFA09, 'M', '降'), - (0xFA0A, 'M', '見'), - (0xFA0B, 'M', '廓'), - (0xFA0C, 'M', '兀'), - (0xFA0D, 'M', '嗀'), - (0xFA0E, 'V'), - (0xFA10, 'M', '塚'), - (0xFA11, 'V'), - (0xFA12, 'M', '晴'), - ] - -def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA13, 'V'), - (0xFA15, 'M', '凞'), - (0xFA16, 'M', '猪'), - (0xFA17, 'M', '益'), - (0xFA18, 'M', '礼'), - (0xFA19, 'M', '神'), - (0xFA1A, 'M', '祥'), - (0xFA1B, 'M', '福'), - (0xFA1C, 'M', '靖'), - (0xFA1D, 'M', '精'), - (0xFA1E, 'M', '羽'), - (0xFA1F, 'V'), - (0xFA20, 'M', '蘒'), - (0xFA21, 'V'), - (0xFA22, 'M', '諸'), - (0xFA23, 'V'), - (0xFA25, 'M', '逸'), - (0xFA26, 'M', '都'), - (0xFA27, 'V'), - (0xFA2A, 'M', '飯'), - (0xFA2B, 'M', '飼'), - (0xFA2C, 'M', '館'), - (0xFA2D, 'M', '鶴'), - (0xFA2E, 'M', '郞'), - (0xFA2F, 'M', '隷'), - (0xFA30, 'M', '侮'), - (0xFA31, 'M', '僧'), - (0xFA32, 'M', '免'), - (0xFA33, 'M', '勉'), - (0xFA34, 'M', '勤'), - (0xFA35, 'M', '卑'), - (0xFA36, 'M', '喝'), - (0xFA37, 'M', '嘆'), - (0xFA38, 'M', '器'), - (0xFA39, 'M', '塀'), - (0xFA3A, 'M', '墨'), - (0xFA3B, 'M', '層'), - (0xFA3C, 'M', '屮'), - (0xFA3D, 'M', '悔'), - (0xFA3E, 'M', '慨'), - (0xFA3F, 'M', '憎'), - (0xFA40, 'M', '懲'), - (0xFA41, 'M', '敏'), - (0xFA42, 'M', '既'), - (0xFA43, 'M', '暑'), - (0xFA44, 'M', '梅'), - (0xFA45, 'M', '海'), - (0xFA46, 'M', '渚'), - (0xFA47, 'M', '漢'), - (0xFA48, 'M', '煮'), - (0xFA49, 'M', '爫'), - (0xFA4A, 'M', '琢'), - (0xFA4B, 'M', '碑'), - (0xFA4C, 'M', '社'), - (0xFA4D, 'M', '祉'), - (0xFA4E, 'M', '祈'), - (0xFA4F, 'M', '祐'), - (0xFA50, 'M', '祖'), - (0xFA51, 'M', '祝'), - (0xFA52, 'M', '禍'), - (0xFA53, 'M', '禎'), - (0xFA54, 'M', '穀'), - (0xFA55, 'M', '突'), - (0xFA56, 'M', '節'), - (0xFA57, 'M', '練'), - (0xFA58, 'M', '縉'), - (0xFA59, 'M', '繁'), - (0xFA5A, 'M', '署'), - (0xFA5B, 'M', '者'), - (0xFA5C, 'M', '臭'), - (0xFA5D, 'M', '艹'), - (0xFA5F, 'M', '著'), - (0xFA60, 'M', '褐'), - (0xFA61, 'M', '視'), - (0xFA62, 'M', '謁'), - (0xFA63, 'M', '謹'), - (0xFA64, 'M', '賓'), - (0xFA65, 'M', '贈'), - (0xFA66, 'M', '辶'), - (0xFA67, 'M', '逸'), - (0xFA68, 'M', '難'), - (0xFA69, 'M', '響'), - (0xFA6A, 'M', '頻'), - (0xFA6B, 'M', '恵'), - (0xFA6C, 'M', '𤋮'), - (0xFA6D, 'M', '舘'), - (0xFA6E, 'X'), - (0xFA70, 'M', '並'), - (0xFA71, 'M', '况'), - (0xFA72, 'M', '全'), - (0xFA73, 'M', '侀'), - (0xFA74, 'M', '充'), - (0xFA75, 'M', '冀'), - (0xFA76, 'M', '勇'), - (0xFA77, 'M', '勺'), - (0xFA78, 'M', '喝'), - (0xFA79, 'M', '啕'), - (0xFA7A, 'M', '喙'), - (0xFA7B, 'M', '嗢'), - (0xFA7C, 'M', '塚'), - ] - -def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFA7D, 'M', '墳'), - (0xFA7E, 'M', '奄'), - (0xFA7F, 'M', '奔'), - (0xFA80, 'M', '婢'), - (0xFA81, 'M', '嬨'), - (0xFA82, 'M', '廒'), - (0xFA83, 'M', '廙'), - (0xFA84, 'M', '彩'), - (0xFA85, 'M', '徭'), - (0xFA86, 'M', '惘'), - (0xFA87, 'M', '慎'), - (0xFA88, 'M', '愈'), - (0xFA89, 'M', '憎'), - (0xFA8A, 'M', '慠'), - (0xFA8B, 'M', '懲'), - (0xFA8C, 'M', '戴'), - (0xFA8D, 'M', '揄'), - (0xFA8E, 'M', '搜'), - (0xFA8F, 'M', '摒'), - (0xFA90, 'M', '敖'), - (0xFA91, 'M', '晴'), - (0xFA92, 'M', '朗'), - (0xFA93, 'M', '望'), - (0xFA94, 'M', '杖'), - (0xFA95, 'M', '歹'), - (0xFA96, 'M', '殺'), - (0xFA97, 'M', '流'), - (0xFA98, 'M', '滛'), - (0xFA99, 'M', '滋'), - (0xFA9A, 'M', '漢'), - (0xFA9B, 'M', '瀞'), - (0xFA9C, 'M', '煮'), - (0xFA9D, 'M', '瞧'), - (0xFA9E, 'M', '爵'), - (0xFA9F, 'M', '犯'), - (0xFAA0, 'M', '猪'), - (0xFAA1, 'M', '瑱'), - (0xFAA2, 'M', '甆'), - (0xFAA3, 'M', '画'), - (0xFAA4, 'M', '瘝'), - (0xFAA5, 'M', '瘟'), - (0xFAA6, 'M', '益'), - (0xFAA7, 'M', '盛'), - (0xFAA8, 'M', '直'), - (0xFAA9, 'M', '睊'), - (0xFAAA, 'M', '着'), - (0xFAAB, 'M', '磌'), - (0xFAAC, 'M', '窱'), - (0xFAAD, 'M', '節'), - (0xFAAE, 'M', '类'), - (0xFAAF, 'M', '絛'), - (0xFAB0, 'M', '練'), - (0xFAB1, 'M', '缾'), - (0xFAB2, 'M', '者'), - (0xFAB3, 'M', '荒'), - (0xFAB4, 'M', '華'), - (0xFAB5, 'M', '蝹'), - (0xFAB6, 'M', '襁'), - (0xFAB7, 'M', '覆'), - (0xFAB8, 'M', '視'), - (0xFAB9, 'M', '調'), - (0xFABA, 'M', '諸'), - (0xFABB, 'M', '請'), - (0xFABC, 'M', '謁'), - (0xFABD, 'M', '諾'), - (0xFABE, 'M', '諭'), - (0xFABF, 'M', '謹'), - (0xFAC0, 'M', '變'), - (0xFAC1, 'M', '贈'), - (0xFAC2, 'M', '輸'), - (0xFAC3, 'M', '遲'), - (0xFAC4, 'M', '醙'), - (0xFAC5, 'M', '鉶'), - (0xFAC6, 'M', '陼'), - (0xFAC7, 'M', '難'), - (0xFAC8, 'M', '靖'), - (0xFAC9, 'M', '韛'), - (0xFACA, 'M', '響'), - (0xFACB, 'M', '頋'), - (0xFACC, 'M', '頻'), - (0xFACD, 'M', '鬒'), - (0xFACE, 'M', '龜'), - (0xFACF, 'M', '𢡊'), - (0xFAD0, 'M', '𢡄'), - (0xFAD1, 'M', '𣏕'), - (0xFAD2, 'M', '㮝'), - (0xFAD3, 'M', '䀘'), - (0xFAD4, 'M', '䀹'), - (0xFAD5, 'M', '𥉉'), - (0xFAD6, 'M', '𥳐'), - (0xFAD7, 'M', '𧻓'), - (0xFAD8, 'M', '齃'), - (0xFAD9, 'M', '龎'), - (0xFADA, 'X'), - (0xFB00, 'M', 'ff'), - (0xFB01, 'M', 'fi'), - (0xFB02, 'M', 'fl'), - (0xFB03, 'M', 'ffi'), - (0xFB04, 'M', 'ffl'), - (0xFB05, 'M', 'st'), - ] - -def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFB07, 'X'), - (0xFB13, 'M', 'մն'), - (0xFB14, 'M', 'մե'), - (0xFB15, 'M', 'մի'), - (0xFB16, 'M', 'վն'), - (0xFB17, 'M', 'մխ'), - (0xFB18, 'X'), - (0xFB1D, 'M', 'יִ'), - (0xFB1E, 'V'), - (0xFB1F, 'M', 'ײַ'), - (0xFB20, 'M', 'ע'), - (0xFB21, 'M', 'א'), - (0xFB22, 'M', 'ד'), - (0xFB23, 'M', 'ה'), - (0xFB24, 'M', 'כ'), - (0xFB25, 'M', 'ל'), - (0xFB26, 'M', 'ם'), - (0xFB27, 'M', 'ר'), - (0xFB28, 'M', 'ת'), - (0xFB29, '3', '+'), - (0xFB2A, 'M', 'שׁ'), - (0xFB2B, 'M', 'שׂ'), - (0xFB2C, 'M', 'שּׁ'), - (0xFB2D, 'M', 'שּׂ'), - (0xFB2E, 'M', 'אַ'), - (0xFB2F, 'M', 'אָ'), - (0xFB30, 'M', 'אּ'), - (0xFB31, 'M', 'בּ'), - (0xFB32, 'M', 'גּ'), - (0xFB33, 'M', 'דּ'), - (0xFB34, 'M', 'הּ'), - (0xFB35, 'M', 'וּ'), - (0xFB36, 'M', 'זּ'), - (0xFB37, 'X'), - (0xFB38, 'M', 'טּ'), - (0xFB39, 'M', 'יּ'), - (0xFB3A, 'M', 'ךּ'), - (0xFB3B, 'M', 'כּ'), - (0xFB3C, 'M', 'לּ'), - (0xFB3D, 'X'), - (0xFB3E, 'M', 'מּ'), - (0xFB3F, 'X'), - (0xFB40, 'M', 'נּ'), - (0xFB41, 'M', 'סּ'), - (0xFB42, 'X'), - (0xFB43, 'M', 'ףּ'), - (0xFB44, 'M', 'פּ'), - (0xFB45, 'X'), - (0xFB46, 'M', 'צּ'), - (0xFB47, 'M', 'קּ'), - (0xFB48, 'M', 'רּ'), - (0xFB49, 'M', 'שּ'), - (0xFB4A, 'M', 'תּ'), - (0xFB4B, 'M', 'וֹ'), - (0xFB4C, 'M', 'בֿ'), - (0xFB4D, 'M', 'כֿ'), - (0xFB4E, 'M', 'פֿ'), - (0xFB4F, 'M', 'אל'), - (0xFB50, 'M', 'ٱ'), - (0xFB52, 'M', 'ٻ'), - (0xFB56, 'M', 'پ'), - (0xFB5A, 'M', 'ڀ'), - (0xFB5E, 'M', 'ٺ'), - (0xFB62, 'M', 'ٿ'), - (0xFB66, 'M', 'ٹ'), - (0xFB6A, 'M', 'ڤ'), - (0xFB6E, 'M', 'ڦ'), - (0xFB72, 'M', 'ڄ'), - (0xFB76, 'M', 'ڃ'), - (0xFB7A, 'M', 'چ'), - (0xFB7E, 'M', 'ڇ'), - (0xFB82, 'M', 'ڍ'), - (0xFB84, 'M', 'ڌ'), - (0xFB86, 'M', 'ڎ'), - (0xFB88, 'M', 'ڈ'), - (0xFB8A, 'M', 'ژ'), - (0xFB8C, 'M', 'ڑ'), - (0xFB8E, 'M', 'ک'), - (0xFB92, 'M', 'گ'), - (0xFB96, 'M', 'ڳ'), - (0xFB9A, 'M', 'ڱ'), - (0xFB9E, 'M', 'ں'), - (0xFBA0, 'M', 'ڻ'), - (0xFBA4, 'M', 'ۀ'), - (0xFBA6, 'M', 'ہ'), - (0xFBAA, 'M', 'ھ'), - (0xFBAE, 'M', 'ے'), - (0xFBB0, 'M', 'ۓ'), - (0xFBB2, 'V'), - (0xFBC3, 'X'), - (0xFBD3, 'M', 'ڭ'), - (0xFBD7, 'M', 'ۇ'), - (0xFBD9, 'M', 'ۆ'), - (0xFBDB, 'M', 'ۈ'), - (0xFBDD, 'M', 'ۇٴ'), - (0xFBDE, 'M', 'ۋ'), - (0xFBE0, 'M', 'ۅ'), - (0xFBE2, 'M', 'ۉ'), - (0xFBE4, 'M', 'ې'), - (0xFBE8, 'M', 'ى'), - ] - -def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFBEA, 'M', 'ئا'), - (0xFBEC, 'M', 'ئە'), - (0xFBEE, 'M', 'ئو'), - (0xFBF0, 'M', 'ئۇ'), - (0xFBF2, 'M', 'ئۆ'), - (0xFBF4, 'M', 'ئۈ'), - (0xFBF6, 'M', 'ئې'), - (0xFBF9, 'M', 'ئى'), - (0xFBFC, 'M', 'ی'), - (0xFC00, 'M', 'ئج'), - (0xFC01, 'M', 'ئح'), - (0xFC02, 'M', 'ئم'), - (0xFC03, 'M', 'ئى'), - (0xFC04, 'M', 'ئي'), - (0xFC05, 'M', 'بج'), - (0xFC06, 'M', 'بح'), - (0xFC07, 'M', 'بخ'), - (0xFC08, 'M', 'بم'), - (0xFC09, 'M', 'بى'), - (0xFC0A, 'M', 'بي'), - (0xFC0B, 'M', 'تج'), - (0xFC0C, 'M', 'تح'), - (0xFC0D, 'M', 'تخ'), - (0xFC0E, 'M', 'تم'), - (0xFC0F, 'M', 'تى'), - (0xFC10, 'M', 'تي'), - (0xFC11, 'M', 'ثج'), - (0xFC12, 'M', 'ثم'), - (0xFC13, 'M', 'ثى'), - (0xFC14, 'M', 'ثي'), - (0xFC15, 'M', 'جح'), - (0xFC16, 'M', 'جم'), - (0xFC17, 'M', 'حج'), - (0xFC18, 'M', 'حم'), - (0xFC19, 'M', 'خج'), - (0xFC1A, 'M', 'خح'), - (0xFC1B, 'M', 'خم'), - (0xFC1C, 'M', 'سج'), - (0xFC1D, 'M', 'سح'), - (0xFC1E, 'M', 'سخ'), - (0xFC1F, 'M', 'سم'), - (0xFC20, 'M', 'صح'), - (0xFC21, 'M', 'صم'), - (0xFC22, 'M', 'ضج'), - (0xFC23, 'M', 'ضح'), - (0xFC24, 'M', 'ضخ'), - (0xFC25, 'M', 'ضم'), - (0xFC26, 'M', 'طح'), - (0xFC27, 'M', 'طم'), - (0xFC28, 'M', 'ظم'), - (0xFC29, 'M', 'عج'), - (0xFC2A, 'M', 'عم'), - (0xFC2B, 'M', 'غج'), - (0xFC2C, 'M', 'غم'), - (0xFC2D, 'M', 'فج'), - (0xFC2E, 'M', 'فح'), - (0xFC2F, 'M', 'فخ'), - (0xFC30, 'M', 'فم'), - (0xFC31, 'M', 'فى'), - (0xFC32, 'M', 'في'), - (0xFC33, 'M', 'قح'), - (0xFC34, 'M', 'قم'), - (0xFC35, 'M', 'قى'), - (0xFC36, 'M', 'قي'), - (0xFC37, 'M', 'كا'), - (0xFC38, 'M', 'كج'), - (0xFC39, 'M', 'كح'), - (0xFC3A, 'M', 'كخ'), - (0xFC3B, 'M', 'كل'), - (0xFC3C, 'M', 'كم'), - (0xFC3D, 'M', 'كى'), - (0xFC3E, 'M', 'كي'), - (0xFC3F, 'M', 'لج'), - (0xFC40, 'M', 'لح'), - (0xFC41, 'M', 'لخ'), - (0xFC42, 'M', 'لم'), - (0xFC43, 'M', 'لى'), - (0xFC44, 'M', 'لي'), - (0xFC45, 'M', 'مج'), - (0xFC46, 'M', 'مح'), - (0xFC47, 'M', 'مخ'), - (0xFC48, 'M', 'مم'), - (0xFC49, 'M', 'مى'), - (0xFC4A, 'M', 'مي'), - (0xFC4B, 'M', 'نج'), - (0xFC4C, 'M', 'نح'), - (0xFC4D, 'M', 'نخ'), - (0xFC4E, 'M', 'نم'), - (0xFC4F, 'M', 'نى'), - (0xFC50, 'M', 'ني'), - (0xFC51, 'M', 'هج'), - (0xFC52, 'M', 'هم'), - (0xFC53, 'M', 'هى'), - (0xFC54, 'M', 'هي'), - (0xFC55, 'M', 'يج'), - (0xFC56, 'M', 'يح'), - (0xFC57, 'M', 'يخ'), - (0xFC58, 'M', 'يم'), - (0xFC59, 'M', 'يى'), - (0xFC5A, 'M', 'يي'), - ] - -def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFC5B, 'M', 'ذٰ'), - (0xFC5C, 'M', 'رٰ'), - (0xFC5D, 'M', 'ىٰ'), - (0xFC5E, '3', ' ٌّ'), - (0xFC5F, '3', ' ٍّ'), - (0xFC60, '3', ' َّ'), - (0xFC61, '3', ' ُّ'), - (0xFC62, '3', ' ِّ'), - (0xFC63, '3', ' ّٰ'), - (0xFC64, 'M', 'ئر'), - (0xFC65, 'M', 'ئز'), - (0xFC66, 'M', 'ئم'), - (0xFC67, 'M', 'ئن'), - (0xFC68, 'M', 'ئى'), - (0xFC69, 'M', 'ئي'), - (0xFC6A, 'M', 'بر'), - (0xFC6B, 'M', 'بز'), - (0xFC6C, 'M', 'بم'), - (0xFC6D, 'M', 'بن'), - (0xFC6E, 'M', 'بى'), - (0xFC6F, 'M', 'بي'), - (0xFC70, 'M', 'تر'), - (0xFC71, 'M', 'تز'), - (0xFC72, 'M', 'تم'), - (0xFC73, 'M', 'تن'), - (0xFC74, 'M', 'تى'), - (0xFC75, 'M', 'تي'), - (0xFC76, 'M', 'ثر'), - (0xFC77, 'M', 'ثز'), - (0xFC78, 'M', 'ثم'), - (0xFC79, 'M', 'ثن'), - (0xFC7A, 'M', 'ثى'), - (0xFC7B, 'M', 'ثي'), - (0xFC7C, 'M', 'فى'), - (0xFC7D, 'M', 'في'), - (0xFC7E, 'M', 'قى'), - (0xFC7F, 'M', 'قي'), - (0xFC80, 'M', 'كا'), - (0xFC81, 'M', 'كل'), - (0xFC82, 'M', 'كم'), - (0xFC83, 'M', 'كى'), - (0xFC84, 'M', 'كي'), - (0xFC85, 'M', 'لم'), - (0xFC86, 'M', 'لى'), - (0xFC87, 'M', 'لي'), - (0xFC88, 'M', 'ما'), - (0xFC89, 'M', 'مم'), - (0xFC8A, 'M', 'نر'), - (0xFC8B, 'M', 'نز'), - (0xFC8C, 'M', 'نم'), - (0xFC8D, 'M', 'نن'), - (0xFC8E, 'M', 'نى'), - (0xFC8F, 'M', 'ني'), - (0xFC90, 'M', 'ىٰ'), - (0xFC91, 'M', 'ير'), - (0xFC92, 'M', 'يز'), - (0xFC93, 'M', 'يم'), - (0xFC94, 'M', 'ين'), - (0xFC95, 'M', 'يى'), - (0xFC96, 'M', 'يي'), - (0xFC97, 'M', 'ئج'), - (0xFC98, 'M', 'ئح'), - (0xFC99, 'M', 'ئخ'), - (0xFC9A, 'M', 'ئم'), - (0xFC9B, 'M', 'ئه'), - (0xFC9C, 'M', 'بج'), - (0xFC9D, 'M', 'بح'), - (0xFC9E, 'M', 'بخ'), - (0xFC9F, 'M', 'بم'), - (0xFCA0, 'M', 'به'), - (0xFCA1, 'M', 'تج'), - (0xFCA2, 'M', 'تح'), - (0xFCA3, 'M', 'تخ'), - (0xFCA4, 'M', 'تم'), - (0xFCA5, 'M', 'ته'), - (0xFCA6, 'M', 'ثم'), - (0xFCA7, 'M', 'جح'), - (0xFCA8, 'M', 'جم'), - (0xFCA9, 'M', 'حج'), - (0xFCAA, 'M', 'حم'), - (0xFCAB, 'M', 'خج'), - (0xFCAC, 'M', 'خم'), - (0xFCAD, 'M', 'سج'), - (0xFCAE, 'M', 'سح'), - (0xFCAF, 'M', 'سخ'), - (0xFCB0, 'M', 'سم'), - (0xFCB1, 'M', 'صح'), - (0xFCB2, 'M', 'صخ'), - (0xFCB3, 'M', 'صم'), - (0xFCB4, 'M', 'ضج'), - (0xFCB5, 'M', 'ضح'), - (0xFCB6, 'M', 'ضخ'), - (0xFCB7, 'M', 'ضم'), - (0xFCB8, 'M', 'طح'), - (0xFCB9, 'M', 'ظم'), - (0xFCBA, 'M', 'عج'), - (0xFCBB, 'M', 'عم'), - (0xFCBC, 'M', 'غج'), - (0xFCBD, 'M', 'غم'), - (0xFCBE, 'M', 'فج'), - ] - -def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFCBF, 'M', 'فح'), - (0xFCC0, 'M', 'فخ'), - (0xFCC1, 'M', 'فم'), - (0xFCC2, 'M', 'قح'), - (0xFCC3, 'M', 'قم'), - (0xFCC4, 'M', 'كج'), - (0xFCC5, 'M', 'كح'), - (0xFCC6, 'M', 'كخ'), - (0xFCC7, 'M', 'كل'), - (0xFCC8, 'M', 'كم'), - (0xFCC9, 'M', 'لج'), - (0xFCCA, 'M', 'لح'), - (0xFCCB, 'M', 'لخ'), - (0xFCCC, 'M', 'لم'), - (0xFCCD, 'M', 'له'), - (0xFCCE, 'M', 'مج'), - (0xFCCF, 'M', 'مح'), - (0xFCD0, 'M', 'مخ'), - (0xFCD1, 'M', 'مم'), - (0xFCD2, 'M', 'نج'), - (0xFCD3, 'M', 'نح'), - (0xFCD4, 'M', 'نخ'), - (0xFCD5, 'M', 'نم'), - (0xFCD6, 'M', 'نه'), - (0xFCD7, 'M', 'هج'), - (0xFCD8, 'M', 'هم'), - (0xFCD9, 'M', 'هٰ'), - (0xFCDA, 'M', 'يج'), - (0xFCDB, 'M', 'يح'), - (0xFCDC, 'M', 'يخ'), - (0xFCDD, 'M', 'يم'), - (0xFCDE, 'M', 'يه'), - (0xFCDF, 'M', 'ئم'), - (0xFCE0, 'M', 'ئه'), - (0xFCE1, 'M', 'بم'), - (0xFCE2, 'M', 'به'), - (0xFCE3, 'M', 'تم'), - (0xFCE4, 'M', 'ته'), - (0xFCE5, 'M', 'ثم'), - (0xFCE6, 'M', 'ثه'), - (0xFCE7, 'M', 'سم'), - (0xFCE8, 'M', 'سه'), - (0xFCE9, 'M', 'شم'), - (0xFCEA, 'M', 'شه'), - (0xFCEB, 'M', 'كل'), - (0xFCEC, 'M', 'كم'), - (0xFCED, 'M', 'لم'), - (0xFCEE, 'M', 'نم'), - (0xFCEF, 'M', 'نه'), - (0xFCF0, 'M', 'يم'), - (0xFCF1, 'M', 'يه'), - (0xFCF2, 'M', 'ـَّ'), - (0xFCF3, 'M', 'ـُّ'), - (0xFCF4, 'M', 'ـِّ'), - (0xFCF5, 'M', 'طى'), - (0xFCF6, 'M', 'طي'), - (0xFCF7, 'M', 'عى'), - (0xFCF8, 'M', 'عي'), - (0xFCF9, 'M', 'غى'), - (0xFCFA, 'M', 'غي'), - (0xFCFB, 'M', 'سى'), - (0xFCFC, 'M', 'سي'), - (0xFCFD, 'M', 'شى'), - (0xFCFE, 'M', 'شي'), - (0xFCFF, 'M', 'حى'), - (0xFD00, 'M', 'حي'), - (0xFD01, 'M', 'جى'), - (0xFD02, 'M', 'جي'), - (0xFD03, 'M', 'خى'), - (0xFD04, 'M', 'خي'), - (0xFD05, 'M', 'صى'), - (0xFD06, 'M', 'صي'), - (0xFD07, 'M', 'ضى'), - (0xFD08, 'M', 'ضي'), - (0xFD09, 'M', 'شج'), - (0xFD0A, 'M', 'شح'), - (0xFD0B, 'M', 'شخ'), - (0xFD0C, 'M', 'شم'), - (0xFD0D, 'M', 'شر'), - (0xFD0E, 'M', 'سر'), - (0xFD0F, 'M', 'صر'), - (0xFD10, 'M', 'ضر'), - (0xFD11, 'M', 'طى'), - (0xFD12, 'M', 'طي'), - (0xFD13, 'M', 'عى'), - (0xFD14, 'M', 'عي'), - (0xFD15, 'M', 'غى'), - (0xFD16, 'M', 'غي'), - (0xFD17, 'M', 'سى'), - (0xFD18, 'M', 'سي'), - (0xFD19, 'M', 'شى'), - (0xFD1A, 'M', 'شي'), - (0xFD1B, 'M', 'حى'), - (0xFD1C, 'M', 'حي'), - (0xFD1D, 'M', 'جى'), - (0xFD1E, 'M', 'جي'), - (0xFD1F, 'M', 'خى'), - (0xFD20, 'M', 'خي'), - (0xFD21, 'M', 'صى'), - (0xFD22, 'M', 'صي'), - ] - -def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFD23, 'M', 'ضى'), - (0xFD24, 'M', 'ضي'), - (0xFD25, 'M', 'شج'), - (0xFD26, 'M', 'شح'), - (0xFD27, 'M', 'شخ'), - (0xFD28, 'M', 'شم'), - (0xFD29, 'M', 'شر'), - (0xFD2A, 'M', 'سر'), - (0xFD2B, 'M', 'صر'), - (0xFD2C, 'M', 'ضر'), - (0xFD2D, 'M', 'شج'), - (0xFD2E, 'M', 'شح'), - (0xFD2F, 'M', 'شخ'), - (0xFD30, 'M', 'شم'), - (0xFD31, 'M', 'سه'), - (0xFD32, 'M', 'شه'), - (0xFD33, 'M', 'طم'), - (0xFD34, 'M', 'سج'), - (0xFD35, 'M', 'سح'), - (0xFD36, 'M', 'سخ'), - (0xFD37, 'M', 'شج'), - (0xFD38, 'M', 'شح'), - (0xFD39, 'M', 'شخ'), - (0xFD3A, 'M', 'طم'), - (0xFD3B, 'M', 'ظم'), - (0xFD3C, 'M', 'اً'), - (0xFD3E, 'V'), - (0xFD50, 'M', 'تجم'), - (0xFD51, 'M', 'تحج'), - (0xFD53, 'M', 'تحم'), - (0xFD54, 'M', 'تخم'), - (0xFD55, 'M', 'تمج'), - (0xFD56, 'M', 'تمح'), - (0xFD57, 'M', 'تمخ'), - (0xFD58, 'M', 'جمح'), - (0xFD5A, 'M', 'حمي'), - (0xFD5B, 'M', 'حمى'), - (0xFD5C, 'M', 'سحج'), - (0xFD5D, 'M', 'سجح'), - (0xFD5E, 'M', 'سجى'), - (0xFD5F, 'M', 'سمح'), - (0xFD61, 'M', 'سمج'), - (0xFD62, 'M', 'سمم'), - (0xFD64, 'M', 'صحح'), - (0xFD66, 'M', 'صمم'), - (0xFD67, 'M', 'شحم'), - (0xFD69, 'M', 'شجي'), - (0xFD6A, 'M', 'شمخ'), - (0xFD6C, 'M', 'شمم'), - (0xFD6E, 'M', 'ضحى'), - (0xFD6F, 'M', 'ضخم'), - (0xFD71, 'M', 'طمح'), - (0xFD73, 'M', 'طمم'), - (0xFD74, 'M', 'طمي'), - (0xFD75, 'M', 'عجم'), - (0xFD76, 'M', 'عمم'), - (0xFD78, 'M', 'عمى'), - (0xFD79, 'M', 'غمم'), - (0xFD7A, 'M', 'غمي'), - (0xFD7B, 'M', 'غمى'), - (0xFD7C, 'M', 'فخم'), - (0xFD7E, 'M', 'قمح'), - (0xFD7F, 'M', 'قمم'), - (0xFD80, 'M', 'لحم'), - (0xFD81, 'M', 'لحي'), - (0xFD82, 'M', 'لحى'), - (0xFD83, 'M', 'لجج'), - (0xFD85, 'M', 'لخم'), - (0xFD87, 'M', 'لمح'), - (0xFD89, 'M', 'محج'), - (0xFD8A, 'M', 'محم'), - (0xFD8B, 'M', 'محي'), - (0xFD8C, 'M', 'مجح'), - (0xFD8D, 'M', 'مجم'), - (0xFD8E, 'M', 'مخج'), - (0xFD8F, 'M', 'مخم'), - (0xFD90, 'X'), - (0xFD92, 'M', 'مجخ'), - (0xFD93, 'M', 'همج'), - (0xFD94, 'M', 'همم'), - (0xFD95, 'M', 'نحم'), - (0xFD96, 'M', 'نحى'), - (0xFD97, 'M', 'نجم'), - (0xFD99, 'M', 'نجى'), - (0xFD9A, 'M', 'نمي'), - (0xFD9B, 'M', 'نمى'), - (0xFD9C, 'M', 'يمم'), - (0xFD9E, 'M', 'بخي'), - (0xFD9F, 'M', 'تجي'), - (0xFDA0, 'M', 'تجى'), - (0xFDA1, 'M', 'تخي'), - (0xFDA2, 'M', 'تخى'), - (0xFDA3, 'M', 'تمي'), - (0xFDA4, 'M', 'تمى'), - (0xFDA5, 'M', 'جمي'), - (0xFDA6, 'M', 'جحى'), - (0xFDA7, 'M', 'جمى'), - (0xFDA8, 'M', 'سخى'), - (0xFDA9, 'M', 'صحي'), - (0xFDAA, 'M', 'شحي'), - ] - -def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFDAB, 'M', 'ضحي'), - (0xFDAC, 'M', 'لجي'), - (0xFDAD, 'M', 'لمي'), - (0xFDAE, 'M', 'يحي'), - (0xFDAF, 'M', 'يجي'), - (0xFDB0, 'M', 'يمي'), - (0xFDB1, 'M', 'ممي'), - (0xFDB2, 'M', 'قمي'), - (0xFDB3, 'M', 'نحي'), - (0xFDB4, 'M', 'قمح'), - (0xFDB5, 'M', 'لحم'), - (0xFDB6, 'M', 'عمي'), - (0xFDB7, 'M', 'كمي'), - (0xFDB8, 'M', 'نجح'), - (0xFDB9, 'M', 'مخي'), - (0xFDBA, 'M', 'لجم'), - (0xFDBB, 'M', 'كمم'), - (0xFDBC, 'M', 'لجم'), - (0xFDBD, 'M', 'نجح'), - (0xFDBE, 'M', 'جحي'), - (0xFDBF, 'M', 'حجي'), - (0xFDC0, 'M', 'مجي'), - (0xFDC1, 'M', 'فمي'), - (0xFDC2, 'M', 'بحي'), - (0xFDC3, 'M', 'كمم'), - (0xFDC4, 'M', 'عجم'), - (0xFDC5, 'M', 'صمم'), - (0xFDC6, 'M', 'سخي'), - (0xFDC7, 'M', 'نجي'), - (0xFDC8, 'X'), - (0xFDCF, 'V'), - (0xFDD0, 'X'), - (0xFDF0, 'M', 'صلے'), - (0xFDF1, 'M', 'قلے'), - (0xFDF2, 'M', 'الله'), - (0xFDF3, 'M', 'اكبر'), - (0xFDF4, 'M', 'محمد'), - (0xFDF5, 'M', 'صلعم'), - (0xFDF6, 'M', 'رسول'), - (0xFDF7, 'M', 'عليه'), - (0xFDF8, 'M', 'وسلم'), - (0xFDF9, 'M', 'صلى'), - (0xFDFA, '3', 'صلى الله عليه وسلم'), - (0xFDFB, '3', 'جل جلاله'), - (0xFDFC, 'M', 'ریال'), - (0xFDFD, 'V'), - (0xFE00, 'I'), - (0xFE10, '3', ','), - (0xFE11, 'M', '、'), - (0xFE12, 'X'), - (0xFE13, '3', ':'), - (0xFE14, '3', ';'), - (0xFE15, '3', '!'), - (0xFE16, '3', '?'), - (0xFE17, 'M', '〖'), - (0xFE18, 'M', '〗'), - (0xFE19, 'X'), - (0xFE20, 'V'), - (0xFE30, 'X'), - (0xFE31, 'M', '—'), - (0xFE32, 'M', '–'), - (0xFE33, '3', '_'), - (0xFE35, '3', '('), - (0xFE36, '3', ')'), - (0xFE37, '3', '{'), - (0xFE38, '3', '}'), - (0xFE39, 'M', '〔'), - (0xFE3A, 'M', '〕'), - (0xFE3B, 'M', '【'), - (0xFE3C, 'M', '】'), - (0xFE3D, 'M', '《'), - (0xFE3E, 'M', '》'), - (0xFE3F, 'M', '〈'), - (0xFE40, 'M', '〉'), - (0xFE41, 'M', '「'), - (0xFE42, 'M', '」'), - (0xFE43, 'M', '『'), - (0xFE44, 'M', '』'), - (0xFE45, 'V'), - (0xFE47, '3', '['), - (0xFE48, '3', ']'), - (0xFE49, '3', ' ̅'), - (0xFE4D, '3', '_'), - (0xFE50, '3', ','), - (0xFE51, 'M', '、'), - (0xFE52, 'X'), - (0xFE54, '3', ';'), - (0xFE55, '3', ':'), - (0xFE56, '3', '?'), - (0xFE57, '3', '!'), - (0xFE58, 'M', '—'), - (0xFE59, '3', '('), - (0xFE5A, '3', ')'), - (0xFE5B, '3', '{'), - (0xFE5C, '3', '}'), - (0xFE5D, 'M', '〔'), - (0xFE5E, 'M', '〕'), - (0xFE5F, '3', '#'), - (0xFE60, '3', '&'), - (0xFE61, '3', '*'), - ] - -def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFE62, '3', '+'), - (0xFE63, 'M', '-'), - (0xFE64, '3', '<'), - (0xFE65, '3', '>'), - (0xFE66, '3', '='), - (0xFE67, 'X'), - (0xFE68, '3', '\\'), - (0xFE69, '3', '$'), - (0xFE6A, '3', '%'), - (0xFE6B, '3', '@'), - (0xFE6C, 'X'), - (0xFE70, '3', ' ً'), - (0xFE71, 'M', 'ـً'), - (0xFE72, '3', ' ٌ'), - (0xFE73, 'V'), - (0xFE74, '3', ' ٍ'), - (0xFE75, 'X'), - (0xFE76, '3', ' َ'), - (0xFE77, 'M', 'ـَ'), - (0xFE78, '3', ' ُ'), - (0xFE79, 'M', 'ـُ'), - (0xFE7A, '3', ' ِ'), - (0xFE7B, 'M', 'ـِ'), - (0xFE7C, '3', ' ّ'), - (0xFE7D, 'M', 'ـّ'), - (0xFE7E, '3', ' ْ'), - (0xFE7F, 'M', 'ـْ'), - (0xFE80, 'M', 'ء'), - (0xFE81, 'M', 'آ'), - (0xFE83, 'M', 'أ'), - (0xFE85, 'M', 'ؤ'), - (0xFE87, 'M', 'إ'), - (0xFE89, 'M', 'ئ'), - (0xFE8D, 'M', 'ا'), - (0xFE8F, 'M', 'ب'), - (0xFE93, 'M', 'ة'), - (0xFE95, 'M', 'ت'), - (0xFE99, 'M', 'ث'), - (0xFE9D, 'M', 'ج'), - (0xFEA1, 'M', 'ح'), - (0xFEA5, 'M', 'خ'), - (0xFEA9, 'M', 'د'), - (0xFEAB, 'M', 'ذ'), - (0xFEAD, 'M', 'ر'), - (0xFEAF, 'M', 'ز'), - (0xFEB1, 'M', 'س'), - (0xFEB5, 'M', 'ش'), - (0xFEB9, 'M', 'ص'), - (0xFEBD, 'M', 'ض'), - (0xFEC1, 'M', 'ط'), - (0xFEC5, 'M', 'ظ'), - (0xFEC9, 'M', 'ع'), - (0xFECD, 'M', 'غ'), - (0xFED1, 'M', 'ف'), - (0xFED5, 'M', 'ق'), - (0xFED9, 'M', 'ك'), - (0xFEDD, 'M', 'ل'), - (0xFEE1, 'M', 'م'), - (0xFEE5, 'M', 'ن'), - (0xFEE9, 'M', 'ه'), - (0xFEED, 'M', 'و'), - (0xFEEF, 'M', 'ى'), - (0xFEF1, 'M', 'ي'), - (0xFEF5, 'M', 'لآ'), - (0xFEF7, 'M', 'لأ'), - (0xFEF9, 'M', 'لإ'), - (0xFEFB, 'M', 'لا'), - (0xFEFD, 'X'), - (0xFEFF, 'I'), - (0xFF00, 'X'), - (0xFF01, '3', '!'), - (0xFF02, '3', '"'), - (0xFF03, '3', '#'), - (0xFF04, '3', '$'), - (0xFF05, '3', '%'), - (0xFF06, '3', '&'), - (0xFF07, '3', '\''), - (0xFF08, '3', '('), - (0xFF09, '3', ')'), - (0xFF0A, '3', '*'), - (0xFF0B, '3', '+'), - (0xFF0C, '3', ','), - (0xFF0D, 'M', '-'), - (0xFF0E, 'M', '.'), - (0xFF0F, '3', '/'), - (0xFF10, 'M', '0'), - (0xFF11, 'M', '1'), - (0xFF12, 'M', '2'), - (0xFF13, 'M', '3'), - (0xFF14, 'M', '4'), - (0xFF15, 'M', '5'), - (0xFF16, 'M', '6'), - (0xFF17, 'M', '7'), - (0xFF18, 'M', '8'), - (0xFF19, 'M', '9'), - (0xFF1A, '3', ':'), - (0xFF1B, '3', ';'), - (0xFF1C, '3', '<'), - (0xFF1D, '3', '='), - (0xFF1E, '3', '>'), - ] - -def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF1F, '3', '?'), - (0xFF20, '3', '@'), - (0xFF21, 'M', 'a'), - (0xFF22, 'M', 'b'), - (0xFF23, 'M', 'c'), - (0xFF24, 'M', 'd'), - (0xFF25, 'M', 'e'), - (0xFF26, 'M', 'f'), - (0xFF27, 'M', 'g'), - (0xFF28, 'M', 'h'), - (0xFF29, 'M', 'i'), - (0xFF2A, 'M', 'j'), - (0xFF2B, 'M', 'k'), - (0xFF2C, 'M', 'l'), - (0xFF2D, 'M', 'm'), - (0xFF2E, 'M', 'n'), - (0xFF2F, 'M', 'o'), - (0xFF30, 'M', 'p'), - (0xFF31, 'M', 'q'), - (0xFF32, 'M', 'r'), - (0xFF33, 'M', 's'), - (0xFF34, 'M', 't'), - (0xFF35, 'M', 'u'), - (0xFF36, 'M', 'v'), - (0xFF37, 'M', 'w'), - (0xFF38, 'M', 'x'), - (0xFF39, 'M', 'y'), - (0xFF3A, 'M', 'z'), - (0xFF3B, '3', '['), - (0xFF3C, '3', '\\'), - (0xFF3D, '3', ']'), - (0xFF3E, '3', '^'), - (0xFF3F, '3', '_'), - (0xFF40, '3', '`'), - (0xFF41, 'M', 'a'), - (0xFF42, 'M', 'b'), - (0xFF43, 'M', 'c'), - (0xFF44, 'M', 'd'), - (0xFF45, 'M', 'e'), - (0xFF46, 'M', 'f'), - (0xFF47, 'M', 'g'), - (0xFF48, 'M', 'h'), - (0xFF49, 'M', 'i'), - (0xFF4A, 'M', 'j'), - (0xFF4B, 'M', 'k'), - (0xFF4C, 'M', 'l'), - (0xFF4D, 'M', 'm'), - (0xFF4E, 'M', 'n'), - (0xFF4F, 'M', 'o'), - (0xFF50, 'M', 'p'), - (0xFF51, 'M', 'q'), - (0xFF52, 'M', 'r'), - (0xFF53, 'M', 's'), - (0xFF54, 'M', 't'), - (0xFF55, 'M', 'u'), - (0xFF56, 'M', 'v'), - (0xFF57, 'M', 'w'), - (0xFF58, 'M', 'x'), - (0xFF59, 'M', 'y'), - (0xFF5A, 'M', 'z'), - (0xFF5B, '3', '{'), - (0xFF5C, '3', '|'), - (0xFF5D, '3', '}'), - (0xFF5E, '3', '~'), - (0xFF5F, 'M', '⦅'), - (0xFF60, 'M', '⦆'), - (0xFF61, 'M', '.'), - (0xFF62, 'M', '「'), - (0xFF63, 'M', '」'), - (0xFF64, 'M', '、'), - (0xFF65, 'M', '・'), - (0xFF66, 'M', 'ヲ'), - (0xFF67, 'M', 'ァ'), - (0xFF68, 'M', 'ィ'), - (0xFF69, 'M', 'ゥ'), - (0xFF6A, 'M', 'ェ'), - (0xFF6B, 'M', 'ォ'), - (0xFF6C, 'M', 'ャ'), - (0xFF6D, 'M', 'ュ'), - (0xFF6E, 'M', 'ョ'), - (0xFF6F, 'M', 'ッ'), - (0xFF70, 'M', 'ー'), - (0xFF71, 'M', 'ア'), - (0xFF72, 'M', 'イ'), - (0xFF73, 'M', 'ウ'), - (0xFF74, 'M', 'エ'), - (0xFF75, 'M', 'オ'), - (0xFF76, 'M', 'カ'), - (0xFF77, 'M', 'キ'), - (0xFF78, 'M', 'ク'), - (0xFF79, 'M', 'ケ'), - (0xFF7A, 'M', 'コ'), - (0xFF7B, 'M', 'サ'), - (0xFF7C, 'M', 'シ'), - (0xFF7D, 'M', 'ス'), - (0xFF7E, 'M', 'セ'), - (0xFF7F, 'M', 'ソ'), - (0xFF80, 'M', 'タ'), - (0xFF81, 'M', 'チ'), - (0xFF82, 'M', 'ツ'), - ] - -def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFF83, 'M', 'テ'), - (0xFF84, 'M', 'ト'), - (0xFF85, 'M', 'ナ'), - (0xFF86, 'M', 'ニ'), - (0xFF87, 'M', 'ヌ'), - (0xFF88, 'M', 'ネ'), - (0xFF89, 'M', 'ノ'), - (0xFF8A, 'M', 'ハ'), - (0xFF8B, 'M', 'ヒ'), - (0xFF8C, 'M', 'フ'), - (0xFF8D, 'M', 'ヘ'), - (0xFF8E, 'M', 'ホ'), - (0xFF8F, 'M', 'マ'), - (0xFF90, 'M', 'ミ'), - (0xFF91, 'M', 'ム'), - (0xFF92, 'M', 'メ'), - (0xFF93, 'M', 'モ'), - (0xFF94, 'M', 'ヤ'), - (0xFF95, 'M', 'ユ'), - (0xFF96, 'M', 'ヨ'), - (0xFF97, 'M', 'ラ'), - (0xFF98, 'M', 'リ'), - (0xFF99, 'M', 'ル'), - (0xFF9A, 'M', 'レ'), - (0xFF9B, 'M', 'ロ'), - (0xFF9C, 'M', 'ワ'), - (0xFF9D, 'M', 'ン'), - (0xFF9E, 'M', '゙'), - (0xFF9F, 'M', '゚'), - (0xFFA0, 'X'), - (0xFFA1, 'M', 'ᄀ'), - (0xFFA2, 'M', 'ᄁ'), - (0xFFA3, 'M', 'ᆪ'), - (0xFFA4, 'M', 'ᄂ'), - (0xFFA5, 'M', 'ᆬ'), - (0xFFA6, 'M', 'ᆭ'), - (0xFFA7, 'M', 'ᄃ'), - (0xFFA8, 'M', 'ᄄ'), - (0xFFA9, 'M', 'ᄅ'), - (0xFFAA, 'M', 'ᆰ'), - (0xFFAB, 'M', 'ᆱ'), - (0xFFAC, 'M', 'ᆲ'), - (0xFFAD, 'M', 'ᆳ'), - (0xFFAE, 'M', 'ᆴ'), - (0xFFAF, 'M', 'ᆵ'), - (0xFFB0, 'M', 'ᄚ'), - (0xFFB1, 'M', 'ᄆ'), - (0xFFB2, 'M', 'ᄇ'), - (0xFFB3, 'M', 'ᄈ'), - (0xFFB4, 'M', 'ᄡ'), - (0xFFB5, 'M', 'ᄉ'), - (0xFFB6, 'M', 'ᄊ'), - (0xFFB7, 'M', 'ᄋ'), - (0xFFB8, 'M', 'ᄌ'), - (0xFFB9, 'M', 'ᄍ'), - (0xFFBA, 'M', 'ᄎ'), - (0xFFBB, 'M', 'ᄏ'), - (0xFFBC, 'M', 'ᄐ'), - (0xFFBD, 'M', 'ᄑ'), - (0xFFBE, 'M', 'ᄒ'), - (0xFFBF, 'X'), - (0xFFC2, 'M', 'ᅡ'), - (0xFFC3, 'M', 'ᅢ'), - (0xFFC4, 'M', 'ᅣ'), - (0xFFC5, 'M', 'ᅤ'), - (0xFFC6, 'M', 'ᅥ'), - (0xFFC7, 'M', 'ᅦ'), - (0xFFC8, 'X'), - (0xFFCA, 'M', 'ᅧ'), - (0xFFCB, 'M', 'ᅨ'), - (0xFFCC, 'M', 'ᅩ'), - (0xFFCD, 'M', 'ᅪ'), - (0xFFCE, 'M', 'ᅫ'), - (0xFFCF, 'M', 'ᅬ'), - (0xFFD0, 'X'), - (0xFFD2, 'M', 'ᅭ'), - (0xFFD3, 'M', 'ᅮ'), - (0xFFD4, 'M', 'ᅯ'), - (0xFFD5, 'M', 'ᅰ'), - (0xFFD6, 'M', 'ᅱ'), - (0xFFD7, 'M', 'ᅲ'), - (0xFFD8, 'X'), - (0xFFDA, 'M', 'ᅳ'), - (0xFFDB, 'M', 'ᅴ'), - (0xFFDC, 'M', 'ᅵ'), - (0xFFDD, 'X'), - (0xFFE0, 'M', '¢'), - (0xFFE1, 'M', '£'), - (0xFFE2, 'M', '¬'), - (0xFFE3, '3', ' ̄'), - (0xFFE4, 'M', '¦'), - (0xFFE5, 'M', '¥'), - (0xFFE6, 'M', '₩'), - (0xFFE7, 'X'), - (0xFFE8, 'M', '│'), - (0xFFE9, 'M', '←'), - (0xFFEA, 'M', '↑'), - (0xFFEB, 'M', '→'), - (0xFFEC, 'M', '↓'), - (0xFFED, 'M', '■'), - ] - -def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0xFFEE, 'M', '○'), - (0xFFEF, 'X'), - (0x10000, 'V'), - (0x1000C, 'X'), - (0x1000D, 'V'), - (0x10027, 'X'), - (0x10028, 'V'), - (0x1003B, 'X'), - (0x1003C, 'V'), - (0x1003E, 'X'), - (0x1003F, 'V'), - (0x1004E, 'X'), - (0x10050, 'V'), - (0x1005E, 'X'), - (0x10080, 'V'), - (0x100FB, 'X'), - (0x10100, 'V'), - (0x10103, 'X'), - (0x10107, 'V'), - (0x10134, 'X'), - (0x10137, 'V'), - (0x1018F, 'X'), - (0x10190, 'V'), - (0x1019D, 'X'), - (0x101A0, 'V'), - (0x101A1, 'X'), - (0x101D0, 'V'), - (0x101FE, 'X'), - (0x10280, 'V'), - (0x1029D, 'X'), - (0x102A0, 'V'), - (0x102D1, 'X'), - (0x102E0, 'V'), - (0x102FC, 'X'), - (0x10300, 'V'), - (0x10324, 'X'), - (0x1032D, 'V'), - (0x1034B, 'X'), - (0x10350, 'V'), - (0x1037B, 'X'), - (0x10380, 'V'), - (0x1039E, 'X'), - (0x1039F, 'V'), - (0x103C4, 'X'), - (0x103C8, 'V'), - (0x103D6, 'X'), - (0x10400, 'M', '𐐨'), - (0x10401, 'M', '𐐩'), - (0x10402, 'M', '𐐪'), - (0x10403, 'M', '𐐫'), - (0x10404, 'M', '𐐬'), - (0x10405, 'M', '𐐭'), - (0x10406, 'M', '𐐮'), - (0x10407, 'M', '𐐯'), - (0x10408, 'M', '𐐰'), - (0x10409, 'M', '𐐱'), - (0x1040A, 'M', '𐐲'), - (0x1040B, 'M', '𐐳'), - (0x1040C, 'M', '𐐴'), - (0x1040D, 'M', '𐐵'), - (0x1040E, 'M', '𐐶'), - (0x1040F, 'M', '𐐷'), - (0x10410, 'M', '𐐸'), - (0x10411, 'M', '𐐹'), - (0x10412, 'M', '𐐺'), - (0x10413, 'M', '𐐻'), - (0x10414, 'M', '𐐼'), - (0x10415, 'M', '𐐽'), - (0x10416, 'M', '𐐾'), - (0x10417, 'M', '𐐿'), - (0x10418, 'M', '𐑀'), - (0x10419, 'M', '𐑁'), - (0x1041A, 'M', '𐑂'), - (0x1041B, 'M', '𐑃'), - (0x1041C, 'M', '𐑄'), - (0x1041D, 'M', '𐑅'), - (0x1041E, 'M', '𐑆'), - (0x1041F, 'M', '𐑇'), - (0x10420, 'M', '𐑈'), - (0x10421, 'M', '𐑉'), - (0x10422, 'M', '𐑊'), - (0x10423, 'M', '𐑋'), - (0x10424, 'M', '𐑌'), - (0x10425, 'M', '𐑍'), - (0x10426, 'M', '𐑎'), - (0x10427, 'M', '𐑏'), - (0x10428, 'V'), - (0x1049E, 'X'), - (0x104A0, 'V'), - (0x104AA, 'X'), - (0x104B0, 'M', '𐓘'), - (0x104B1, 'M', '𐓙'), - (0x104B2, 'M', '𐓚'), - (0x104B3, 'M', '𐓛'), - (0x104B4, 'M', '𐓜'), - (0x104B5, 'M', '𐓝'), - (0x104B6, 'M', '𐓞'), - (0x104B7, 'M', '𐓟'), - (0x104B8, 'M', '𐓠'), - (0x104B9, 'M', '𐓡'), - ] - -def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x104BA, 'M', '𐓢'), - (0x104BB, 'M', '𐓣'), - (0x104BC, 'M', '𐓤'), - (0x104BD, 'M', '𐓥'), - (0x104BE, 'M', '𐓦'), - (0x104BF, 'M', '𐓧'), - (0x104C0, 'M', '𐓨'), - (0x104C1, 'M', '𐓩'), - (0x104C2, 'M', '𐓪'), - (0x104C3, 'M', '𐓫'), - (0x104C4, 'M', '𐓬'), - (0x104C5, 'M', '𐓭'), - (0x104C6, 'M', '𐓮'), - (0x104C7, 'M', '𐓯'), - (0x104C8, 'M', '𐓰'), - (0x104C9, 'M', '𐓱'), - (0x104CA, 'M', '𐓲'), - (0x104CB, 'M', '𐓳'), - (0x104CC, 'M', '𐓴'), - (0x104CD, 'M', '𐓵'), - (0x104CE, 'M', '𐓶'), - (0x104CF, 'M', '𐓷'), - (0x104D0, 'M', '𐓸'), - (0x104D1, 'M', '𐓹'), - (0x104D2, 'M', '𐓺'), - (0x104D3, 'M', '𐓻'), - (0x104D4, 'X'), - (0x104D8, 'V'), - (0x104FC, 'X'), - (0x10500, 'V'), - (0x10528, 'X'), - (0x10530, 'V'), - (0x10564, 'X'), - (0x1056F, 'V'), - (0x10570, 'M', '𐖗'), - (0x10571, 'M', '𐖘'), - (0x10572, 'M', '𐖙'), - (0x10573, 'M', '𐖚'), - (0x10574, 'M', '𐖛'), - (0x10575, 'M', '𐖜'), - (0x10576, 'M', '𐖝'), - (0x10577, 'M', '𐖞'), - (0x10578, 'M', '𐖟'), - (0x10579, 'M', '𐖠'), - (0x1057A, 'M', '𐖡'), - (0x1057B, 'X'), - (0x1057C, 'M', '𐖣'), - (0x1057D, 'M', '𐖤'), - (0x1057E, 'M', '𐖥'), - (0x1057F, 'M', '𐖦'), - (0x10580, 'M', '𐖧'), - (0x10581, 'M', '𐖨'), - (0x10582, 'M', '𐖩'), - (0x10583, 'M', '𐖪'), - (0x10584, 'M', '𐖫'), - (0x10585, 'M', '𐖬'), - (0x10586, 'M', '𐖭'), - (0x10587, 'M', '𐖮'), - (0x10588, 'M', '𐖯'), - (0x10589, 'M', '𐖰'), - (0x1058A, 'M', '𐖱'), - (0x1058B, 'X'), - (0x1058C, 'M', '𐖳'), - (0x1058D, 'M', '𐖴'), - (0x1058E, 'M', '𐖵'), - (0x1058F, 'M', '𐖶'), - (0x10590, 'M', '𐖷'), - (0x10591, 'M', '𐖸'), - (0x10592, 'M', '𐖹'), - (0x10593, 'X'), - (0x10594, 'M', '𐖻'), - (0x10595, 'M', '𐖼'), - (0x10596, 'X'), - (0x10597, 'V'), - (0x105A2, 'X'), - (0x105A3, 'V'), - (0x105B2, 'X'), - (0x105B3, 'V'), - (0x105BA, 'X'), - (0x105BB, 'V'), - (0x105BD, 'X'), - (0x10600, 'V'), - (0x10737, 'X'), - (0x10740, 'V'), - (0x10756, 'X'), - (0x10760, 'V'), - (0x10768, 'X'), - (0x10780, 'V'), - (0x10781, 'M', 'ː'), - (0x10782, 'M', 'ˑ'), - (0x10783, 'M', 'æ'), - (0x10784, 'M', 'ʙ'), - (0x10785, 'M', 'ɓ'), - (0x10786, 'X'), - (0x10787, 'M', 'ʣ'), - (0x10788, 'M', 'ꭦ'), - (0x10789, 'M', 'ʥ'), - (0x1078A, 'M', 'ʤ'), - (0x1078B, 'M', 'ɖ'), - (0x1078C, 'M', 'ɗ'), - ] - -def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1078D, 'M', 'ᶑ'), - (0x1078E, 'M', 'ɘ'), - (0x1078F, 'M', 'ɞ'), - (0x10790, 'M', 'ʩ'), - (0x10791, 'M', 'ɤ'), - (0x10792, 'M', 'ɢ'), - (0x10793, 'M', 'ɠ'), - (0x10794, 'M', 'ʛ'), - (0x10795, 'M', 'ħ'), - (0x10796, 'M', 'ʜ'), - (0x10797, 'M', 'ɧ'), - (0x10798, 'M', 'ʄ'), - (0x10799, 'M', 'ʪ'), - (0x1079A, 'M', 'ʫ'), - (0x1079B, 'M', 'ɬ'), - (0x1079C, 'M', '𝼄'), - (0x1079D, 'M', 'ꞎ'), - (0x1079E, 'M', 'ɮ'), - (0x1079F, 'M', '𝼅'), - (0x107A0, 'M', 'ʎ'), - (0x107A1, 'M', '𝼆'), - (0x107A2, 'M', 'ø'), - (0x107A3, 'M', 'ɶ'), - (0x107A4, 'M', 'ɷ'), - (0x107A5, 'M', 'q'), - (0x107A6, 'M', 'ɺ'), - (0x107A7, 'M', '𝼈'), - (0x107A8, 'M', 'ɽ'), - (0x107A9, 'M', 'ɾ'), - (0x107AA, 'M', 'ʀ'), - (0x107AB, 'M', 'ʨ'), - (0x107AC, 'M', 'ʦ'), - (0x107AD, 'M', 'ꭧ'), - (0x107AE, 'M', 'ʧ'), - (0x107AF, 'M', 'ʈ'), - (0x107B0, 'M', 'ⱱ'), - (0x107B1, 'X'), - (0x107B2, 'M', 'ʏ'), - (0x107B3, 'M', 'ʡ'), - (0x107B4, 'M', 'ʢ'), - (0x107B5, 'M', 'ʘ'), - (0x107B6, 'M', 'ǀ'), - (0x107B7, 'M', 'ǁ'), - (0x107B8, 'M', 'ǂ'), - (0x107B9, 'M', '𝼊'), - (0x107BA, 'M', '𝼞'), - (0x107BB, 'X'), - (0x10800, 'V'), - (0x10806, 'X'), - (0x10808, 'V'), - (0x10809, 'X'), - (0x1080A, 'V'), - (0x10836, 'X'), - (0x10837, 'V'), - (0x10839, 'X'), - (0x1083C, 'V'), - (0x1083D, 'X'), - (0x1083F, 'V'), - (0x10856, 'X'), - (0x10857, 'V'), - (0x1089F, 'X'), - (0x108A7, 'V'), - (0x108B0, 'X'), - (0x108E0, 'V'), - (0x108F3, 'X'), - (0x108F4, 'V'), - (0x108F6, 'X'), - (0x108FB, 'V'), - (0x1091C, 'X'), - (0x1091F, 'V'), - (0x1093A, 'X'), - (0x1093F, 'V'), - (0x10940, 'X'), - (0x10980, 'V'), - (0x109B8, 'X'), - (0x109BC, 'V'), - (0x109D0, 'X'), - (0x109D2, 'V'), - (0x10A04, 'X'), - (0x10A05, 'V'), - (0x10A07, 'X'), - (0x10A0C, 'V'), - (0x10A14, 'X'), - (0x10A15, 'V'), - (0x10A18, 'X'), - (0x10A19, 'V'), - (0x10A36, 'X'), - (0x10A38, 'V'), - (0x10A3B, 'X'), - (0x10A3F, 'V'), - (0x10A49, 'X'), - (0x10A50, 'V'), - (0x10A59, 'X'), - (0x10A60, 'V'), - (0x10AA0, 'X'), - (0x10AC0, 'V'), - (0x10AE7, 'X'), - (0x10AEB, 'V'), - (0x10AF7, 'X'), - (0x10B00, 'V'), - ] - -def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x10B36, 'X'), - (0x10B39, 'V'), - (0x10B56, 'X'), - (0x10B58, 'V'), - (0x10B73, 'X'), - (0x10B78, 'V'), - (0x10B92, 'X'), - (0x10B99, 'V'), - (0x10B9D, 'X'), - (0x10BA9, 'V'), - (0x10BB0, 'X'), - (0x10C00, 'V'), - (0x10C49, 'X'), - (0x10C80, 'M', '𐳀'), - (0x10C81, 'M', '𐳁'), - (0x10C82, 'M', '𐳂'), - (0x10C83, 'M', '𐳃'), - (0x10C84, 'M', '𐳄'), - (0x10C85, 'M', '𐳅'), - (0x10C86, 'M', '𐳆'), - (0x10C87, 'M', '𐳇'), - (0x10C88, 'M', '𐳈'), - (0x10C89, 'M', '𐳉'), - (0x10C8A, 'M', '𐳊'), - (0x10C8B, 'M', '𐳋'), - (0x10C8C, 'M', '𐳌'), - (0x10C8D, 'M', '𐳍'), - (0x10C8E, 'M', '𐳎'), - (0x10C8F, 'M', '𐳏'), - (0x10C90, 'M', '𐳐'), - (0x10C91, 'M', '𐳑'), - (0x10C92, 'M', '𐳒'), - (0x10C93, 'M', '𐳓'), - (0x10C94, 'M', '𐳔'), - (0x10C95, 'M', '𐳕'), - (0x10C96, 'M', '𐳖'), - (0x10C97, 'M', '𐳗'), - (0x10C98, 'M', '𐳘'), - (0x10C99, 'M', '𐳙'), - (0x10C9A, 'M', '𐳚'), - (0x10C9B, 'M', '𐳛'), - (0x10C9C, 'M', '𐳜'), - (0x10C9D, 'M', '𐳝'), - (0x10C9E, 'M', '𐳞'), - (0x10C9F, 'M', '𐳟'), - (0x10CA0, 'M', '𐳠'), - (0x10CA1, 'M', '𐳡'), - (0x10CA2, 'M', '𐳢'), - (0x10CA3, 'M', '𐳣'), - (0x10CA4, 'M', '𐳤'), - (0x10CA5, 'M', '𐳥'), - (0x10CA6, 'M', '𐳦'), - (0x10CA7, 'M', '𐳧'), - (0x10CA8, 'M', '𐳨'), - (0x10CA9, 'M', '𐳩'), - (0x10CAA, 'M', '𐳪'), - (0x10CAB, 'M', '𐳫'), - (0x10CAC, 'M', '𐳬'), - (0x10CAD, 'M', '𐳭'), - (0x10CAE, 'M', '𐳮'), - (0x10CAF, 'M', '𐳯'), - (0x10CB0, 'M', '𐳰'), - (0x10CB1, 'M', '𐳱'), - (0x10CB2, 'M', '𐳲'), - (0x10CB3, 'X'), - (0x10CC0, 'V'), - (0x10CF3, 'X'), - (0x10CFA, 'V'), - (0x10D28, 'X'), - (0x10D30, 'V'), - (0x10D3A, 'X'), - (0x10E60, 'V'), - (0x10E7F, 'X'), - (0x10E80, 'V'), - (0x10EAA, 'X'), - (0x10EAB, 'V'), - (0x10EAE, 'X'), - (0x10EB0, 'V'), - (0x10EB2, 'X'), - (0x10EFD, 'V'), - (0x10F28, 'X'), - (0x10F30, 'V'), - (0x10F5A, 'X'), - (0x10F70, 'V'), - (0x10F8A, 'X'), - (0x10FB0, 'V'), - (0x10FCC, 'X'), - (0x10FE0, 'V'), - (0x10FF7, 'X'), - (0x11000, 'V'), - (0x1104E, 'X'), - (0x11052, 'V'), - (0x11076, 'X'), - (0x1107F, 'V'), - (0x110BD, 'X'), - (0x110BE, 'V'), - (0x110C3, 'X'), - (0x110D0, 'V'), - (0x110E9, 'X'), - (0x110F0, 'V'), - ] - -def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x110FA, 'X'), - (0x11100, 'V'), - (0x11135, 'X'), - (0x11136, 'V'), - (0x11148, 'X'), - (0x11150, 'V'), - (0x11177, 'X'), - (0x11180, 'V'), - (0x111E0, 'X'), - (0x111E1, 'V'), - (0x111F5, 'X'), - (0x11200, 'V'), - (0x11212, 'X'), - (0x11213, 'V'), - (0x11242, 'X'), - (0x11280, 'V'), - (0x11287, 'X'), - (0x11288, 'V'), - (0x11289, 'X'), - (0x1128A, 'V'), - (0x1128E, 'X'), - (0x1128F, 'V'), - (0x1129E, 'X'), - (0x1129F, 'V'), - (0x112AA, 'X'), - (0x112B0, 'V'), - (0x112EB, 'X'), - (0x112F0, 'V'), - (0x112FA, 'X'), - (0x11300, 'V'), - (0x11304, 'X'), - (0x11305, 'V'), - (0x1130D, 'X'), - (0x1130F, 'V'), - (0x11311, 'X'), - (0x11313, 'V'), - (0x11329, 'X'), - (0x1132A, 'V'), - (0x11331, 'X'), - (0x11332, 'V'), - (0x11334, 'X'), - (0x11335, 'V'), - (0x1133A, 'X'), - (0x1133B, 'V'), - (0x11345, 'X'), - (0x11347, 'V'), - (0x11349, 'X'), - (0x1134B, 'V'), - (0x1134E, 'X'), - (0x11350, 'V'), - (0x11351, 'X'), - (0x11357, 'V'), - (0x11358, 'X'), - (0x1135D, 'V'), - (0x11364, 'X'), - (0x11366, 'V'), - (0x1136D, 'X'), - (0x11370, 'V'), - (0x11375, 'X'), - (0x11400, 'V'), - (0x1145C, 'X'), - (0x1145D, 'V'), - (0x11462, 'X'), - (0x11480, 'V'), - (0x114C8, 'X'), - (0x114D0, 'V'), - (0x114DA, 'X'), - (0x11580, 'V'), - (0x115B6, 'X'), - (0x115B8, 'V'), - (0x115DE, 'X'), - (0x11600, 'V'), - (0x11645, 'X'), - (0x11650, 'V'), - (0x1165A, 'X'), - (0x11660, 'V'), - (0x1166D, 'X'), - (0x11680, 'V'), - (0x116BA, 'X'), - (0x116C0, 'V'), - (0x116CA, 'X'), - (0x11700, 'V'), - (0x1171B, 'X'), - (0x1171D, 'V'), - (0x1172C, 'X'), - (0x11730, 'V'), - (0x11747, 'X'), - (0x11800, 'V'), - (0x1183C, 'X'), - (0x118A0, 'M', '𑣀'), - (0x118A1, 'M', '𑣁'), - (0x118A2, 'M', '𑣂'), - (0x118A3, 'M', '𑣃'), - (0x118A4, 'M', '𑣄'), - (0x118A5, 'M', '𑣅'), - (0x118A6, 'M', '𑣆'), - (0x118A7, 'M', '𑣇'), - (0x118A8, 'M', '𑣈'), - (0x118A9, 'M', '𑣉'), - (0x118AA, 'M', '𑣊'), - ] - -def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x118AB, 'M', '𑣋'), - (0x118AC, 'M', '𑣌'), - (0x118AD, 'M', '𑣍'), - (0x118AE, 'M', '𑣎'), - (0x118AF, 'M', '𑣏'), - (0x118B0, 'M', '𑣐'), - (0x118B1, 'M', '𑣑'), - (0x118B2, 'M', '𑣒'), - (0x118B3, 'M', '𑣓'), - (0x118B4, 'M', '𑣔'), - (0x118B5, 'M', '𑣕'), - (0x118B6, 'M', '𑣖'), - (0x118B7, 'M', '𑣗'), - (0x118B8, 'M', '𑣘'), - (0x118B9, 'M', '𑣙'), - (0x118BA, 'M', '𑣚'), - (0x118BB, 'M', '𑣛'), - (0x118BC, 'M', '𑣜'), - (0x118BD, 'M', '𑣝'), - (0x118BE, 'M', '𑣞'), - (0x118BF, 'M', '𑣟'), - (0x118C0, 'V'), - (0x118F3, 'X'), - (0x118FF, 'V'), - (0x11907, 'X'), - (0x11909, 'V'), - (0x1190A, 'X'), - (0x1190C, 'V'), - (0x11914, 'X'), - (0x11915, 'V'), - (0x11917, 'X'), - (0x11918, 'V'), - (0x11936, 'X'), - (0x11937, 'V'), - (0x11939, 'X'), - (0x1193B, 'V'), - (0x11947, 'X'), - (0x11950, 'V'), - (0x1195A, 'X'), - (0x119A0, 'V'), - (0x119A8, 'X'), - (0x119AA, 'V'), - (0x119D8, 'X'), - (0x119DA, 'V'), - (0x119E5, 'X'), - (0x11A00, 'V'), - (0x11A48, 'X'), - (0x11A50, 'V'), - (0x11AA3, 'X'), - (0x11AB0, 'V'), - (0x11AF9, 'X'), - (0x11B00, 'V'), - (0x11B0A, 'X'), - (0x11C00, 'V'), - (0x11C09, 'X'), - (0x11C0A, 'V'), - (0x11C37, 'X'), - (0x11C38, 'V'), - (0x11C46, 'X'), - (0x11C50, 'V'), - (0x11C6D, 'X'), - (0x11C70, 'V'), - (0x11C90, 'X'), - (0x11C92, 'V'), - (0x11CA8, 'X'), - (0x11CA9, 'V'), - (0x11CB7, 'X'), - (0x11D00, 'V'), - (0x11D07, 'X'), - (0x11D08, 'V'), - (0x11D0A, 'X'), - (0x11D0B, 'V'), - (0x11D37, 'X'), - (0x11D3A, 'V'), - (0x11D3B, 'X'), - (0x11D3C, 'V'), - (0x11D3E, 'X'), - (0x11D3F, 'V'), - (0x11D48, 'X'), - (0x11D50, 'V'), - (0x11D5A, 'X'), - (0x11D60, 'V'), - (0x11D66, 'X'), - (0x11D67, 'V'), - (0x11D69, 'X'), - (0x11D6A, 'V'), - (0x11D8F, 'X'), - (0x11D90, 'V'), - (0x11D92, 'X'), - (0x11D93, 'V'), - (0x11D99, 'X'), - (0x11DA0, 'V'), - (0x11DAA, 'X'), - (0x11EE0, 'V'), - (0x11EF9, 'X'), - (0x11F00, 'V'), - (0x11F11, 'X'), - (0x11F12, 'V'), - (0x11F3B, 'X'), - (0x11F3E, 'V'), - ] - -def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x11F5A, 'X'), - (0x11FB0, 'V'), - (0x11FB1, 'X'), - (0x11FC0, 'V'), - (0x11FF2, 'X'), - (0x11FFF, 'V'), - (0x1239A, 'X'), - (0x12400, 'V'), - (0x1246F, 'X'), - (0x12470, 'V'), - (0x12475, 'X'), - (0x12480, 'V'), - (0x12544, 'X'), - (0x12F90, 'V'), - (0x12FF3, 'X'), - (0x13000, 'V'), - (0x13430, 'X'), - (0x13440, 'V'), - (0x13456, 'X'), - (0x14400, 'V'), - (0x14647, 'X'), - (0x16800, 'V'), - (0x16A39, 'X'), - (0x16A40, 'V'), - (0x16A5F, 'X'), - (0x16A60, 'V'), - (0x16A6A, 'X'), - (0x16A6E, 'V'), - (0x16ABF, 'X'), - (0x16AC0, 'V'), - (0x16ACA, 'X'), - (0x16AD0, 'V'), - (0x16AEE, 'X'), - (0x16AF0, 'V'), - (0x16AF6, 'X'), - (0x16B00, 'V'), - (0x16B46, 'X'), - (0x16B50, 'V'), - (0x16B5A, 'X'), - (0x16B5B, 'V'), - (0x16B62, 'X'), - (0x16B63, 'V'), - (0x16B78, 'X'), - (0x16B7D, 'V'), - (0x16B90, 'X'), - (0x16E40, 'M', '𖹠'), - (0x16E41, 'M', '𖹡'), - (0x16E42, 'M', '𖹢'), - (0x16E43, 'M', '𖹣'), - (0x16E44, 'M', '𖹤'), - (0x16E45, 'M', '𖹥'), - (0x16E46, 'M', '𖹦'), - (0x16E47, 'M', '𖹧'), - (0x16E48, 'M', '𖹨'), - (0x16E49, 'M', '𖹩'), - (0x16E4A, 'M', '𖹪'), - (0x16E4B, 'M', '𖹫'), - (0x16E4C, 'M', '𖹬'), - (0x16E4D, 'M', '𖹭'), - (0x16E4E, 'M', '𖹮'), - (0x16E4F, 'M', '𖹯'), - (0x16E50, 'M', '𖹰'), - (0x16E51, 'M', '𖹱'), - (0x16E52, 'M', '𖹲'), - (0x16E53, 'M', '𖹳'), - (0x16E54, 'M', '𖹴'), - (0x16E55, 'M', '𖹵'), - (0x16E56, 'M', '𖹶'), - (0x16E57, 'M', '𖹷'), - (0x16E58, 'M', '𖹸'), - (0x16E59, 'M', '𖹹'), - (0x16E5A, 'M', '𖹺'), - (0x16E5B, 'M', '𖹻'), - (0x16E5C, 'M', '𖹼'), - (0x16E5D, 'M', '𖹽'), - (0x16E5E, 'M', '𖹾'), - (0x16E5F, 'M', '𖹿'), - (0x16E60, 'V'), - (0x16E9B, 'X'), - (0x16F00, 'V'), - (0x16F4B, 'X'), - (0x16F4F, 'V'), - (0x16F88, 'X'), - (0x16F8F, 'V'), - (0x16FA0, 'X'), - (0x16FE0, 'V'), - (0x16FE5, 'X'), - (0x16FF0, 'V'), - (0x16FF2, 'X'), - (0x17000, 'V'), - (0x187F8, 'X'), - (0x18800, 'V'), - (0x18CD6, 'X'), - (0x18D00, 'V'), - (0x18D09, 'X'), - (0x1AFF0, 'V'), - (0x1AFF4, 'X'), - (0x1AFF5, 'V'), - (0x1AFFC, 'X'), - (0x1AFFD, 'V'), - ] - -def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1AFFF, 'X'), - (0x1B000, 'V'), - (0x1B123, 'X'), - (0x1B132, 'V'), - (0x1B133, 'X'), - (0x1B150, 'V'), - (0x1B153, 'X'), - (0x1B155, 'V'), - (0x1B156, 'X'), - (0x1B164, 'V'), - (0x1B168, 'X'), - (0x1B170, 'V'), - (0x1B2FC, 'X'), - (0x1BC00, 'V'), - (0x1BC6B, 'X'), - (0x1BC70, 'V'), - (0x1BC7D, 'X'), - (0x1BC80, 'V'), - (0x1BC89, 'X'), - (0x1BC90, 'V'), - (0x1BC9A, 'X'), - (0x1BC9C, 'V'), - (0x1BCA0, 'I'), - (0x1BCA4, 'X'), - (0x1CF00, 'V'), - (0x1CF2E, 'X'), - (0x1CF30, 'V'), - (0x1CF47, 'X'), - (0x1CF50, 'V'), - (0x1CFC4, 'X'), - (0x1D000, 'V'), - (0x1D0F6, 'X'), - (0x1D100, 'V'), - (0x1D127, 'X'), - (0x1D129, 'V'), - (0x1D15E, 'M', '𝅗𝅥'), - (0x1D15F, 'M', '𝅘𝅥'), - (0x1D160, 'M', '𝅘𝅥𝅮'), - (0x1D161, 'M', '𝅘𝅥𝅯'), - (0x1D162, 'M', '𝅘𝅥𝅰'), - (0x1D163, 'M', '𝅘𝅥𝅱'), - (0x1D164, 'M', '𝅘𝅥𝅲'), - (0x1D165, 'V'), - (0x1D173, 'X'), - (0x1D17B, 'V'), - (0x1D1BB, 'M', '𝆹𝅥'), - (0x1D1BC, 'M', '𝆺𝅥'), - (0x1D1BD, 'M', '𝆹𝅥𝅮'), - (0x1D1BE, 'M', '𝆺𝅥𝅮'), - (0x1D1BF, 'M', '𝆹𝅥𝅯'), - (0x1D1C0, 'M', '𝆺𝅥𝅯'), - (0x1D1C1, 'V'), - (0x1D1EB, 'X'), - (0x1D200, 'V'), - (0x1D246, 'X'), - (0x1D2C0, 'V'), - (0x1D2D4, 'X'), - (0x1D2E0, 'V'), - (0x1D2F4, 'X'), - (0x1D300, 'V'), - (0x1D357, 'X'), - (0x1D360, 'V'), - (0x1D379, 'X'), - (0x1D400, 'M', 'a'), - (0x1D401, 'M', 'b'), - (0x1D402, 'M', 'c'), - (0x1D403, 'M', 'd'), - (0x1D404, 'M', 'e'), - (0x1D405, 'M', 'f'), - (0x1D406, 'M', 'g'), - (0x1D407, 'M', 'h'), - (0x1D408, 'M', 'i'), - (0x1D409, 'M', 'j'), - (0x1D40A, 'M', 'k'), - (0x1D40B, 'M', 'l'), - (0x1D40C, 'M', 'm'), - (0x1D40D, 'M', 'n'), - (0x1D40E, 'M', 'o'), - (0x1D40F, 'M', 'p'), - (0x1D410, 'M', 'q'), - (0x1D411, 'M', 'r'), - (0x1D412, 'M', 's'), - (0x1D413, 'M', 't'), - (0x1D414, 'M', 'u'), - (0x1D415, 'M', 'v'), - (0x1D416, 'M', 'w'), - (0x1D417, 'M', 'x'), - (0x1D418, 'M', 'y'), - (0x1D419, 'M', 'z'), - (0x1D41A, 'M', 'a'), - (0x1D41B, 'M', 'b'), - (0x1D41C, 'M', 'c'), - (0x1D41D, 'M', 'd'), - (0x1D41E, 'M', 'e'), - (0x1D41F, 'M', 'f'), - (0x1D420, 'M', 'g'), - (0x1D421, 'M', 'h'), - (0x1D422, 'M', 'i'), - (0x1D423, 'M', 'j'), - (0x1D424, 'M', 'k'), - ] - -def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D425, 'M', 'l'), - (0x1D426, 'M', 'm'), - (0x1D427, 'M', 'n'), - (0x1D428, 'M', 'o'), - (0x1D429, 'M', 'p'), - (0x1D42A, 'M', 'q'), - (0x1D42B, 'M', 'r'), - (0x1D42C, 'M', 's'), - (0x1D42D, 'M', 't'), - (0x1D42E, 'M', 'u'), - (0x1D42F, 'M', 'v'), - (0x1D430, 'M', 'w'), - (0x1D431, 'M', 'x'), - (0x1D432, 'M', 'y'), - (0x1D433, 'M', 'z'), - (0x1D434, 'M', 'a'), - (0x1D435, 'M', 'b'), - (0x1D436, 'M', 'c'), - (0x1D437, 'M', 'd'), - (0x1D438, 'M', 'e'), - (0x1D439, 'M', 'f'), - (0x1D43A, 'M', 'g'), - (0x1D43B, 'M', 'h'), - (0x1D43C, 'M', 'i'), - (0x1D43D, 'M', 'j'), - (0x1D43E, 'M', 'k'), - (0x1D43F, 'M', 'l'), - (0x1D440, 'M', 'm'), - (0x1D441, 'M', 'n'), - (0x1D442, 'M', 'o'), - (0x1D443, 'M', 'p'), - (0x1D444, 'M', 'q'), - (0x1D445, 'M', 'r'), - (0x1D446, 'M', 's'), - (0x1D447, 'M', 't'), - (0x1D448, 'M', 'u'), - (0x1D449, 'M', 'v'), - (0x1D44A, 'M', 'w'), - (0x1D44B, 'M', 'x'), - (0x1D44C, 'M', 'y'), - (0x1D44D, 'M', 'z'), - (0x1D44E, 'M', 'a'), - (0x1D44F, 'M', 'b'), - (0x1D450, 'M', 'c'), - (0x1D451, 'M', 'd'), - (0x1D452, 'M', 'e'), - (0x1D453, 'M', 'f'), - (0x1D454, 'M', 'g'), - (0x1D455, 'X'), - (0x1D456, 'M', 'i'), - (0x1D457, 'M', 'j'), - (0x1D458, 'M', 'k'), - (0x1D459, 'M', 'l'), - (0x1D45A, 'M', 'm'), - (0x1D45B, 'M', 'n'), - (0x1D45C, 'M', 'o'), - (0x1D45D, 'M', 'p'), - (0x1D45E, 'M', 'q'), - (0x1D45F, 'M', 'r'), - (0x1D460, 'M', 's'), - (0x1D461, 'M', 't'), - (0x1D462, 'M', 'u'), - (0x1D463, 'M', 'v'), - (0x1D464, 'M', 'w'), - (0x1D465, 'M', 'x'), - (0x1D466, 'M', 'y'), - (0x1D467, 'M', 'z'), - (0x1D468, 'M', 'a'), - (0x1D469, 'M', 'b'), - (0x1D46A, 'M', 'c'), - (0x1D46B, 'M', 'd'), - (0x1D46C, 'M', 'e'), - (0x1D46D, 'M', 'f'), - (0x1D46E, 'M', 'g'), - (0x1D46F, 'M', 'h'), - (0x1D470, 'M', 'i'), - (0x1D471, 'M', 'j'), - (0x1D472, 'M', 'k'), - (0x1D473, 'M', 'l'), - (0x1D474, 'M', 'm'), - (0x1D475, 'M', 'n'), - (0x1D476, 'M', 'o'), - (0x1D477, 'M', 'p'), - (0x1D478, 'M', 'q'), - (0x1D479, 'M', 'r'), - (0x1D47A, 'M', 's'), - (0x1D47B, 'M', 't'), - (0x1D47C, 'M', 'u'), - (0x1D47D, 'M', 'v'), - (0x1D47E, 'M', 'w'), - (0x1D47F, 'M', 'x'), - (0x1D480, 'M', 'y'), - (0x1D481, 'M', 'z'), - (0x1D482, 'M', 'a'), - (0x1D483, 'M', 'b'), - (0x1D484, 'M', 'c'), - (0x1D485, 'M', 'd'), - (0x1D486, 'M', 'e'), - (0x1D487, 'M', 'f'), - (0x1D488, 'M', 'g'), - ] - -def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D489, 'M', 'h'), - (0x1D48A, 'M', 'i'), - (0x1D48B, 'M', 'j'), - (0x1D48C, 'M', 'k'), - (0x1D48D, 'M', 'l'), - (0x1D48E, 'M', 'm'), - (0x1D48F, 'M', 'n'), - (0x1D490, 'M', 'o'), - (0x1D491, 'M', 'p'), - (0x1D492, 'M', 'q'), - (0x1D493, 'M', 'r'), - (0x1D494, 'M', 's'), - (0x1D495, 'M', 't'), - (0x1D496, 'M', 'u'), - (0x1D497, 'M', 'v'), - (0x1D498, 'M', 'w'), - (0x1D499, 'M', 'x'), - (0x1D49A, 'M', 'y'), - (0x1D49B, 'M', 'z'), - (0x1D49C, 'M', 'a'), - (0x1D49D, 'X'), - (0x1D49E, 'M', 'c'), - (0x1D49F, 'M', 'd'), - (0x1D4A0, 'X'), - (0x1D4A2, 'M', 'g'), - (0x1D4A3, 'X'), - (0x1D4A5, 'M', 'j'), - (0x1D4A6, 'M', 'k'), - (0x1D4A7, 'X'), - (0x1D4A9, 'M', 'n'), - (0x1D4AA, 'M', 'o'), - (0x1D4AB, 'M', 'p'), - (0x1D4AC, 'M', 'q'), - (0x1D4AD, 'X'), - (0x1D4AE, 'M', 's'), - (0x1D4AF, 'M', 't'), - (0x1D4B0, 'M', 'u'), - (0x1D4B1, 'M', 'v'), - (0x1D4B2, 'M', 'w'), - (0x1D4B3, 'M', 'x'), - (0x1D4B4, 'M', 'y'), - (0x1D4B5, 'M', 'z'), - (0x1D4B6, 'M', 'a'), - (0x1D4B7, 'M', 'b'), - (0x1D4B8, 'M', 'c'), - (0x1D4B9, 'M', 'd'), - (0x1D4BA, 'X'), - (0x1D4BB, 'M', 'f'), - (0x1D4BC, 'X'), - (0x1D4BD, 'M', 'h'), - (0x1D4BE, 'M', 'i'), - (0x1D4BF, 'M', 'j'), - (0x1D4C0, 'M', 'k'), - (0x1D4C1, 'M', 'l'), - (0x1D4C2, 'M', 'm'), - (0x1D4C3, 'M', 'n'), - (0x1D4C4, 'X'), - (0x1D4C5, 'M', 'p'), - (0x1D4C6, 'M', 'q'), - (0x1D4C7, 'M', 'r'), - (0x1D4C8, 'M', 's'), - (0x1D4C9, 'M', 't'), - (0x1D4CA, 'M', 'u'), - (0x1D4CB, 'M', 'v'), - (0x1D4CC, 'M', 'w'), - (0x1D4CD, 'M', 'x'), - (0x1D4CE, 'M', 'y'), - (0x1D4CF, 'M', 'z'), - (0x1D4D0, 'M', 'a'), - (0x1D4D1, 'M', 'b'), - (0x1D4D2, 'M', 'c'), - (0x1D4D3, 'M', 'd'), - (0x1D4D4, 'M', 'e'), - (0x1D4D5, 'M', 'f'), - (0x1D4D6, 'M', 'g'), - (0x1D4D7, 'M', 'h'), - (0x1D4D8, 'M', 'i'), - (0x1D4D9, 'M', 'j'), - (0x1D4DA, 'M', 'k'), - (0x1D4DB, 'M', 'l'), - (0x1D4DC, 'M', 'm'), - (0x1D4DD, 'M', 'n'), - (0x1D4DE, 'M', 'o'), - (0x1D4DF, 'M', 'p'), - (0x1D4E0, 'M', 'q'), - (0x1D4E1, 'M', 'r'), - (0x1D4E2, 'M', 's'), - (0x1D4E3, 'M', 't'), - (0x1D4E4, 'M', 'u'), - (0x1D4E5, 'M', 'v'), - (0x1D4E6, 'M', 'w'), - (0x1D4E7, 'M', 'x'), - (0x1D4E8, 'M', 'y'), - (0x1D4E9, 'M', 'z'), - (0x1D4EA, 'M', 'a'), - (0x1D4EB, 'M', 'b'), - (0x1D4EC, 'M', 'c'), - (0x1D4ED, 'M', 'd'), - (0x1D4EE, 'M', 'e'), - (0x1D4EF, 'M', 'f'), - ] - -def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D4F0, 'M', 'g'), - (0x1D4F1, 'M', 'h'), - (0x1D4F2, 'M', 'i'), - (0x1D4F3, 'M', 'j'), - (0x1D4F4, 'M', 'k'), - (0x1D4F5, 'M', 'l'), - (0x1D4F6, 'M', 'm'), - (0x1D4F7, 'M', 'n'), - (0x1D4F8, 'M', 'o'), - (0x1D4F9, 'M', 'p'), - (0x1D4FA, 'M', 'q'), - (0x1D4FB, 'M', 'r'), - (0x1D4FC, 'M', 's'), - (0x1D4FD, 'M', 't'), - (0x1D4FE, 'M', 'u'), - (0x1D4FF, 'M', 'v'), - (0x1D500, 'M', 'w'), - (0x1D501, 'M', 'x'), - (0x1D502, 'M', 'y'), - (0x1D503, 'M', 'z'), - (0x1D504, 'M', 'a'), - (0x1D505, 'M', 'b'), - (0x1D506, 'X'), - (0x1D507, 'M', 'd'), - (0x1D508, 'M', 'e'), - (0x1D509, 'M', 'f'), - (0x1D50A, 'M', 'g'), - (0x1D50B, 'X'), - (0x1D50D, 'M', 'j'), - (0x1D50E, 'M', 'k'), - (0x1D50F, 'M', 'l'), - (0x1D510, 'M', 'm'), - (0x1D511, 'M', 'n'), - (0x1D512, 'M', 'o'), - (0x1D513, 'M', 'p'), - (0x1D514, 'M', 'q'), - (0x1D515, 'X'), - (0x1D516, 'M', 's'), - (0x1D517, 'M', 't'), - (0x1D518, 'M', 'u'), - (0x1D519, 'M', 'v'), - (0x1D51A, 'M', 'w'), - (0x1D51B, 'M', 'x'), - (0x1D51C, 'M', 'y'), - (0x1D51D, 'X'), - (0x1D51E, 'M', 'a'), - (0x1D51F, 'M', 'b'), - (0x1D520, 'M', 'c'), - (0x1D521, 'M', 'd'), - (0x1D522, 'M', 'e'), - (0x1D523, 'M', 'f'), - (0x1D524, 'M', 'g'), - (0x1D525, 'M', 'h'), - (0x1D526, 'M', 'i'), - (0x1D527, 'M', 'j'), - (0x1D528, 'M', 'k'), - (0x1D529, 'M', 'l'), - (0x1D52A, 'M', 'm'), - (0x1D52B, 'M', 'n'), - (0x1D52C, 'M', 'o'), - (0x1D52D, 'M', 'p'), - (0x1D52E, 'M', 'q'), - (0x1D52F, 'M', 'r'), - (0x1D530, 'M', 's'), - (0x1D531, 'M', 't'), - (0x1D532, 'M', 'u'), - (0x1D533, 'M', 'v'), - (0x1D534, 'M', 'w'), - (0x1D535, 'M', 'x'), - (0x1D536, 'M', 'y'), - (0x1D537, 'M', 'z'), - (0x1D538, 'M', 'a'), - (0x1D539, 'M', 'b'), - (0x1D53A, 'X'), - (0x1D53B, 'M', 'd'), - (0x1D53C, 'M', 'e'), - (0x1D53D, 'M', 'f'), - (0x1D53E, 'M', 'g'), - (0x1D53F, 'X'), - (0x1D540, 'M', 'i'), - (0x1D541, 'M', 'j'), - (0x1D542, 'M', 'k'), - (0x1D543, 'M', 'l'), - (0x1D544, 'M', 'm'), - (0x1D545, 'X'), - (0x1D546, 'M', 'o'), - (0x1D547, 'X'), - (0x1D54A, 'M', 's'), - (0x1D54B, 'M', 't'), - (0x1D54C, 'M', 'u'), - (0x1D54D, 'M', 'v'), - (0x1D54E, 'M', 'w'), - (0x1D54F, 'M', 'x'), - (0x1D550, 'M', 'y'), - (0x1D551, 'X'), - (0x1D552, 'M', 'a'), - (0x1D553, 'M', 'b'), - (0x1D554, 'M', 'c'), - (0x1D555, 'M', 'd'), - (0x1D556, 'M', 'e'), - ] - -def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D557, 'M', 'f'), - (0x1D558, 'M', 'g'), - (0x1D559, 'M', 'h'), - (0x1D55A, 'M', 'i'), - (0x1D55B, 'M', 'j'), - (0x1D55C, 'M', 'k'), - (0x1D55D, 'M', 'l'), - (0x1D55E, 'M', 'm'), - (0x1D55F, 'M', 'n'), - (0x1D560, 'M', 'o'), - (0x1D561, 'M', 'p'), - (0x1D562, 'M', 'q'), - (0x1D563, 'M', 'r'), - (0x1D564, 'M', 's'), - (0x1D565, 'M', 't'), - (0x1D566, 'M', 'u'), - (0x1D567, 'M', 'v'), - (0x1D568, 'M', 'w'), - (0x1D569, 'M', 'x'), - (0x1D56A, 'M', 'y'), - (0x1D56B, 'M', 'z'), - (0x1D56C, 'M', 'a'), - (0x1D56D, 'M', 'b'), - (0x1D56E, 'M', 'c'), - (0x1D56F, 'M', 'd'), - (0x1D570, 'M', 'e'), - (0x1D571, 'M', 'f'), - (0x1D572, 'M', 'g'), - (0x1D573, 'M', 'h'), - (0x1D574, 'M', 'i'), - (0x1D575, 'M', 'j'), - (0x1D576, 'M', 'k'), - (0x1D577, 'M', 'l'), - (0x1D578, 'M', 'm'), - (0x1D579, 'M', 'n'), - (0x1D57A, 'M', 'o'), - (0x1D57B, 'M', 'p'), - (0x1D57C, 'M', 'q'), - (0x1D57D, 'M', 'r'), - (0x1D57E, 'M', 's'), - (0x1D57F, 'M', 't'), - (0x1D580, 'M', 'u'), - (0x1D581, 'M', 'v'), - (0x1D582, 'M', 'w'), - (0x1D583, 'M', 'x'), - (0x1D584, 'M', 'y'), - (0x1D585, 'M', 'z'), - (0x1D586, 'M', 'a'), - (0x1D587, 'M', 'b'), - (0x1D588, 'M', 'c'), - (0x1D589, 'M', 'd'), - (0x1D58A, 'M', 'e'), - (0x1D58B, 'M', 'f'), - (0x1D58C, 'M', 'g'), - (0x1D58D, 'M', 'h'), - (0x1D58E, 'M', 'i'), - (0x1D58F, 'M', 'j'), - (0x1D590, 'M', 'k'), - (0x1D591, 'M', 'l'), - (0x1D592, 'M', 'm'), - (0x1D593, 'M', 'n'), - (0x1D594, 'M', 'o'), - (0x1D595, 'M', 'p'), - (0x1D596, 'M', 'q'), - (0x1D597, 'M', 'r'), - (0x1D598, 'M', 's'), - (0x1D599, 'M', 't'), - (0x1D59A, 'M', 'u'), - (0x1D59B, 'M', 'v'), - (0x1D59C, 'M', 'w'), - (0x1D59D, 'M', 'x'), - (0x1D59E, 'M', 'y'), - (0x1D59F, 'M', 'z'), - (0x1D5A0, 'M', 'a'), - (0x1D5A1, 'M', 'b'), - (0x1D5A2, 'M', 'c'), - (0x1D5A3, 'M', 'd'), - (0x1D5A4, 'M', 'e'), - (0x1D5A5, 'M', 'f'), - (0x1D5A6, 'M', 'g'), - (0x1D5A7, 'M', 'h'), - (0x1D5A8, 'M', 'i'), - (0x1D5A9, 'M', 'j'), - (0x1D5AA, 'M', 'k'), - (0x1D5AB, 'M', 'l'), - (0x1D5AC, 'M', 'm'), - (0x1D5AD, 'M', 'n'), - (0x1D5AE, 'M', 'o'), - (0x1D5AF, 'M', 'p'), - (0x1D5B0, 'M', 'q'), - (0x1D5B1, 'M', 'r'), - (0x1D5B2, 'M', 's'), - (0x1D5B3, 'M', 't'), - (0x1D5B4, 'M', 'u'), - (0x1D5B5, 'M', 'v'), - (0x1D5B6, 'M', 'w'), - (0x1D5B7, 'M', 'x'), - (0x1D5B8, 'M', 'y'), - (0x1D5B9, 'M', 'z'), - (0x1D5BA, 'M', 'a'), - ] - -def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D5BB, 'M', 'b'), - (0x1D5BC, 'M', 'c'), - (0x1D5BD, 'M', 'd'), - (0x1D5BE, 'M', 'e'), - (0x1D5BF, 'M', 'f'), - (0x1D5C0, 'M', 'g'), - (0x1D5C1, 'M', 'h'), - (0x1D5C2, 'M', 'i'), - (0x1D5C3, 'M', 'j'), - (0x1D5C4, 'M', 'k'), - (0x1D5C5, 'M', 'l'), - (0x1D5C6, 'M', 'm'), - (0x1D5C7, 'M', 'n'), - (0x1D5C8, 'M', 'o'), - (0x1D5C9, 'M', 'p'), - (0x1D5CA, 'M', 'q'), - (0x1D5CB, 'M', 'r'), - (0x1D5CC, 'M', 's'), - (0x1D5CD, 'M', 't'), - (0x1D5CE, 'M', 'u'), - (0x1D5CF, 'M', 'v'), - (0x1D5D0, 'M', 'w'), - (0x1D5D1, 'M', 'x'), - (0x1D5D2, 'M', 'y'), - (0x1D5D3, 'M', 'z'), - (0x1D5D4, 'M', 'a'), - (0x1D5D5, 'M', 'b'), - (0x1D5D6, 'M', 'c'), - (0x1D5D7, 'M', 'd'), - (0x1D5D8, 'M', 'e'), - (0x1D5D9, 'M', 'f'), - (0x1D5DA, 'M', 'g'), - (0x1D5DB, 'M', 'h'), - (0x1D5DC, 'M', 'i'), - (0x1D5DD, 'M', 'j'), - (0x1D5DE, 'M', 'k'), - (0x1D5DF, 'M', 'l'), - (0x1D5E0, 'M', 'm'), - (0x1D5E1, 'M', 'n'), - (0x1D5E2, 'M', 'o'), - (0x1D5E3, 'M', 'p'), - (0x1D5E4, 'M', 'q'), - (0x1D5E5, 'M', 'r'), - (0x1D5E6, 'M', 's'), - (0x1D5E7, 'M', 't'), - (0x1D5E8, 'M', 'u'), - (0x1D5E9, 'M', 'v'), - (0x1D5EA, 'M', 'w'), - (0x1D5EB, 'M', 'x'), - (0x1D5EC, 'M', 'y'), - (0x1D5ED, 'M', 'z'), - (0x1D5EE, 'M', 'a'), - (0x1D5EF, 'M', 'b'), - (0x1D5F0, 'M', 'c'), - (0x1D5F1, 'M', 'd'), - (0x1D5F2, 'M', 'e'), - (0x1D5F3, 'M', 'f'), - (0x1D5F4, 'M', 'g'), - (0x1D5F5, 'M', 'h'), - (0x1D5F6, 'M', 'i'), - (0x1D5F7, 'M', 'j'), - (0x1D5F8, 'M', 'k'), - (0x1D5F9, 'M', 'l'), - (0x1D5FA, 'M', 'm'), - (0x1D5FB, 'M', 'n'), - (0x1D5FC, 'M', 'o'), - (0x1D5FD, 'M', 'p'), - (0x1D5FE, 'M', 'q'), - (0x1D5FF, 'M', 'r'), - (0x1D600, 'M', 's'), - (0x1D601, 'M', 't'), - (0x1D602, 'M', 'u'), - (0x1D603, 'M', 'v'), - (0x1D604, 'M', 'w'), - (0x1D605, 'M', 'x'), - (0x1D606, 'M', 'y'), - (0x1D607, 'M', 'z'), - (0x1D608, 'M', 'a'), - (0x1D609, 'M', 'b'), - (0x1D60A, 'M', 'c'), - (0x1D60B, 'M', 'd'), - (0x1D60C, 'M', 'e'), - (0x1D60D, 'M', 'f'), - (0x1D60E, 'M', 'g'), - (0x1D60F, 'M', 'h'), - (0x1D610, 'M', 'i'), - (0x1D611, 'M', 'j'), - (0x1D612, 'M', 'k'), - (0x1D613, 'M', 'l'), - (0x1D614, 'M', 'm'), - (0x1D615, 'M', 'n'), - (0x1D616, 'M', 'o'), - (0x1D617, 'M', 'p'), - (0x1D618, 'M', 'q'), - (0x1D619, 'M', 'r'), - (0x1D61A, 'M', 's'), - (0x1D61B, 'M', 't'), - (0x1D61C, 'M', 'u'), - (0x1D61D, 'M', 'v'), - (0x1D61E, 'M', 'w'), - ] - -def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D61F, 'M', 'x'), - (0x1D620, 'M', 'y'), - (0x1D621, 'M', 'z'), - (0x1D622, 'M', 'a'), - (0x1D623, 'M', 'b'), - (0x1D624, 'M', 'c'), - (0x1D625, 'M', 'd'), - (0x1D626, 'M', 'e'), - (0x1D627, 'M', 'f'), - (0x1D628, 'M', 'g'), - (0x1D629, 'M', 'h'), - (0x1D62A, 'M', 'i'), - (0x1D62B, 'M', 'j'), - (0x1D62C, 'M', 'k'), - (0x1D62D, 'M', 'l'), - (0x1D62E, 'M', 'm'), - (0x1D62F, 'M', 'n'), - (0x1D630, 'M', 'o'), - (0x1D631, 'M', 'p'), - (0x1D632, 'M', 'q'), - (0x1D633, 'M', 'r'), - (0x1D634, 'M', 's'), - (0x1D635, 'M', 't'), - (0x1D636, 'M', 'u'), - (0x1D637, 'M', 'v'), - (0x1D638, 'M', 'w'), - (0x1D639, 'M', 'x'), - (0x1D63A, 'M', 'y'), - (0x1D63B, 'M', 'z'), - (0x1D63C, 'M', 'a'), - (0x1D63D, 'M', 'b'), - (0x1D63E, 'M', 'c'), - (0x1D63F, 'M', 'd'), - (0x1D640, 'M', 'e'), - (0x1D641, 'M', 'f'), - (0x1D642, 'M', 'g'), - (0x1D643, 'M', 'h'), - (0x1D644, 'M', 'i'), - (0x1D645, 'M', 'j'), - (0x1D646, 'M', 'k'), - (0x1D647, 'M', 'l'), - (0x1D648, 'M', 'm'), - (0x1D649, 'M', 'n'), - (0x1D64A, 'M', 'o'), - (0x1D64B, 'M', 'p'), - (0x1D64C, 'M', 'q'), - (0x1D64D, 'M', 'r'), - (0x1D64E, 'M', 's'), - (0x1D64F, 'M', 't'), - (0x1D650, 'M', 'u'), - (0x1D651, 'M', 'v'), - (0x1D652, 'M', 'w'), - (0x1D653, 'M', 'x'), - (0x1D654, 'M', 'y'), - (0x1D655, 'M', 'z'), - (0x1D656, 'M', 'a'), - (0x1D657, 'M', 'b'), - (0x1D658, 'M', 'c'), - (0x1D659, 'M', 'd'), - (0x1D65A, 'M', 'e'), - (0x1D65B, 'M', 'f'), - (0x1D65C, 'M', 'g'), - (0x1D65D, 'M', 'h'), - (0x1D65E, 'M', 'i'), - (0x1D65F, 'M', 'j'), - (0x1D660, 'M', 'k'), - (0x1D661, 'M', 'l'), - (0x1D662, 'M', 'm'), - (0x1D663, 'M', 'n'), - (0x1D664, 'M', 'o'), - (0x1D665, 'M', 'p'), - (0x1D666, 'M', 'q'), - (0x1D667, 'M', 'r'), - (0x1D668, 'M', 's'), - (0x1D669, 'M', 't'), - (0x1D66A, 'M', 'u'), - (0x1D66B, 'M', 'v'), - (0x1D66C, 'M', 'w'), - (0x1D66D, 'M', 'x'), - (0x1D66E, 'M', 'y'), - (0x1D66F, 'M', 'z'), - (0x1D670, 'M', 'a'), - (0x1D671, 'M', 'b'), - (0x1D672, 'M', 'c'), - (0x1D673, 'M', 'd'), - (0x1D674, 'M', 'e'), - (0x1D675, 'M', 'f'), - (0x1D676, 'M', 'g'), - (0x1D677, 'M', 'h'), - (0x1D678, 'M', 'i'), - (0x1D679, 'M', 'j'), - (0x1D67A, 'M', 'k'), - (0x1D67B, 'M', 'l'), - (0x1D67C, 'M', 'm'), - (0x1D67D, 'M', 'n'), - (0x1D67E, 'M', 'o'), - (0x1D67F, 'M', 'p'), - (0x1D680, 'M', 'q'), - (0x1D681, 'M', 'r'), - (0x1D682, 'M', 's'), - ] - -def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D683, 'M', 't'), - (0x1D684, 'M', 'u'), - (0x1D685, 'M', 'v'), - (0x1D686, 'M', 'w'), - (0x1D687, 'M', 'x'), - (0x1D688, 'M', 'y'), - (0x1D689, 'M', 'z'), - (0x1D68A, 'M', 'a'), - (0x1D68B, 'M', 'b'), - (0x1D68C, 'M', 'c'), - (0x1D68D, 'M', 'd'), - (0x1D68E, 'M', 'e'), - (0x1D68F, 'M', 'f'), - (0x1D690, 'M', 'g'), - (0x1D691, 'M', 'h'), - (0x1D692, 'M', 'i'), - (0x1D693, 'M', 'j'), - (0x1D694, 'M', 'k'), - (0x1D695, 'M', 'l'), - (0x1D696, 'M', 'm'), - (0x1D697, 'M', 'n'), - (0x1D698, 'M', 'o'), - (0x1D699, 'M', 'p'), - (0x1D69A, 'M', 'q'), - (0x1D69B, 'M', 'r'), - (0x1D69C, 'M', 's'), - (0x1D69D, 'M', 't'), - (0x1D69E, 'M', 'u'), - (0x1D69F, 'M', 'v'), - (0x1D6A0, 'M', 'w'), - (0x1D6A1, 'M', 'x'), - (0x1D6A2, 'M', 'y'), - (0x1D6A3, 'M', 'z'), - (0x1D6A4, 'M', 'ı'), - (0x1D6A5, 'M', 'ȷ'), - (0x1D6A6, 'X'), - (0x1D6A8, 'M', 'α'), - (0x1D6A9, 'M', 'β'), - (0x1D6AA, 'M', 'γ'), - (0x1D6AB, 'M', 'δ'), - (0x1D6AC, 'M', 'ε'), - (0x1D6AD, 'M', 'ζ'), - (0x1D6AE, 'M', 'η'), - (0x1D6AF, 'M', 'θ'), - (0x1D6B0, 'M', 'ι'), - (0x1D6B1, 'M', 'κ'), - (0x1D6B2, 'M', 'λ'), - (0x1D6B3, 'M', 'μ'), - (0x1D6B4, 'M', 'ν'), - (0x1D6B5, 'M', 'ξ'), - (0x1D6B6, 'M', 'ο'), - (0x1D6B7, 'M', 'π'), - (0x1D6B8, 'M', 'ρ'), - (0x1D6B9, 'M', 'θ'), - (0x1D6BA, 'M', 'σ'), - (0x1D6BB, 'M', 'τ'), - (0x1D6BC, 'M', 'υ'), - (0x1D6BD, 'M', 'φ'), - (0x1D6BE, 'M', 'χ'), - (0x1D6BF, 'M', 'ψ'), - (0x1D6C0, 'M', 'ω'), - (0x1D6C1, 'M', '∇'), - (0x1D6C2, 'M', 'α'), - (0x1D6C3, 'M', 'β'), - (0x1D6C4, 'M', 'γ'), - (0x1D6C5, 'M', 'δ'), - (0x1D6C6, 'M', 'ε'), - (0x1D6C7, 'M', 'ζ'), - (0x1D6C8, 'M', 'η'), - (0x1D6C9, 'M', 'θ'), - (0x1D6CA, 'M', 'ι'), - (0x1D6CB, 'M', 'κ'), - (0x1D6CC, 'M', 'λ'), - (0x1D6CD, 'M', 'μ'), - (0x1D6CE, 'M', 'ν'), - (0x1D6CF, 'M', 'ξ'), - (0x1D6D0, 'M', 'ο'), - (0x1D6D1, 'M', 'π'), - (0x1D6D2, 'M', 'ρ'), - (0x1D6D3, 'M', 'σ'), - (0x1D6D5, 'M', 'τ'), - (0x1D6D6, 'M', 'υ'), - (0x1D6D7, 'M', 'φ'), - (0x1D6D8, 'M', 'χ'), - (0x1D6D9, 'M', 'ψ'), - (0x1D6DA, 'M', 'ω'), - (0x1D6DB, 'M', '∂'), - (0x1D6DC, 'M', 'ε'), - (0x1D6DD, 'M', 'θ'), - (0x1D6DE, 'M', 'κ'), - (0x1D6DF, 'M', 'φ'), - (0x1D6E0, 'M', 'ρ'), - (0x1D6E1, 'M', 'π'), - (0x1D6E2, 'M', 'α'), - (0x1D6E3, 'M', 'β'), - (0x1D6E4, 'M', 'γ'), - (0x1D6E5, 'M', 'δ'), - (0x1D6E6, 'M', 'ε'), - (0x1D6E7, 'M', 'ζ'), - (0x1D6E8, 'M', 'η'), - ] - -def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D6E9, 'M', 'θ'), - (0x1D6EA, 'M', 'ι'), - (0x1D6EB, 'M', 'κ'), - (0x1D6EC, 'M', 'λ'), - (0x1D6ED, 'M', 'μ'), - (0x1D6EE, 'M', 'ν'), - (0x1D6EF, 'M', 'ξ'), - (0x1D6F0, 'M', 'ο'), - (0x1D6F1, 'M', 'π'), - (0x1D6F2, 'M', 'ρ'), - (0x1D6F3, 'M', 'θ'), - (0x1D6F4, 'M', 'σ'), - (0x1D6F5, 'M', 'τ'), - (0x1D6F6, 'M', 'υ'), - (0x1D6F7, 'M', 'φ'), - (0x1D6F8, 'M', 'χ'), - (0x1D6F9, 'M', 'ψ'), - (0x1D6FA, 'M', 'ω'), - (0x1D6FB, 'M', '∇'), - (0x1D6FC, 'M', 'α'), - (0x1D6FD, 'M', 'β'), - (0x1D6FE, 'M', 'γ'), - (0x1D6FF, 'M', 'δ'), - (0x1D700, 'M', 'ε'), - (0x1D701, 'M', 'ζ'), - (0x1D702, 'M', 'η'), - (0x1D703, 'M', 'θ'), - (0x1D704, 'M', 'ι'), - (0x1D705, 'M', 'κ'), - (0x1D706, 'M', 'λ'), - (0x1D707, 'M', 'μ'), - (0x1D708, 'M', 'ν'), - (0x1D709, 'M', 'ξ'), - (0x1D70A, 'M', 'ο'), - (0x1D70B, 'M', 'π'), - (0x1D70C, 'M', 'ρ'), - (0x1D70D, 'M', 'σ'), - (0x1D70F, 'M', 'τ'), - (0x1D710, 'M', 'υ'), - (0x1D711, 'M', 'φ'), - (0x1D712, 'M', 'χ'), - (0x1D713, 'M', 'ψ'), - (0x1D714, 'M', 'ω'), - (0x1D715, 'M', '∂'), - (0x1D716, 'M', 'ε'), - (0x1D717, 'M', 'θ'), - (0x1D718, 'M', 'κ'), - (0x1D719, 'M', 'φ'), - (0x1D71A, 'M', 'ρ'), - (0x1D71B, 'M', 'π'), - (0x1D71C, 'M', 'α'), - (0x1D71D, 'M', 'β'), - (0x1D71E, 'M', 'γ'), - (0x1D71F, 'M', 'δ'), - (0x1D720, 'M', 'ε'), - (0x1D721, 'M', 'ζ'), - (0x1D722, 'M', 'η'), - (0x1D723, 'M', 'θ'), - (0x1D724, 'M', 'ι'), - (0x1D725, 'M', 'κ'), - (0x1D726, 'M', 'λ'), - (0x1D727, 'M', 'μ'), - (0x1D728, 'M', 'ν'), - (0x1D729, 'M', 'ξ'), - (0x1D72A, 'M', 'ο'), - (0x1D72B, 'M', 'π'), - (0x1D72C, 'M', 'ρ'), - (0x1D72D, 'M', 'θ'), - (0x1D72E, 'M', 'σ'), - (0x1D72F, 'M', 'τ'), - (0x1D730, 'M', 'υ'), - (0x1D731, 'M', 'φ'), - (0x1D732, 'M', 'χ'), - (0x1D733, 'M', 'ψ'), - (0x1D734, 'M', 'ω'), - (0x1D735, 'M', '∇'), - (0x1D736, 'M', 'α'), - (0x1D737, 'M', 'β'), - (0x1D738, 'M', 'γ'), - (0x1D739, 'M', 'δ'), - (0x1D73A, 'M', 'ε'), - (0x1D73B, 'M', 'ζ'), - (0x1D73C, 'M', 'η'), - (0x1D73D, 'M', 'θ'), - (0x1D73E, 'M', 'ι'), - (0x1D73F, 'M', 'κ'), - (0x1D740, 'M', 'λ'), - (0x1D741, 'M', 'μ'), - (0x1D742, 'M', 'ν'), - (0x1D743, 'M', 'ξ'), - (0x1D744, 'M', 'ο'), - (0x1D745, 'M', 'π'), - (0x1D746, 'M', 'ρ'), - (0x1D747, 'M', 'σ'), - (0x1D749, 'M', 'τ'), - (0x1D74A, 'M', 'υ'), - (0x1D74B, 'M', 'φ'), - (0x1D74C, 'M', 'χ'), - (0x1D74D, 'M', 'ψ'), - (0x1D74E, 'M', 'ω'), - ] - -def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D74F, 'M', '∂'), - (0x1D750, 'M', 'ε'), - (0x1D751, 'M', 'θ'), - (0x1D752, 'M', 'κ'), - (0x1D753, 'M', 'φ'), - (0x1D754, 'M', 'ρ'), - (0x1D755, 'M', 'π'), - (0x1D756, 'M', 'α'), - (0x1D757, 'M', 'β'), - (0x1D758, 'M', 'γ'), - (0x1D759, 'M', 'δ'), - (0x1D75A, 'M', 'ε'), - (0x1D75B, 'M', 'ζ'), - (0x1D75C, 'M', 'η'), - (0x1D75D, 'M', 'θ'), - (0x1D75E, 'M', 'ι'), - (0x1D75F, 'M', 'κ'), - (0x1D760, 'M', 'λ'), - (0x1D761, 'M', 'μ'), - (0x1D762, 'M', 'ν'), - (0x1D763, 'M', 'ξ'), - (0x1D764, 'M', 'ο'), - (0x1D765, 'M', 'π'), - (0x1D766, 'M', 'ρ'), - (0x1D767, 'M', 'θ'), - (0x1D768, 'M', 'σ'), - (0x1D769, 'M', 'τ'), - (0x1D76A, 'M', 'υ'), - (0x1D76B, 'M', 'φ'), - (0x1D76C, 'M', 'χ'), - (0x1D76D, 'M', 'ψ'), - (0x1D76E, 'M', 'ω'), - (0x1D76F, 'M', '∇'), - (0x1D770, 'M', 'α'), - (0x1D771, 'M', 'β'), - (0x1D772, 'M', 'γ'), - (0x1D773, 'M', 'δ'), - (0x1D774, 'M', 'ε'), - (0x1D775, 'M', 'ζ'), - (0x1D776, 'M', 'η'), - (0x1D777, 'M', 'θ'), - (0x1D778, 'M', 'ι'), - (0x1D779, 'M', 'κ'), - (0x1D77A, 'M', 'λ'), - (0x1D77B, 'M', 'μ'), - (0x1D77C, 'M', 'ν'), - (0x1D77D, 'M', 'ξ'), - (0x1D77E, 'M', 'ο'), - (0x1D77F, 'M', 'π'), - (0x1D780, 'M', 'ρ'), - (0x1D781, 'M', 'σ'), - (0x1D783, 'M', 'τ'), - (0x1D784, 'M', 'υ'), - (0x1D785, 'M', 'φ'), - (0x1D786, 'M', 'χ'), - (0x1D787, 'M', 'ψ'), - (0x1D788, 'M', 'ω'), - (0x1D789, 'M', '∂'), - (0x1D78A, 'M', 'ε'), - (0x1D78B, 'M', 'θ'), - (0x1D78C, 'M', 'κ'), - (0x1D78D, 'M', 'φ'), - (0x1D78E, 'M', 'ρ'), - (0x1D78F, 'M', 'π'), - (0x1D790, 'M', 'α'), - (0x1D791, 'M', 'β'), - (0x1D792, 'M', 'γ'), - (0x1D793, 'M', 'δ'), - (0x1D794, 'M', 'ε'), - (0x1D795, 'M', 'ζ'), - (0x1D796, 'M', 'η'), - (0x1D797, 'M', 'θ'), - (0x1D798, 'M', 'ι'), - (0x1D799, 'M', 'κ'), - (0x1D79A, 'M', 'λ'), - (0x1D79B, 'M', 'μ'), - (0x1D79C, 'M', 'ν'), - (0x1D79D, 'M', 'ξ'), - (0x1D79E, 'M', 'ο'), - (0x1D79F, 'M', 'π'), - (0x1D7A0, 'M', 'ρ'), - (0x1D7A1, 'M', 'θ'), - (0x1D7A2, 'M', 'σ'), - (0x1D7A3, 'M', 'τ'), - (0x1D7A4, 'M', 'υ'), - (0x1D7A5, 'M', 'φ'), - (0x1D7A6, 'M', 'χ'), - (0x1D7A7, 'M', 'ψ'), - (0x1D7A8, 'M', 'ω'), - (0x1D7A9, 'M', '∇'), - (0x1D7AA, 'M', 'α'), - (0x1D7AB, 'M', 'β'), - (0x1D7AC, 'M', 'γ'), - (0x1D7AD, 'M', 'δ'), - (0x1D7AE, 'M', 'ε'), - (0x1D7AF, 'M', 'ζ'), - (0x1D7B0, 'M', 'η'), - (0x1D7B1, 'M', 'θ'), - (0x1D7B2, 'M', 'ι'), - (0x1D7B3, 'M', 'κ'), - ] - -def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1D7B4, 'M', 'λ'), - (0x1D7B5, 'M', 'μ'), - (0x1D7B6, 'M', 'ν'), - (0x1D7B7, 'M', 'ξ'), - (0x1D7B8, 'M', 'ο'), - (0x1D7B9, 'M', 'π'), - (0x1D7BA, 'M', 'ρ'), - (0x1D7BB, 'M', 'σ'), - (0x1D7BD, 'M', 'τ'), - (0x1D7BE, 'M', 'υ'), - (0x1D7BF, 'M', 'φ'), - (0x1D7C0, 'M', 'χ'), - (0x1D7C1, 'M', 'ψ'), - (0x1D7C2, 'M', 'ω'), - (0x1D7C3, 'M', '∂'), - (0x1D7C4, 'M', 'ε'), - (0x1D7C5, 'M', 'θ'), - (0x1D7C6, 'M', 'κ'), - (0x1D7C7, 'M', 'φ'), - (0x1D7C8, 'M', 'ρ'), - (0x1D7C9, 'M', 'π'), - (0x1D7CA, 'M', 'ϝ'), - (0x1D7CC, 'X'), - (0x1D7CE, 'M', '0'), - (0x1D7CF, 'M', '1'), - (0x1D7D0, 'M', '2'), - (0x1D7D1, 'M', '3'), - (0x1D7D2, 'M', '4'), - (0x1D7D3, 'M', '5'), - (0x1D7D4, 'M', '6'), - (0x1D7D5, 'M', '7'), - (0x1D7D6, 'M', '8'), - (0x1D7D7, 'M', '9'), - (0x1D7D8, 'M', '0'), - (0x1D7D9, 'M', '1'), - (0x1D7DA, 'M', '2'), - (0x1D7DB, 'M', '3'), - (0x1D7DC, 'M', '4'), - (0x1D7DD, 'M', '5'), - (0x1D7DE, 'M', '6'), - (0x1D7DF, 'M', '7'), - (0x1D7E0, 'M', '8'), - (0x1D7E1, 'M', '9'), - (0x1D7E2, 'M', '0'), - (0x1D7E3, 'M', '1'), - (0x1D7E4, 'M', '2'), - (0x1D7E5, 'M', '3'), - (0x1D7E6, 'M', '4'), - (0x1D7E7, 'M', '5'), - (0x1D7E8, 'M', '6'), - (0x1D7E9, 'M', '7'), - (0x1D7EA, 'M', '8'), - (0x1D7EB, 'M', '9'), - (0x1D7EC, 'M', '0'), - (0x1D7ED, 'M', '1'), - (0x1D7EE, 'M', '2'), - (0x1D7EF, 'M', '3'), - (0x1D7F0, 'M', '4'), - (0x1D7F1, 'M', '5'), - (0x1D7F2, 'M', '6'), - (0x1D7F3, 'M', '7'), - (0x1D7F4, 'M', '8'), - (0x1D7F5, 'M', '9'), - (0x1D7F6, 'M', '0'), - (0x1D7F7, 'M', '1'), - (0x1D7F8, 'M', '2'), - (0x1D7F9, 'M', '3'), - (0x1D7FA, 'M', '4'), - (0x1D7FB, 'M', '5'), - (0x1D7FC, 'M', '6'), - (0x1D7FD, 'M', '7'), - (0x1D7FE, 'M', '8'), - (0x1D7FF, 'M', '9'), - (0x1D800, 'V'), - (0x1DA8C, 'X'), - (0x1DA9B, 'V'), - (0x1DAA0, 'X'), - (0x1DAA1, 'V'), - (0x1DAB0, 'X'), - (0x1DF00, 'V'), - (0x1DF1F, 'X'), - (0x1DF25, 'V'), - (0x1DF2B, 'X'), - (0x1E000, 'V'), - (0x1E007, 'X'), - (0x1E008, 'V'), - (0x1E019, 'X'), - (0x1E01B, 'V'), - (0x1E022, 'X'), - (0x1E023, 'V'), - (0x1E025, 'X'), - (0x1E026, 'V'), - (0x1E02B, 'X'), - (0x1E030, 'M', 'а'), - (0x1E031, 'M', 'б'), - (0x1E032, 'M', 'в'), - (0x1E033, 'M', 'г'), - (0x1E034, 'M', 'д'), - (0x1E035, 'M', 'е'), - (0x1E036, 'M', 'ж'), - ] - -def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E037, 'M', 'з'), - (0x1E038, 'M', 'и'), - (0x1E039, 'M', 'к'), - (0x1E03A, 'M', 'л'), - (0x1E03B, 'M', 'м'), - (0x1E03C, 'M', 'о'), - (0x1E03D, 'M', 'п'), - (0x1E03E, 'M', 'р'), - (0x1E03F, 'M', 'с'), - (0x1E040, 'M', 'т'), - (0x1E041, 'M', 'у'), - (0x1E042, 'M', 'ф'), - (0x1E043, 'M', 'х'), - (0x1E044, 'M', 'ц'), - (0x1E045, 'M', 'ч'), - (0x1E046, 'M', 'ш'), - (0x1E047, 'M', 'ы'), - (0x1E048, 'M', 'э'), - (0x1E049, 'M', 'ю'), - (0x1E04A, 'M', 'ꚉ'), - (0x1E04B, 'M', 'ә'), - (0x1E04C, 'M', 'і'), - (0x1E04D, 'M', 'ј'), - (0x1E04E, 'M', 'ө'), - (0x1E04F, 'M', 'ү'), - (0x1E050, 'M', 'ӏ'), - (0x1E051, 'M', 'а'), - (0x1E052, 'M', 'б'), - (0x1E053, 'M', 'в'), - (0x1E054, 'M', 'г'), - (0x1E055, 'M', 'д'), - (0x1E056, 'M', 'е'), - (0x1E057, 'M', 'ж'), - (0x1E058, 'M', 'з'), - (0x1E059, 'M', 'и'), - (0x1E05A, 'M', 'к'), - (0x1E05B, 'M', 'л'), - (0x1E05C, 'M', 'о'), - (0x1E05D, 'M', 'п'), - (0x1E05E, 'M', 'с'), - (0x1E05F, 'M', 'у'), - (0x1E060, 'M', 'ф'), - (0x1E061, 'M', 'х'), - (0x1E062, 'M', 'ц'), - (0x1E063, 'M', 'ч'), - (0x1E064, 'M', 'ш'), - (0x1E065, 'M', 'ъ'), - (0x1E066, 'M', 'ы'), - (0x1E067, 'M', 'ґ'), - (0x1E068, 'M', 'і'), - (0x1E069, 'M', 'ѕ'), - (0x1E06A, 'M', 'џ'), - (0x1E06B, 'M', 'ҫ'), - (0x1E06C, 'M', 'ꙑ'), - (0x1E06D, 'M', 'ұ'), - (0x1E06E, 'X'), - (0x1E08F, 'V'), - (0x1E090, 'X'), - (0x1E100, 'V'), - (0x1E12D, 'X'), - (0x1E130, 'V'), - (0x1E13E, 'X'), - (0x1E140, 'V'), - (0x1E14A, 'X'), - (0x1E14E, 'V'), - (0x1E150, 'X'), - (0x1E290, 'V'), - (0x1E2AF, 'X'), - (0x1E2C0, 'V'), - (0x1E2FA, 'X'), - (0x1E2FF, 'V'), - (0x1E300, 'X'), - (0x1E4D0, 'V'), - (0x1E4FA, 'X'), - (0x1E7E0, 'V'), - (0x1E7E7, 'X'), - (0x1E7E8, 'V'), - (0x1E7EC, 'X'), - (0x1E7ED, 'V'), - (0x1E7EF, 'X'), - (0x1E7F0, 'V'), - (0x1E7FF, 'X'), - (0x1E800, 'V'), - (0x1E8C5, 'X'), - (0x1E8C7, 'V'), - (0x1E8D7, 'X'), - (0x1E900, 'M', '𞤢'), - (0x1E901, 'M', '𞤣'), - (0x1E902, 'M', '𞤤'), - (0x1E903, 'M', '𞤥'), - (0x1E904, 'M', '𞤦'), - (0x1E905, 'M', '𞤧'), - (0x1E906, 'M', '𞤨'), - (0x1E907, 'M', '𞤩'), - (0x1E908, 'M', '𞤪'), - (0x1E909, 'M', '𞤫'), - (0x1E90A, 'M', '𞤬'), - (0x1E90B, 'M', '𞤭'), - (0x1E90C, 'M', '𞤮'), - (0x1E90D, 'M', '𞤯'), - ] - -def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1E90E, 'M', '𞤰'), - (0x1E90F, 'M', '𞤱'), - (0x1E910, 'M', '𞤲'), - (0x1E911, 'M', '𞤳'), - (0x1E912, 'M', '𞤴'), - (0x1E913, 'M', '𞤵'), - (0x1E914, 'M', '𞤶'), - (0x1E915, 'M', '𞤷'), - (0x1E916, 'M', '𞤸'), - (0x1E917, 'M', '𞤹'), - (0x1E918, 'M', '𞤺'), - (0x1E919, 'M', '𞤻'), - (0x1E91A, 'M', '𞤼'), - (0x1E91B, 'M', '𞤽'), - (0x1E91C, 'M', '𞤾'), - (0x1E91D, 'M', '𞤿'), - (0x1E91E, 'M', '𞥀'), - (0x1E91F, 'M', '𞥁'), - (0x1E920, 'M', '𞥂'), - (0x1E921, 'M', '𞥃'), - (0x1E922, 'V'), - (0x1E94C, 'X'), - (0x1E950, 'V'), - (0x1E95A, 'X'), - (0x1E95E, 'V'), - (0x1E960, 'X'), - (0x1EC71, 'V'), - (0x1ECB5, 'X'), - (0x1ED01, 'V'), - (0x1ED3E, 'X'), - (0x1EE00, 'M', 'ا'), - (0x1EE01, 'M', 'ب'), - (0x1EE02, 'M', 'ج'), - (0x1EE03, 'M', 'د'), - (0x1EE04, 'X'), - (0x1EE05, 'M', 'و'), - (0x1EE06, 'M', 'ز'), - (0x1EE07, 'M', 'ح'), - (0x1EE08, 'M', 'ط'), - (0x1EE09, 'M', 'ي'), - (0x1EE0A, 'M', 'ك'), - (0x1EE0B, 'M', 'ل'), - (0x1EE0C, 'M', 'م'), - (0x1EE0D, 'M', 'ن'), - (0x1EE0E, 'M', 'س'), - (0x1EE0F, 'M', 'ع'), - (0x1EE10, 'M', 'ف'), - (0x1EE11, 'M', 'ص'), - (0x1EE12, 'M', 'ق'), - (0x1EE13, 'M', 'ر'), - (0x1EE14, 'M', 'ش'), - (0x1EE15, 'M', 'ت'), - (0x1EE16, 'M', 'ث'), - (0x1EE17, 'M', 'خ'), - (0x1EE18, 'M', 'ذ'), - (0x1EE19, 'M', 'ض'), - (0x1EE1A, 'M', 'ظ'), - (0x1EE1B, 'M', 'غ'), - (0x1EE1C, 'M', 'ٮ'), - (0x1EE1D, 'M', 'ں'), - (0x1EE1E, 'M', 'ڡ'), - (0x1EE1F, 'M', 'ٯ'), - (0x1EE20, 'X'), - (0x1EE21, 'M', 'ب'), - (0x1EE22, 'M', 'ج'), - (0x1EE23, 'X'), - (0x1EE24, 'M', 'ه'), - (0x1EE25, 'X'), - (0x1EE27, 'M', 'ح'), - (0x1EE28, 'X'), - (0x1EE29, 'M', 'ي'), - (0x1EE2A, 'M', 'ك'), - (0x1EE2B, 'M', 'ل'), - (0x1EE2C, 'M', 'م'), - (0x1EE2D, 'M', 'ن'), - (0x1EE2E, 'M', 'س'), - (0x1EE2F, 'M', 'ع'), - (0x1EE30, 'M', 'ف'), - (0x1EE31, 'M', 'ص'), - (0x1EE32, 'M', 'ق'), - (0x1EE33, 'X'), - (0x1EE34, 'M', 'ش'), - (0x1EE35, 'M', 'ت'), - (0x1EE36, 'M', 'ث'), - (0x1EE37, 'M', 'خ'), - (0x1EE38, 'X'), - (0x1EE39, 'M', 'ض'), - (0x1EE3A, 'X'), - (0x1EE3B, 'M', 'غ'), - (0x1EE3C, 'X'), - (0x1EE42, 'M', 'ج'), - (0x1EE43, 'X'), - (0x1EE47, 'M', 'ح'), - (0x1EE48, 'X'), - (0x1EE49, 'M', 'ي'), - (0x1EE4A, 'X'), - (0x1EE4B, 'M', 'ل'), - (0x1EE4C, 'X'), - (0x1EE4D, 'M', 'ن'), - (0x1EE4E, 'M', 'س'), - ] - -def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EE4F, 'M', 'ع'), - (0x1EE50, 'X'), - (0x1EE51, 'M', 'ص'), - (0x1EE52, 'M', 'ق'), - (0x1EE53, 'X'), - (0x1EE54, 'M', 'ش'), - (0x1EE55, 'X'), - (0x1EE57, 'M', 'خ'), - (0x1EE58, 'X'), - (0x1EE59, 'M', 'ض'), - (0x1EE5A, 'X'), - (0x1EE5B, 'M', 'غ'), - (0x1EE5C, 'X'), - (0x1EE5D, 'M', 'ں'), - (0x1EE5E, 'X'), - (0x1EE5F, 'M', 'ٯ'), - (0x1EE60, 'X'), - (0x1EE61, 'M', 'ب'), - (0x1EE62, 'M', 'ج'), - (0x1EE63, 'X'), - (0x1EE64, 'M', 'ه'), - (0x1EE65, 'X'), - (0x1EE67, 'M', 'ح'), - (0x1EE68, 'M', 'ط'), - (0x1EE69, 'M', 'ي'), - (0x1EE6A, 'M', 'ك'), - (0x1EE6B, 'X'), - (0x1EE6C, 'M', 'م'), - (0x1EE6D, 'M', 'ن'), - (0x1EE6E, 'M', 'س'), - (0x1EE6F, 'M', 'ع'), - (0x1EE70, 'M', 'ف'), - (0x1EE71, 'M', 'ص'), - (0x1EE72, 'M', 'ق'), - (0x1EE73, 'X'), - (0x1EE74, 'M', 'ش'), - (0x1EE75, 'M', 'ت'), - (0x1EE76, 'M', 'ث'), - (0x1EE77, 'M', 'خ'), - (0x1EE78, 'X'), - (0x1EE79, 'M', 'ض'), - (0x1EE7A, 'M', 'ظ'), - (0x1EE7B, 'M', 'غ'), - (0x1EE7C, 'M', 'ٮ'), - (0x1EE7D, 'X'), - (0x1EE7E, 'M', 'ڡ'), - (0x1EE7F, 'X'), - (0x1EE80, 'M', 'ا'), - (0x1EE81, 'M', 'ب'), - (0x1EE82, 'M', 'ج'), - (0x1EE83, 'M', 'د'), - (0x1EE84, 'M', 'ه'), - (0x1EE85, 'M', 'و'), - (0x1EE86, 'M', 'ز'), - (0x1EE87, 'M', 'ح'), - (0x1EE88, 'M', 'ط'), - (0x1EE89, 'M', 'ي'), - (0x1EE8A, 'X'), - (0x1EE8B, 'M', 'ل'), - (0x1EE8C, 'M', 'م'), - (0x1EE8D, 'M', 'ن'), - (0x1EE8E, 'M', 'س'), - (0x1EE8F, 'M', 'ع'), - (0x1EE90, 'M', 'ف'), - (0x1EE91, 'M', 'ص'), - (0x1EE92, 'M', 'ق'), - (0x1EE93, 'M', 'ر'), - (0x1EE94, 'M', 'ش'), - (0x1EE95, 'M', 'ت'), - (0x1EE96, 'M', 'ث'), - (0x1EE97, 'M', 'خ'), - (0x1EE98, 'M', 'ذ'), - (0x1EE99, 'M', 'ض'), - (0x1EE9A, 'M', 'ظ'), - (0x1EE9B, 'M', 'غ'), - (0x1EE9C, 'X'), - (0x1EEA1, 'M', 'ب'), - (0x1EEA2, 'M', 'ج'), - (0x1EEA3, 'M', 'د'), - (0x1EEA4, 'X'), - (0x1EEA5, 'M', 'و'), - (0x1EEA6, 'M', 'ز'), - (0x1EEA7, 'M', 'ح'), - (0x1EEA8, 'M', 'ط'), - (0x1EEA9, 'M', 'ي'), - (0x1EEAA, 'X'), - (0x1EEAB, 'M', 'ل'), - (0x1EEAC, 'M', 'م'), - (0x1EEAD, 'M', 'ن'), - (0x1EEAE, 'M', 'س'), - (0x1EEAF, 'M', 'ع'), - (0x1EEB0, 'M', 'ف'), - (0x1EEB1, 'M', 'ص'), - (0x1EEB2, 'M', 'ق'), - (0x1EEB3, 'M', 'ر'), - (0x1EEB4, 'M', 'ش'), - (0x1EEB5, 'M', 'ت'), - (0x1EEB6, 'M', 'ث'), - (0x1EEB7, 'M', 'خ'), - (0x1EEB8, 'M', 'ذ'), - ] - -def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1EEB9, 'M', 'ض'), - (0x1EEBA, 'M', 'ظ'), - (0x1EEBB, 'M', 'غ'), - (0x1EEBC, 'X'), - (0x1EEF0, 'V'), - (0x1EEF2, 'X'), - (0x1F000, 'V'), - (0x1F02C, 'X'), - (0x1F030, 'V'), - (0x1F094, 'X'), - (0x1F0A0, 'V'), - (0x1F0AF, 'X'), - (0x1F0B1, 'V'), - (0x1F0C0, 'X'), - (0x1F0C1, 'V'), - (0x1F0D0, 'X'), - (0x1F0D1, 'V'), - (0x1F0F6, 'X'), - (0x1F101, '3', '0,'), - (0x1F102, '3', '1,'), - (0x1F103, '3', '2,'), - (0x1F104, '3', '3,'), - (0x1F105, '3', '4,'), - (0x1F106, '3', '5,'), - (0x1F107, '3', '6,'), - (0x1F108, '3', '7,'), - (0x1F109, '3', '8,'), - (0x1F10A, '3', '9,'), - (0x1F10B, 'V'), - (0x1F110, '3', '(a)'), - (0x1F111, '3', '(b)'), - (0x1F112, '3', '(c)'), - (0x1F113, '3', '(d)'), - (0x1F114, '3', '(e)'), - (0x1F115, '3', '(f)'), - (0x1F116, '3', '(g)'), - (0x1F117, '3', '(h)'), - (0x1F118, '3', '(i)'), - (0x1F119, '3', '(j)'), - (0x1F11A, '3', '(k)'), - (0x1F11B, '3', '(l)'), - (0x1F11C, '3', '(m)'), - (0x1F11D, '3', '(n)'), - (0x1F11E, '3', '(o)'), - (0x1F11F, '3', '(p)'), - (0x1F120, '3', '(q)'), - (0x1F121, '3', '(r)'), - (0x1F122, '3', '(s)'), - (0x1F123, '3', '(t)'), - (0x1F124, '3', '(u)'), - (0x1F125, '3', '(v)'), - (0x1F126, '3', '(w)'), - (0x1F127, '3', '(x)'), - (0x1F128, '3', '(y)'), - (0x1F129, '3', '(z)'), - (0x1F12A, 'M', '〔s〕'), - (0x1F12B, 'M', 'c'), - (0x1F12C, 'M', 'r'), - (0x1F12D, 'M', 'cd'), - (0x1F12E, 'M', 'wz'), - (0x1F12F, 'V'), - (0x1F130, 'M', 'a'), - (0x1F131, 'M', 'b'), - (0x1F132, 'M', 'c'), - (0x1F133, 'M', 'd'), - (0x1F134, 'M', 'e'), - (0x1F135, 'M', 'f'), - (0x1F136, 'M', 'g'), - (0x1F137, 'M', 'h'), - (0x1F138, 'M', 'i'), - (0x1F139, 'M', 'j'), - (0x1F13A, 'M', 'k'), - (0x1F13B, 'M', 'l'), - (0x1F13C, 'M', 'm'), - (0x1F13D, 'M', 'n'), - (0x1F13E, 'M', 'o'), - (0x1F13F, 'M', 'p'), - (0x1F140, 'M', 'q'), - (0x1F141, 'M', 'r'), - (0x1F142, 'M', 's'), - (0x1F143, 'M', 't'), - (0x1F144, 'M', 'u'), - (0x1F145, 'M', 'v'), - (0x1F146, 'M', 'w'), - (0x1F147, 'M', 'x'), - (0x1F148, 'M', 'y'), - (0x1F149, 'M', 'z'), - (0x1F14A, 'M', 'hv'), - (0x1F14B, 'M', 'mv'), - (0x1F14C, 'M', 'sd'), - (0x1F14D, 'M', 'ss'), - (0x1F14E, 'M', 'ppv'), - (0x1F14F, 'M', 'wc'), - (0x1F150, 'V'), - (0x1F16A, 'M', 'mc'), - (0x1F16B, 'M', 'md'), - (0x1F16C, 'M', 'mr'), - (0x1F16D, 'V'), - (0x1F190, 'M', 'dj'), - (0x1F191, 'V'), - ] - -def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1F1AE, 'X'), - (0x1F1E6, 'V'), - (0x1F200, 'M', 'ほか'), - (0x1F201, 'M', 'ココ'), - (0x1F202, 'M', 'サ'), - (0x1F203, 'X'), - (0x1F210, 'M', '手'), - (0x1F211, 'M', '字'), - (0x1F212, 'M', '双'), - (0x1F213, 'M', 'デ'), - (0x1F214, 'M', '二'), - (0x1F215, 'M', '多'), - (0x1F216, 'M', '解'), - (0x1F217, 'M', '天'), - (0x1F218, 'M', '交'), - (0x1F219, 'M', '映'), - (0x1F21A, 'M', '無'), - (0x1F21B, 'M', '料'), - (0x1F21C, 'M', '前'), - (0x1F21D, 'M', '後'), - (0x1F21E, 'M', '再'), - (0x1F21F, 'M', '新'), - (0x1F220, 'M', '初'), - (0x1F221, 'M', '終'), - (0x1F222, 'M', '生'), - (0x1F223, 'M', '販'), - (0x1F224, 'M', '声'), - (0x1F225, 'M', '吹'), - (0x1F226, 'M', '演'), - (0x1F227, 'M', '投'), - (0x1F228, 'M', '捕'), - (0x1F229, 'M', '一'), - (0x1F22A, 'M', '三'), - (0x1F22B, 'M', '遊'), - (0x1F22C, 'M', '左'), - (0x1F22D, 'M', '中'), - (0x1F22E, 'M', '右'), - (0x1F22F, 'M', '指'), - (0x1F230, 'M', '走'), - (0x1F231, 'M', '打'), - (0x1F232, 'M', '禁'), - (0x1F233, 'M', '空'), - (0x1F234, 'M', '合'), - (0x1F235, 'M', '満'), - (0x1F236, 'M', '有'), - (0x1F237, 'M', '月'), - (0x1F238, 'M', '申'), - (0x1F239, 'M', '割'), - (0x1F23A, 'M', '営'), - (0x1F23B, 'M', '配'), - (0x1F23C, 'X'), - (0x1F240, 'M', '〔本〕'), - (0x1F241, 'M', '〔三〕'), - (0x1F242, 'M', '〔二〕'), - (0x1F243, 'M', '〔安〕'), - (0x1F244, 'M', '〔点〕'), - (0x1F245, 'M', '〔打〕'), - (0x1F246, 'M', '〔盗〕'), - (0x1F247, 'M', '〔勝〕'), - (0x1F248, 'M', '〔敗〕'), - (0x1F249, 'X'), - (0x1F250, 'M', '得'), - (0x1F251, 'M', '可'), - (0x1F252, 'X'), - (0x1F260, 'V'), - (0x1F266, 'X'), - (0x1F300, 'V'), - (0x1F6D8, 'X'), - (0x1F6DC, 'V'), - (0x1F6ED, 'X'), - (0x1F6F0, 'V'), - (0x1F6FD, 'X'), - (0x1F700, 'V'), - (0x1F777, 'X'), - (0x1F77B, 'V'), - (0x1F7DA, 'X'), - (0x1F7E0, 'V'), - (0x1F7EC, 'X'), - (0x1F7F0, 'V'), - (0x1F7F1, 'X'), - (0x1F800, 'V'), - (0x1F80C, 'X'), - (0x1F810, 'V'), - (0x1F848, 'X'), - (0x1F850, 'V'), - (0x1F85A, 'X'), - (0x1F860, 'V'), - (0x1F888, 'X'), - (0x1F890, 'V'), - (0x1F8AE, 'X'), - (0x1F8B0, 'V'), - (0x1F8B2, 'X'), - (0x1F900, 'V'), - (0x1FA54, 'X'), - (0x1FA60, 'V'), - (0x1FA6E, 'X'), - (0x1FA70, 'V'), - (0x1FA7D, 'X'), - (0x1FA80, 'V'), - (0x1FA89, 'X'), - ] - -def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x1FA90, 'V'), - (0x1FABE, 'X'), - (0x1FABF, 'V'), - (0x1FAC6, 'X'), - (0x1FACE, 'V'), - (0x1FADC, 'X'), - (0x1FAE0, 'V'), - (0x1FAE9, 'X'), - (0x1FAF0, 'V'), - (0x1FAF9, 'X'), - (0x1FB00, 'V'), - (0x1FB93, 'X'), - (0x1FB94, 'V'), - (0x1FBCB, 'X'), - (0x1FBF0, 'M', '0'), - (0x1FBF1, 'M', '1'), - (0x1FBF2, 'M', '2'), - (0x1FBF3, 'M', '3'), - (0x1FBF4, 'M', '4'), - (0x1FBF5, 'M', '5'), - (0x1FBF6, 'M', '6'), - (0x1FBF7, 'M', '7'), - (0x1FBF8, 'M', '8'), - (0x1FBF9, 'M', '9'), - (0x1FBFA, 'X'), - (0x20000, 'V'), - (0x2A6E0, 'X'), - (0x2A700, 'V'), - (0x2B73A, 'X'), - (0x2B740, 'V'), - (0x2B81E, 'X'), - (0x2B820, 'V'), - (0x2CEA2, 'X'), - (0x2CEB0, 'V'), - (0x2EBE1, 'X'), - (0x2EBF0, 'V'), - (0x2EE5E, 'X'), - (0x2F800, 'M', '丽'), - (0x2F801, 'M', '丸'), - (0x2F802, 'M', '乁'), - (0x2F803, 'M', '𠄢'), - (0x2F804, 'M', '你'), - (0x2F805, 'M', '侮'), - (0x2F806, 'M', '侻'), - (0x2F807, 'M', '倂'), - (0x2F808, 'M', '偺'), - (0x2F809, 'M', '備'), - (0x2F80A, 'M', '僧'), - (0x2F80B, 'M', '像'), - (0x2F80C, 'M', '㒞'), - (0x2F80D, 'M', '𠘺'), - (0x2F80E, 'M', '免'), - (0x2F80F, 'M', '兔'), - (0x2F810, 'M', '兤'), - (0x2F811, 'M', '具'), - (0x2F812, 'M', '𠔜'), - (0x2F813, 'M', '㒹'), - (0x2F814, 'M', '內'), - (0x2F815, 'M', '再'), - (0x2F816, 'M', '𠕋'), - (0x2F817, 'M', '冗'), - (0x2F818, 'M', '冤'), - (0x2F819, 'M', '仌'), - (0x2F81A, 'M', '冬'), - (0x2F81B, 'M', '况'), - (0x2F81C, 'M', '𩇟'), - (0x2F81D, 'M', '凵'), - (0x2F81E, 'M', '刃'), - (0x2F81F, 'M', '㓟'), - (0x2F820, 'M', '刻'), - (0x2F821, 'M', '剆'), - (0x2F822, 'M', '割'), - (0x2F823, 'M', '剷'), - (0x2F824, 'M', '㔕'), - (0x2F825, 'M', '勇'), - (0x2F826, 'M', '勉'), - (0x2F827, 'M', '勤'), - (0x2F828, 'M', '勺'), - (0x2F829, 'M', '包'), - (0x2F82A, 'M', '匆'), - (0x2F82B, 'M', '北'), - (0x2F82C, 'M', '卉'), - (0x2F82D, 'M', '卑'), - (0x2F82E, 'M', '博'), - (0x2F82F, 'M', '即'), - (0x2F830, 'M', '卽'), - (0x2F831, 'M', '卿'), - (0x2F834, 'M', '𠨬'), - (0x2F835, 'M', '灰'), - (0x2F836, 'M', '及'), - (0x2F837, 'M', '叟'), - (0x2F838, 'M', '𠭣'), - (0x2F839, 'M', '叫'), - (0x2F83A, 'M', '叱'), - (0x2F83B, 'M', '吆'), - (0x2F83C, 'M', '咞'), - (0x2F83D, 'M', '吸'), - (0x2F83E, 'M', '呈'), - (0x2F83F, 'M', '周'), - (0x2F840, 'M', '咢'), - ] - -def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F841, 'M', '哶'), - (0x2F842, 'M', '唐'), - (0x2F843, 'M', '啓'), - (0x2F844, 'M', '啣'), - (0x2F845, 'M', '善'), - (0x2F847, 'M', '喙'), - (0x2F848, 'M', '喫'), - (0x2F849, 'M', '喳'), - (0x2F84A, 'M', '嗂'), - (0x2F84B, 'M', '圖'), - (0x2F84C, 'M', '嘆'), - (0x2F84D, 'M', '圗'), - (0x2F84E, 'M', '噑'), - (0x2F84F, 'M', '噴'), - (0x2F850, 'M', '切'), - (0x2F851, 'M', '壮'), - (0x2F852, 'M', '城'), - (0x2F853, 'M', '埴'), - (0x2F854, 'M', '堍'), - (0x2F855, 'M', '型'), - (0x2F856, 'M', '堲'), - (0x2F857, 'M', '報'), - (0x2F858, 'M', '墬'), - (0x2F859, 'M', '𡓤'), - (0x2F85A, 'M', '売'), - (0x2F85B, 'M', '壷'), - (0x2F85C, 'M', '夆'), - (0x2F85D, 'M', '多'), - (0x2F85E, 'M', '夢'), - (0x2F85F, 'M', '奢'), - (0x2F860, 'M', '𡚨'), - (0x2F861, 'M', '𡛪'), - (0x2F862, 'M', '姬'), - (0x2F863, 'M', '娛'), - (0x2F864, 'M', '娧'), - (0x2F865, 'M', '姘'), - (0x2F866, 'M', '婦'), - (0x2F867, 'M', '㛮'), - (0x2F868, 'X'), - (0x2F869, 'M', '嬈'), - (0x2F86A, 'M', '嬾'), - (0x2F86C, 'M', '𡧈'), - (0x2F86D, 'M', '寃'), - (0x2F86E, 'M', '寘'), - (0x2F86F, 'M', '寧'), - (0x2F870, 'M', '寳'), - (0x2F871, 'M', '𡬘'), - (0x2F872, 'M', '寿'), - (0x2F873, 'M', '将'), - (0x2F874, 'X'), - (0x2F875, 'M', '尢'), - (0x2F876, 'M', '㞁'), - (0x2F877, 'M', '屠'), - (0x2F878, 'M', '屮'), - (0x2F879, 'M', '峀'), - (0x2F87A, 'M', '岍'), - (0x2F87B, 'M', '𡷤'), - (0x2F87C, 'M', '嵃'), - (0x2F87D, 'M', '𡷦'), - (0x2F87E, 'M', '嵮'), - (0x2F87F, 'M', '嵫'), - (0x2F880, 'M', '嵼'), - (0x2F881, 'M', '巡'), - (0x2F882, 'M', '巢'), - (0x2F883, 'M', '㠯'), - (0x2F884, 'M', '巽'), - (0x2F885, 'M', '帨'), - (0x2F886, 'M', '帽'), - (0x2F887, 'M', '幩'), - (0x2F888, 'M', '㡢'), - (0x2F889, 'M', '𢆃'), - (0x2F88A, 'M', '㡼'), - (0x2F88B, 'M', '庰'), - (0x2F88C, 'M', '庳'), - (0x2F88D, 'M', '庶'), - (0x2F88E, 'M', '廊'), - (0x2F88F, 'M', '𪎒'), - (0x2F890, 'M', '廾'), - (0x2F891, 'M', '𢌱'), - (0x2F893, 'M', '舁'), - (0x2F894, 'M', '弢'), - (0x2F896, 'M', '㣇'), - (0x2F897, 'M', '𣊸'), - (0x2F898, 'M', '𦇚'), - (0x2F899, 'M', '形'), - (0x2F89A, 'M', '彫'), - (0x2F89B, 'M', '㣣'), - (0x2F89C, 'M', '徚'), - (0x2F89D, 'M', '忍'), - (0x2F89E, 'M', '志'), - (0x2F89F, 'M', '忹'), - (0x2F8A0, 'M', '悁'), - (0x2F8A1, 'M', '㤺'), - (0x2F8A2, 'M', '㤜'), - (0x2F8A3, 'M', '悔'), - (0x2F8A4, 'M', '𢛔'), - (0x2F8A5, 'M', '惇'), - (0x2F8A6, 'M', '慈'), - (0x2F8A7, 'M', '慌'), - (0x2F8A8, 'M', '慎'), - ] - -def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F8A9, 'M', '慌'), - (0x2F8AA, 'M', '慺'), - (0x2F8AB, 'M', '憎'), - (0x2F8AC, 'M', '憲'), - (0x2F8AD, 'M', '憤'), - (0x2F8AE, 'M', '憯'), - (0x2F8AF, 'M', '懞'), - (0x2F8B0, 'M', '懲'), - (0x2F8B1, 'M', '懶'), - (0x2F8B2, 'M', '成'), - (0x2F8B3, 'M', '戛'), - (0x2F8B4, 'M', '扝'), - (0x2F8B5, 'M', '抱'), - (0x2F8B6, 'M', '拔'), - (0x2F8B7, 'M', '捐'), - (0x2F8B8, 'M', '𢬌'), - (0x2F8B9, 'M', '挽'), - (0x2F8BA, 'M', '拼'), - (0x2F8BB, 'M', '捨'), - (0x2F8BC, 'M', '掃'), - (0x2F8BD, 'M', '揤'), - (0x2F8BE, 'M', '𢯱'), - (0x2F8BF, 'M', '搢'), - (0x2F8C0, 'M', '揅'), - (0x2F8C1, 'M', '掩'), - (0x2F8C2, 'M', '㨮'), - (0x2F8C3, 'M', '摩'), - (0x2F8C4, 'M', '摾'), - (0x2F8C5, 'M', '撝'), - (0x2F8C6, 'M', '摷'), - (0x2F8C7, 'M', '㩬'), - (0x2F8C8, 'M', '敏'), - (0x2F8C9, 'M', '敬'), - (0x2F8CA, 'M', '𣀊'), - (0x2F8CB, 'M', '旣'), - (0x2F8CC, 'M', '書'), - (0x2F8CD, 'M', '晉'), - (0x2F8CE, 'M', '㬙'), - (0x2F8CF, 'M', '暑'), - (0x2F8D0, 'M', '㬈'), - (0x2F8D1, 'M', '㫤'), - (0x2F8D2, 'M', '冒'), - (0x2F8D3, 'M', '冕'), - (0x2F8D4, 'M', '最'), - (0x2F8D5, 'M', '暜'), - (0x2F8D6, 'M', '肭'), - (0x2F8D7, 'M', '䏙'), - (0x2F8D8, 'M', '朗'), - (0x2F8D9, 'M', '望'), - (0x2F8DA, 'M', '朡'), - (0x2F8DB, 'M', '杞'), - (0x2F8DC, 'M', '杓'), - (0x2F8DD, 'M', '𣏃'), - (0x2F8DE, 'M', '㭉'), - (0x2F8DF, 'M', '柺'), - (0x2F8E0, 'M', '枅'), - (0x2F8E1, 'M', '桒'), - (0x2F8E2, 'M', '梅'), - (0x2F8E3, 'M', '𣑭'), - (0x2F8E4, 'M', '梎'), - (0x2F8E5, 'M', '栟'), - (0x2F8E6, 'M', '椔'), - (0x2F8E7, 'M', '㮝'), - (0x2F8E8, 'M', '楂'), - (0x2F8E9, 'M', '榣'), - (0x2F8EA, 'M', '槪'), - (0x2F8EB, 'M', '檨'), - (0x2F8EC, 'M', '𣚣'), - (0x2F8ED, 'M', '櫛'), - (0x2F8EE, 'M', '㰘'), - (0x2F8EF, 'M', '次'), - (0x2F8F0, 'M', '𣢧'), - (0x2F8F1, 'M', '歔'), - (0x2F8F2, 'M', '㱎'), - (0x2F8F3, 'M', '歲'), - (0x2F8F4, 'M', '殟'), - (0x2F8F5, 'M', '殺'), - (0x2F8F6, 'M', '殻'), - (0x2F8F7, 'M', '𣪍'), - (0x2F8F8, 'M', '𡴋'), - (0x2F8F9, 'M', '𣫺'), - (0x2F8FA, 'M', '汎'), - (0x2F8FB, 'M', '𣲼'), - (0x2F8FC, 'M', '沿'), - (0x2F8FD, 'M', '泍'), - (0x2F8FE, 'M', '汧'), - (0x2F8FF, 'M', '洖'), - (0x2F900, 'M', '派'), - (0x2F901, 'M', '海'), - (0x2F902, 'M', '流'), - (0x2F903, 'M', '浩'), - (0x2F904, 'M', '浸'), - (0x2F905, 'M', '涅'), - (0x2F906, 'M', '𣴞'), - (0x2F907, 'M', '洴'), - (0x2F908, 'M', '港'), - (0x2F909, 'M', '湮'), - (0x2F90A, 'M', '㴳'), - (0x2F90B, 'M', '滋'), - (0x2F90C, 'M', '滇'), - ] - -def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F90D, 'M', '𣻑'), - (0x2F90E, 'M', '淹'), - (0x2F90F, 'M', '潮'), - (0x2F910, 'M', '𣽞'), - (0x2F911, 'M', '𣾎'), - (0x2F912, 'M', '濆'), - (0x2F913, 'M', '瀹'), - (0x2F914, 'M', '瀞'), - (0x2F915, 'M', '瀛'), - (0x2F916, 'M', '㶖'), - (0x2F917, 'M', '灊'), - (0x2F918, 'M', '災'), - (0x2F919, 'M', '灷'), - (0x2F91A, 'M', '炭'), - (0x2F91B, 'M', '𠔥'), - (0x2F91C, 'M', '煅'), - (0x2F91D, 'M', '𤉣'), - (0x2F91E, 'M', '熜'), - (0x2F91F, 'X'), - (0x2F920, 'M', '爨'), - (0x2F921, 'M', '爵'), - (0x2F922, 'M', '牐'), - (0x2F923, 'M', '𤘈'), - (0x2F924, 'M', '犀'), - (0x2F925, 'M', '犕'), - (0x2F926, 'M', '𤜵'), - (0x2F927, 'M', '𤠔'), - (0x2F928, 'M', '獺'), - (0x2F929, 'M', '王'), - (0x2F92A, 'M', '㺬'), - (0x2F92B, 'M', '玥'), - (0x2F92C, 'M', '㺸'), - (0x2F92E, 'M', '瑇'), - (0x2F92F, 'M', '瑜'), - (0x2F930, 'M', '瑱'), - (0x2F931, 'M', '璅'), - (0x2F932, 'M', '瓊'), - (0x2F933, 'M', '㼛'), - (0x2F934, 'M', '甤'), - (0x2F935, 'M', '𤰶'), - (0x2F936, 'M', '甾'), - (0x2F937, 'M', '𤲒'), - (0x2F938, 'M', '異'), - (0x2F939, 'M', '𢆟'), - (0x2F93A, 'M', '瘐'), - (0x2F93B, 'M', '𤾡'), - (0x2F93C, 'M', '𤾸'), - (0x2F93D, 'M', '𥁄'), - (0x2F93E, 'M', '㿼'), - (0x2F93F, 'M', '䀈'), - (0x2F940, 'M', '直'), - (0x2F941, 'M', '𥃳'), - (0x2F942, 'M', '𥃲'), - (0x2F943, 'M', '𥄙'), - (0x2F944, 'M', '𥄳'), - (0x2F945, 'M', '眞'), - (0x2F946, 'M', '真'), - (0x2F948, 'M', '睊'), - (0x2F949, 'M', '䀹'), - (0x2F94A, 'M', '瞋'), - (0x2F94B, 'M', '䁆'), - (0x2F94C, 'M', '䂖'), - (0x2F94D, 'M', '𥐝'), - (0x2F94E, 'M', '硎'), - (0x2F94F, 'M', '碌'), - (0x2F950, 'M', '磌'), - (0x2F951, 'M', '䃣'), - (0x2F952, 'M', '𥘦'), - (0x2F953, 'M', '祖'), - (0x2F954, 'M', '𥚚'), - (0x2F955, 'M', '𥛅'), - (0x2F956, 'M', '福'), - (0x2F957, 'M', '秫'), - (0x2F958, 'M', '䄯'), - (0x2F959, 'M', '穀'), - (0x2F95A, 'M', '穊'), - (0x2F95B, 'M', '穏'), - (0x2F95C, 'M', '𥥼'), - (0x2F95D, 'M', '𥪧'), - (0x2F95F, 'X'), - (0x2F960, 'M', '䈂'), - (0x2F961, 'M', '𥮫'), - (0x2F962, 'M', '篆'), - (0x2F963, 'M', '築'), - (0x2F964, 'M', '䈧'), - (0x2F965, 'M', '𥲀'), - (0x2F966, 'M', '糒'), - (0x2F967, 'M', '䊠'), - (0x2F968, 'M', '糨'), - (0x2F969, 'M', '糣'), - (0x2F96A, 'M', '紀'), - (0x2F96B, 'M', '𥾆'), - (0x2F96C, 'M', '絣'), - (0x2F96D, 'M', '䌁'), - (0x2F96E, 'M', '緇'), - (0x2F96F, 'M', '縂'), - (0x2F970, 'M', '繅'), - (0x2F971, 'M', '䌴'), - (0x2F972, 'M', '𦈨'), - (0x2F973, 'M', '𦉇'), - ] - -def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F974, 'M', '䍙'), - (0x2F975, 'M', '𦋙'), - (0x2F976, 'M', '罺'), - (0x2F977, 'M', '𦌾'), - (0x2F978, 'M', '羕'), - (0x2F979, 'M', '翺'), - (0x2F97A, 'M', '者'), - (0x2F97B, 'M', '𦓚'), - (0x2F97C, 'M', '𦔣'), - (0x2F97D, 'M', '聠'), - (0x2F97E, 'M', '𦖨'), - (0x2F97F, 'M', '聰'), - (0x2F980, 'M', '𣍟'), - (0x2F981, 'M', '䏕'), - (0x2F982, 'M', '育'), - (0x2F983, 'M', '脃'), - (0x2F984, 'M', '䐋'), - (0x2F985, 'M', '脾'), - (0x2F986, 'M', '媵'), - (0x2F987, 'M', '𦞧'), - (0x2F988, 'M', '𦞵'), - (0x2F989, 'M', '𣎓'), - (0x2F98A, 'M', '𣎜'), - (0x2F98B, 'M', '舁'), - (0x2F98C, 'M', '舄'), - (0x2F98D, 'M', '辞'), - (0x2F98E, 'M', '䑫'), - (0x2F98F, 'M', '芑'), - (0x2F990, 'M', '芋'), - (0x2F991, 'M', '芝'), - (0x2F992, 'M', '劳'), - (0x2F993, 'M', '花'), - (0x2F994, 'M', '芳'), - (0x2F995, 'M', '芽'), - (0x2F996, 'M', '苦'), - (0x2F997, 'M', '𦬼'), - (0x2F998, 'M', '若'), - (0x2F999, 'M', '茝'), - (0x2F99A, 'M', '荣'), - (0x2F99B, 'M', '莭'), - (0x2F99C, 'M', '茣'), - (0x2F99D, 'M', '莽'), - (0x2F99E, 'M', '菧'), - (0x2F99F, 'M', '著'), - (0x2F9A0, 'M', '荓'), - (0x2F9A1, 'M', '菊'), - (0x2F9A2, 'M', '菌'), - (0x2F9A3, 'M', '菜'), - (0x2F9A4, 'M', '𦰶'), - (0x2F9A5, 'M', '𦵫'), - (0x2F9A6, 'M', '𦳕'), - (0x2F9A7, 'M', '䔫'), - (0x2F9A8, 'M', '蓱'), - (0x2F9A9, 'M', '蓳'), - (0x2F9AA, 'M', '蔖'), - (0x2F9AB, 'M', '𧏊'), - (0x2F9AC, 'M', '蕤'), - (0x2F9AD, 'M', '𦼬'), - (0x2F9AE, 'M', '䕝'), - (0x2F9AF, 'M', '䕡'), - (0x2F9B0, 'M', '𦾱'), - (0x2F9B1, 'M', '𧃒'), - (0x2F9B2, 'M', '䕫'), - (0x2F9B3, 'M', '虐'), - (0x2F9B4, 'M', '虜'), - (0x2F9B5, 'M', '虧'), - (0x2F9B6, 'M', '虩'), - (0x2F9B7, 'M', '蚩'), - (0x2F9B8, 'M', '蚈'), - (0x2F9B9, 'M', '蜎'), - (0x2F9BA, 'M', '蛢'), - (0x2F9BB, 'M', '蝹'), - (0x2F9BC, 'M', '蜨'), - (0x2F9BD, 'M', '蝫'), - (0x2F9BE, 'M', '螆'), - (0x2F9BF, 'X'), - (0x2F9C0, 'M', '蟡'), - (0x2F9C1, 'M', '蠁'), - (0x2F9C2, 'M', '䗹'), - (0x2F9C3, 'M', '衠'), - (0x2F9C4, 'M', '衣'), - (0x2F9C5, 'M', '𧙧'), - (0x2F9C6, 'M', '裗'), - (0x2F9C7, 'M', '裞'), - (0x2F9C8, 'M', '䘵'), - (0x2F9C9, 'M', '裺'), - (0x2F9CA, 'M', '㒻'), - (0x2F9CB, 'M', '𧢮'), - (0x2F9CC, 'M', '𧥦'), - (0x2F9CD, 'M', '䚾'), - (0x2F9CE, 'M', '䛇'), - (0x2F9CF, 'M', '誠'), - (0x2F9D0, 'M', '諭'), - (0x2F9D1, 'M', '變'), - (0x2F9D2, 'M', '豕'), - (0x2F9D3, 'M', '𧲨'), - (0x2F9D4, 'M', '貫'), - (0x2F9D5, 'M', '賁'), - (0x2F9D6, 'M', '贛'), - (0x2F9D7, 'M', '起'), - ] - -def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: - return [ - (0x2F9D8, 'M', '𧼯'), - (0x2F9D9, 'M', '𠠄'), - (0x2F9DA, 'M', '跋'), - (0x2F9DB, 'M', '趼'), - (0x2F9DC, 'M', '跰'), - (0x2F9DD, 'M', '𠣞'), - (0x2F9DE, 'M', '軔'), - (0x2F9DF, 'M', '輸'), - (0x2F9E0, 'M', '𨗒'), - (0x2F9E1, 'M', '𨗭'), - (0x2F9E2, 'M', '邔'), - (0x2F9E3, 'M', '郱'), - (0x2F9E4, 'M', '鄑'), - (0x2F9E5, 'M', '𨜮'), - (0x2F9E6, 'M', '鄛'), - (0x2F9E7, 'M', '鈸'), - (0x2F9E8, 'M', '鋗'), - (0x2F9E9, 'M', '鋘'), - (0x2F9EA, 'M', '鉼'), - (0x2F9EB, 'M', '鏹'), - (0x2F9EC, 'M', '鐕'), - (0x2F9ED, 'M', '𨯺'), - (0x2F9EE, 'M', '開'), - (0x2F9EF, 'M', '䦕'), - (0x2F9F0, 'M', '閷'), - (0x2F9F1, 'M', '𨵷'), - (0x2F9F2, 'M', '䧦'), - (0x2F9F3, 'M', '雃'), - (0x2F9F4, 'M', '嶲'), - (0x2F9F5, 'M', '霣'), - (0x2F9F6, 'M', '𩅅'), - (0x2F9F7, 'M', '𩈚'), - (0x2F9F8, 'M', '䩮'), - (0x2F9F9, 'M', '䩶'), - (0x2F9FA, 'M', '韠'), - (0x2F9FB, 'M', '𩐊'), - (0x2F9FC, 'M', '䪲'), - (0x2F9FD, 'M', '𩒖'), - (0x2F9FE, 'M', '頋'), - (0x2FA00, 'M', '頩'), - (0x2FA01, 'M', '𩖶'), - (0x2FA02, 'M', '飢'), - (0x2FA03, 'M', '䬳'), - (0x2FA04, 'M', '餩'), - (0x2FA05, 'M', '馧'), - (0x2FA06, 'M', '駂'), - (0x2FA07, 'M', '駾'), - (0x2FA08, 'M', '䯎'), - (0x2FA09, 'M', '𩬰'), - (0x2FA0A, 'M', '鬒'), - (0x2FA0B, 'M', '鱀'), - (0x2FA0C, 'M', '鳽'), - (0x2FA0D, 'M', '䳎'), - (0x2FA0E, 'M', '䳭'), - (0x2FA0F, 'M', '鵧'), - (0x2FA10, 'M', '𪃎'), - (0x2FA11, 'M', '䳸'), - (0x2FA12, 'M', '𪄅'), - (0x2FA13, 'M', '𪈎'), - (0x2FA14, 'M', '𪊑'), - (0x2FA15, 'M', '麻'), - (0x2FA16, 'M', '䵖'), - (0x2FA17, 'M', '黹'), - (0x2FA18, 'M', '黾'), - (0x2FA19, 'M', '鼅'), - (0x2FA1A, 'M', '鼏'), - (0x2FA1B, 'M', '鼖'), - (0x2FA1C, 'M', '鼻'), - (0x2FA1D, 'M', '𪘀'), - (0x2FA1E, 'X'), - (0x30000, 'V'), - (0x3134B, 'X'), - (0x31350, 'V'), - (0x323B0, 'X'), - (0xE0100, 'I'), - (0xE01F0, 'X'), - ] - -uts46data = tuple( - _seg_0() - + _seg_1() - + _seg_2() - + _seg_3() - + _seg_4() - + _seg_5() - + _seg_6() - + _seg_7() - + _seg_8() - + _seg_9() - + _seg_10() - + _seg_11() - + _seg_12() - + _seg_13() - + _seg_14() - + _seg_15() - + _seg_16() - + _seg_17() - + _seg_18() - + _seg_19() - + _seg_20() - + _seg_21() - + _seg_22() - + _seg_23() - + _seg_24() - + _seg_25() - + _seg_26() - + _seg_27() - + _seg_28() - + _seg_29() - + _seg_30() - + _seg_31() - + _seg_32() - + _seg_33() - + _seg_34() - + _seg_35() - + _seg_36() - + _seg_37() - + _seg_38() - + _seg_39() - + _seg_40() - + _seg_41() - + _seg_42() - + _seg_43() - + _seg_44() - + _seg_45() - + _seg_46() - + _seg_47() - + _seg_48() - + _seg_49() - + _seg_50() - + _seg_51() - + _seg_52() - + _seg_53() - + _seg_54() - + _seg_55() - + _seg_56() - + _seg_57() - + _seg_58() - + _seg_59() - + _seg_60() - + _seg_61() - + _seg_62() - + _seg_63() - + _seg_64() - + _seg_65() - + _seg_66() - + _seg_67() - + _seg_68() - + _seg_69() - + _seg_70() - + _seg_71() - + _seg_72() - + _seg_73() - + _seg_74() - + _seg_75() - + _seg_76() - + _seg_77() - + _seg_78() - + _seg_79() - + _seg_80() - + _seg_81() -) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/.venv/lib/python3.12/site-packages/pip-24.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/pip-24.2.dist-info/RECORD index 9c347b8..175b46d 100644 --- a/.venv/lib/python3.12/site-packages/pip-24.2.dist-info/RECORD +++ b/.venv/lib/python3.12/site-packages/pip-24.2.dist-info/RECORD @@ -1,6 +1,6 @@ -../../../bin/pip,sha256=DYaCRkqNIIndPxYPZQWkWCV23HZan2xAXzE3ZHvRuU4,248 -../../../bin/pip3,sha256=DYaCRkqNIIndPxYPZQWkWCV23HZan2xAXzE3ZHvRuU4,248 -../../../bin/pip3.12,sha256=DYaCRkqNIIndPxYPZQWkWCV23HZan2xAXzE3ZHvRuU4,248 +../../../bin/pip,sha256=5ixYpWWXrfyv7wMFkzp5XlQZvZgi6J31pSyXRqpmvXs,257 +../../../bin/pip3,sha256=5ixYpWWXrfyv7wMFkzp5XlQZvZgi6J31pSyXRqpmvXs,257 +../../../bin/pip3.12,sha256=5ixYpWWXrfyv7wMFkzp5XlQZvZgi6J31pSyXRqpmvXs,257 pip-24.2.dist-info/AUTHORS.txt,sha256=KDa8Pd3GDeKSogF6yFW0l9A9eMneLDOFrcIDqkL8G8s,10868 pip-24.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 pip-24.2.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093 diff --git a/.venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc index f308b3d8ef79209ccc88f310683ca0f587704ea5..0d473dcf96ccd5364021cee3e72de22dab1853c7 100644 GIT binary patch delta 44 zcmZ3@x`mbdG%qg~0}xz~xv`OZF(YHxcASm-G%qg~0}y-+T)mOonn~JMKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*jw G(=!07ff1ts diff --git a/.venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc index f6bb3314b4b014c059c1111ae049861434abe763..3c80fd928f4477a76bd20370a7789f385d703161 100644 GIT binary patch delta 62 zcmbOwxLA<;G%qg~0}xz~xv`O(gIOg;KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>9&-*W0AQUIy8r+H delta 53 zcmZ21I7^WGG%qg~0}y-+T)mN-gIPLQKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--< Ha}Fy2rwG%qg~0}xz~xv`P^8ly^renx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMKa8S`0DWN<)Bpeg delta 53 zcmbQmHj$0{G%qg~0}y-+T)mO|8l!Z$enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?zX HCQ(KJx$Y3g diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/build_env.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/build_env.cpython-312.pyc index bd9e8796d8c2f56ca1f9f434f7da064a1fb93e80..f1cfe077c3df88beda16260db9711901ba1f6531 100644 GIT binary patch delta 62 zcmdl~xVw=1G%qg~0}xz~xv`P^II~Kkenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMx6DUP0KSVCwg3PC delta 53 zcmdm8xT%o)G%qg~0}y-+T)mO|IJ0zwenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{MG H%tuWC^{Er5 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc index 41b7134d6c45e478902ae234dc167a2bea6245a5..dd4e82354309da2ee002e73103bcc7fac2a2d06a 100644 GIT binary patch delta 62 zcmeyK)R4@5nwOW00SKb8xE^z3Blk5nl_dR){M=OiqJq?tqWrSVC;>~~9{x$;u+~yd) delta 55 zcmZpg#`JS46ZdIeUM>b8_!zi)Blk5n=}7&I{M=OiqJq?tqWrSVM6R!XO diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc index aea8abd171e45d2ae73ff09c3e0b7af4d065637b..be2921936ba61ddd8919150fb0a03263de1b1916 100644 GIT binary patch delta 61 zcmbQkx|EgsG%qg~0}xz~xv`OZ0i#Ncenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- PPGVkaerfUKV~p1TVk{OI delta 52 zcmZ3=I)|0}G%qg~0}y-+T)mNd0i$%Venx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Hz GjMo6D8xjcs diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc index 947e5c94ce440dc89f41e5748fb1453bf39bdd6d..c281e37411c305098618df491489e76f7899009d 100644 GIT binary patch delta 62 zcmZqIn4rOZnwOW00SKIUBD&0HJUdtpET3 delta 53 zcmX?`btH@XG%qg~0}y-+T)mN7kWD&9KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--J H8?Qb9-R}{e diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc index 93ddd94de534e5981159756ed99eb802100d307c..402f9e752a8d0b9199e143c8c016d2540651f0a7 100644 GIT binary patch delta 59 zcmbQjG?$6{G%qg~0}xz~xiOKuO(jJ?BR@A)zo;O!q$s~EGdV*)IX@*;KR2^DIW;FI NF)ua0w0Pp03;;PJ6+{34 delta 50 zcmbQsG=+)#G%qg~0}y-+Ts@JyO*&dXBR@A)zo;O!q$s~EGdV*)IX@*;-#4>3dE(X# E0EN&IH2?qr diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc index 00caed39f15a6a722f86a44ac2e712d076d96579..3e2158978939b5a26288d9cd22556902bcb768f1 100644 GIT binary patch delta 45 zcmdnyyx*DoG%qg~0}xz~xv`O3g^w|7vH_nMZ*FFBa%xUaVqR)~Y4K(+z8qlyJ$4RE delta 36 qcmdn*yv>>WG%qg~0}y-+T)mN7g^w|DvH_nMi*IIe^5$T^9AN;%5(+&4 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc index b4028cf67847b443b0421747692d2ba78c5e7073..6e8b72d585b6fa980063f27818b66b37c51476c1 100644 GIT binary patch delta 45 zcmccZ|HPmBG%qg~0}xz~xv`OZ3I}7x-bf3a diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc index a287ce5d16c51b3a297ccfc9f8e1a6df8eb0ecdb..11ed56e279e3cf16b1b7ca2d9166d3966553f598 100644 GIT binary patch delta 64 zcmdn4byfO0FIRv*Z=?k delta 53 zcmeyv`gB&JhvG diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/index_command.cpython-312.pyc index 5f9e59543532ca4118aee2cfaf1befe250a4a57f..2ee4d7ac8130e6724748603bf405674051caf0d2 100644 GIT binary patch delta 62 zcmca>{>+^FG%qg~0}xz~xv`P^9IHyEenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMFRU*`0lM}Vw*UYD delta 53 zcmaE6e%GA)G%qg~0}y-+T)mO|9IJGKenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{NB HtS?0Y@`)3u diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc index 2834bd12f3a4d23b5d6341dd464bd30b478bd704..41199201aba373fe430ba9c17340ea28553a370e 100644 GIT binary patch delta 62 zcmew**et|-nwOW00SK{o0E6Zg`~Uy| delta 53 zcmdm>wp@+-G%qg~0}y-+T)mMykyScQKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=8~ H)&;x(#9k5Y diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc index 89e95ba82c9f9dacbbddbd9cc9e4a0d9cc908009..1ee91b87fe7789f4672adc1fa312d24af0d0c690 100644 GIT binary patch delta 62 zcmcan`mmJyG%qg~0}xz~xv`PEokb-{KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsS{7|H0L82p^#A|> delta 53 zcmaD@dZU#4G%qg~0}y-+T)mOIokcoQKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9n H7Hu;C`~(u_ diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc index 59e6463ed2db5762834b6adf1af231ca018f3572..eb211a22ca49504987d5e8860b27ececb660af3e 100644 GIT binary patch delta 45 zcmeB`n~KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBIhQ6Y0D6iQO#lD@ delta 53 zcmZ1}zd)Y*G%qg~0}y-+T)mMykV`s2KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*me HmnJI!yiXB6 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc index 13841b98de24d3b6ea148980a04f02b0e945a549..03f1b7dc0e1c2fb71696fae54fd0839ab209e6d3 100644 GIT binary patch delta 62 zcmaFs{mq;EG%qg~0}xz~xv`PkkWD2`KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBINLON0Ii)CdjJ3c delta 53 zcmez7{nnfNG%qg~0}y-+T)mOokWD&PKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*md H+cbFq=syx{ diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc index 7c300b3796cd42f1fcade9d9725c5323fe6e0076..ebceda98522503ced1fb8a552463216de2e8cf7a 100644 GIT binary patch delta 62 zcmdlcvR{PzG%qg~0}xz~xv`O3icKX=KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>1KT%d0Cn>fwEzGB delta 53 zcmdllvQ32hG%qg~0}y-+T)mN7icLCJKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--t H+c#zaxgZgy diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc index 1b970269acac2b51acaa163ecf2661fdfabb6f05..5ea58f7cf8da412c7c1eba71e31eba876af24e36 100644 GIT binary patch delta 62 zcmX@6abAP_G%qg~0}xz~xv`OZ9gj-3enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMD?CN)0Hcr=wg3PC delta 53 zcmX@FaZH2zG%qg~0}y-+T)mNd9glR9enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{N7 HJVopP-kuVt diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc index 169579de4c78a8363b566e015c97402e9bde75f5..562a8b47493d7cec8fe41d426f8a3b6bcc943244 100644 GIT binary patch delta 62 zcmbQ0zBrxxG%qg~0}xz~xv`Nunp-7LKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBBe$dm0Gvz}{{R30 delta 53 zcmZ3SJ}aI3G%qg~0}y-+T)mMynp-+mKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*my Hx13&%*?9-0Dm+UR{#J2 delta 53 zcmeD5Z}8_n&CAQh00bWcS8wF@WtNWB&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTpL@ H%&P(bz*iAS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc index 5a66cf8694d982eda2c5e2ee785a5fafea334dd6..b3ec53dcd402c7eaf8a1187d195d8060263fbee6 100644 GIT binary patch delta 62 zcmca)b=!*jG%qg~0}xz~xv`O(kxwO4KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>I-fQ_0FcZTTL1t6 delta 53 zcmca@b;*kRG%qg~0}y-+T)mN-kxx27KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--H HpEf@L&(jf1 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc index 6a059c5a83f48485dbf748b1768430eb285a41e1..19dc6331892bfdd4a19510f27aff2a5f5f7da69f 100644 GIT binary patch delta 62 zcmZ3Yv{s4xG%qg~0}xz~xv`OZBBM&Wenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM?Toqn0Em1R6#xJL delta 53 zcmZ3hv_y&fG%qg~0}y-+T)mNdBBOMienx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{K^ HjJf;($L10S diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc index 2fcf158dc19aa8b518bb07350b6711e34e28eaa1..640aff435bddac2b81dd101d827b79dfaccf38f2 100644 GIT binary patch delta 45 zcmZ1~zD}I`G%qg~0}xz~xv`Pkg@rM7asZ1MZ*FFBa%xUaVqR)~Y4PSP7Hf6@ESwF} delta 36 qcmZ1{zEqt1G%qg~0}y-+T)mOog@rL@asZ1Mi*IIe^5$X|Yjyy&BMHL* diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc index 842543feb6b106ee2c0ee3a1b7f8f667df143afc..4b3972c518752ca75c32569651d3c776cc91b382 100644 GIT binary patch delta 45 zcmZqYoxsa|nwOW00SK5e$7;a^0A-34&;S4c delta 53 zcmeB|Z%?- diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc index e72763fb4e712d75df586dd47d765dc5762b6b9d..6e44d138924e39366197f940dcf2684455aa74e9 100644 GIT binary patch delta 64 zcmaF)nDNtNM()$Ryj%=Ga6RV6M(%$KDjE72`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu TsW~}`d8zrO#hX=efU diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc index 919b6b627b827ae975727cee28417ba44983d173..3c7e097f961b967c09dfea51fd635986047ebc47 100644 GIT binary patch delta 62 zcmbPMJ-?d!G%qg~0}xz~xv`Nufkh=%KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB8;gJ?0HOdE8UO$Q delta 53 zcmbPVJ*}GiG%qg~0}y-+T)mMyfkiq-KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*nF Hi-08n-P95b diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc index 82c5c27b54fff094885720e30afbf8a513a85b58..6fa97c728c8c1aa7e63f9581731b0cce6d56b969 100644 GIT binary patch delta 45 zcmexv)nLtinwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*J&U&r0H{P3hX4Qo delta 53 zcmZn;`W481nwOW00SG<@uHMMq$s!%2pOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoVmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*4*PvB0FrwajsO4v delta 53 zcmZos{jI`%nwOW00SG<@uHML9#x9+xpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoVR delta 53 zcmdnwy3&>VG%qg~0}y-+T)mOoM?gAOKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*om HfVmI=&i@g% diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc index f0eeb3c6056210b9ef62ca69ec1ca82dfbdc77d1..8e06aa4192774c764e48e97c180bbccec9a05b46 100644 GIT binary patch delta 62 zcmZ3@zJ;CpG%qg~0}xz~xv`P^JfljUenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMuZ-Im0gP=Hs{jB1 delta 53 zcmdnOzM7r;G%qg~0}y-+T)mO|Jfn1~enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{MW HjN2Fi%F+^? diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc index 5cc0dacd978152b30046efa42a1d4847681686e2..bc83ac2c527c303aa34b43d11e464f9d4f86d80b 100644 GIT binary patch delta 62 zcmX>tc14W)G%qg~0}xz~xv`O3id7{`KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>11mQh0Dp)S{Qv*} delta 53 zcmca2c3O=4G%qg~0}y-+T)mN7id8yMKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--t HD>oYe!2A&I diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc index 285459507f6f08314f5e5787daa36dba157ba006..bff3ba3a51418cf9e553aae91aa29a23bbf0bf1a 100644 GIT binary patch delta 61 zcmZ3)yPB8#G%qg~0}xz~xv`L&NhM!DBR@A)zo;O!q$s~EGdV*)IX@*;KR2^DIW;FI PF)ua0w0N@`Qw<9MTo4q) delta 52 zcmZ3@yNH+jG%qg~0}y-+T)mK+NjgnGBR@A)zo;O!q$s~EGdV*)IX@*;-#4>3d9yxK G4GRFGju5l} diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc index 69b0466d9a8e0d60ff09e6880f696d5b4d22511a..c3dccbc28532d93993bf2c84ba42cf69fee60ae3 100644 GIT binary patch delta 62 zcmZ4Bw9<+DG%qg~0}xz~xv`PkkVPe1KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBILk>H0ELGZ00000 delta 53 zcmZ4Kw7`k`G%qg~0}y-+T)mOokVQI4KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*md H%Sjmk#u^dv diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc index 3fcb71a980d3f46b50bef504023a73e8732ac7f9..ef451d8a61c1dcfe846a9b6021cad0717c9b93ae 100644 GIT binary patch delta 62 zcmaDZ_(72SG%qg~0}xz~xv`Pkk4Yt4KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB2~#l}0F<{C_5c6? delta 53 zcmew$_*{_tG%qg~0}y-+T)mOok4ZX7KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m| HQ!yI=(#sL& diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc index 850a4320db50c688b1c2155b5f11547d94082cac..40582d20cae4898093741d6717d0578cc8b4fb74 100644 GIT binary patch delta 59 zcmaFH_@0sbG%qg~0}xz~xiOJDOeIY}BR@A)zo;O!q$s~EGdV*)IX@*;KR2^DIW;FI NF)ua0w0L4o764Wm6^Z}= delta 50 zcmaFQ_>7VJG%qg~0}y-+Ts@IHOgdISBR@A)zo;O!q$s~EGdV*)IX@*;-#4>3d17l8 E0G_fDdjJ3c diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc index 5351b905647e82d7ab35db1a2ed64c1f8ee74b30..42d42cd08b4326660075ca73e28e3abecfed807d 100644 GIT binary patch delta 64 zcmeyqg0W#GBll@uUM>b8xE^z3BX=*GO1geVer~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E SYEDjKUTS`6@#f8JM|=U-2^Zl2 delta 55 zcmZoz$@qN*Bll@uUM>b8_!zi)BX=*Gbew)her~FMQ9)`+QGQuwa)y3#eoCsoZ)S1w J<~?jjd;tn56VdC;>|W(Yo-7I16$k{M=OiqJq?tqWrSVGk^3|+FBbz4T#vc2k=uboB}+deKQ~pss35hZD8DQ-IYU1=KP6Q^H?uf7 SH76%AFEzikcyk)ZZF2y+HWv~A delta 55 zcmX@rz<98Mk^3|+FBbz4d<mSKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m> HXPO`Y(Zdna diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc index 1e4dcca991e7222d5458fa1465d7596011c5c0f1..5f7fbdb772e9b46737ef250fec02c03f89299643 100644 GIT binary patch delta 45 zcmca__ri|*G%qg~0}xz~xv`Pkh@CNKvK_k^Z*FFBa%xUaVqR)~Y4PR=c6JE>O4ANY delta 36 qcmaE1ci)ctG%qg~0}y-+T)mOoh@CNcvK_k^i*IIe^5#T#b_oE@R|-4; diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc index 5d122b70c0d400e9baef6bfd84ab448790c94f02..1168c76881feee44d354ccd3f5e997fc3c2e9f4a 100644 GIT binary patch delta 62 zcmX>sdq$S~G%qg~0}xz~xv`P^CzDE=enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$dFCbD0HoX&P5=M^ delta 53 zcmX>jdsvqHG%qg~0}y-+T)mO|CzEunenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?yj H<|W(!-pUd` diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc index e9b57c9f4ed055e25523229384dacfd73a9eedcd..9e140f651b68228ff401d37f2b560f4cf93a7e0a 100644 GIT binary patch delta 62 zcmeyN+o;EVnwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}A`m3=cm0EBrJ!~g&Q delta 53 zcmZqF`=QHynwOW00SG<@uHMKk&n}&ypOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV?kO HeKS7*#Ap$< diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc index d9d3270c2422ac024dc3de6552921e16e8100469..9b065f2552ac44bfc7ae780b554f82842e55f788 100644 GIT binary patch delta 62 zcmew$_EU`eG%qg~0}xz~xv`O3mRTiDKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>GqWxS0FeL{X#fBK delta 53 zcmew<_CbvMG%qg~0}y-+T)mN7mRUMhKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--V Hvn~e!&-D>n diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc index 88b51f51c107acda9c15bce8128cc28de0cbd814..6b5311979d914a31c3c8db3c25e76a7ee1b94582 100644 GIT binary patch delta 64 zcmbO>nQ8uHChpU`yj%=Ga6RV6M(zhJDyjMz`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu TsW~}`d8zrO#hck!C$sG%qg~0}y-+T)mO|8?$t(enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?zW HmeW=M6y_75 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc index 64d28562247ed039ffad0f29472828b6fbfd2a49..d54f621891b23d5eeeda778964407f5a1bea4f84 100644 GIT binary patch delta 59 zcmaFD^p=VHG%qg~0}xz~xiOJ@k4mwAMt*Lpeo;YcNl|`TW^#spa(+sxer{%Qa%xUa NVqR)~Y4OBIO#o=v7I^>w delta 50 zcmaFM^n{7~G%qg~0}y-+Ts@I{k93xPMt*Lpeo;YcNl|`TW^#spa(+sxzHer6^2FCo E0IhfuY5)KL diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc index f762250031a5b38609f2952a67eef448704688d0..e83add762872804ed84d8434155eab622cd8f1bd 100644 GIT binary patch delta 62 zcmeBFo}|ounwOW00SKt<8 delta 53 zcmbQF+^NicnwOW00SG<@uHMK!he4j#v-o{0EE>QcmMzZ delta 53 zcmeARZw==@&CAQh00bWcS8wEYV3AJO&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTri) HBCier#UK%B diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc index 733d7a46f275a15f1355bf65feb9a577c486e629..ce1ef15971669b4527a3767d7f3717aba552c616 100644 GIT binary patch delta 59 zcmeBRn#ja`nwOW00SKY5)KL delta 53 zcmX@jbBKrgG%qg~0}y-+T)mO|Ad_^wenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{L5 HOwr5$(zg;@ diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc index 890af918edf3e3855ea849effa1466536376db69..af4664eb5cfebff0a22cf54cfe2676ed0f3c631a 100644 GIT binary patch delta 62 zcmcZ_axaAYG%qg~0}xz~xv`Pkj9Dd9KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB4D(Ml0H_ld&Hw-a delta 53 zcmcZ?ay5keG%qg~0}y-+T)mOoj9EHCKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m_ H^G`JZ{8@D&CAQh00h@#ZfxXkV^Yc0&&bbB)h{YYEh)+`%S_JDPtH$C)z8f=PEO6q QNz6;lFD>4@hUqFV0CzYRB>(^b delta 53 zcmeBDY*gev&CAQh00bWcS8wEQW0FqM&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTstg H=_)S(xlR%m diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc index d8c1365681a908106fbfd038c328dac4398bf98f..9d73a20ed84685bd59397a1433a777afc6d2215f 100644 GIT binary patch delta 62 zcmbQkyOfvvG%qg~0}xz~xv`O(i&-T_KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>0do#B0AW`Y!2kdN delta 53 zcmZ3=JBOG1G%qg~0}y-+T)mN-i&;8aKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--r Ha}F~Ar;`w} diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc index 378eb92bf5b9594b8003fa86c56be6518e2752d1..bd4da88c9bcae0729f2e8ad79f301708205d6839 100644 GIT binary patch delta 45 zcmca7_*{_tG%qg~0}xz~xv`P^2qRmYJNPpPZkPs-K%#oSd4I QlbDy9Us}AGhpmhk0Ehn-nE(I) delta 53 zcmeBHZ&K$z&CAQh00bWcS8wEg#wwkppOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV;0- Ht&A4{#x4^ySenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$HufHV0FaCo%m4rY delta 53 zcmbQOJ4u)OG%qg~0}y-+T)mO|0h@HRenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z< H_8xu!&6^Ru diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc index 44472d6007cd0f68c786e04b75f7824ab710c1ea..1f8fd6745f92e3351d6d568b0619d41b815eed0e 100644 GIT binary patch delta 59 zcmeyv*viCxnwOW00SKC;?0FDF9QMGCl|~B delta 55 zcmaF7hVj-KM()$Ryj%=G@G)@pMs9Bw>1h3o{M=OiqJq?tqWrSVFO#}B0HKr>!vFvP delta 53 zcmexs^u>t#G%qg~0}y-+T)mN7ok==IKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mz Hle#zn-T@J` diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc index d95e5e060bd58d157ad58e7f19004725e4671984..eab5998eb421dcd0407fdfff98b16fadf7d9eae5 100644 GIT binary patch delta 62 zcmdnxbl8deG%qg~0}xz~xv`OZC9_I~enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM^UT?@0H+5Q!TEOUVt0H`Y#NdN!< delta 53 zcmbOh-5bq)nwOW00SG<@uHMM~ok==TKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--g HbAc8B;dc@` diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc index 6ca1ce5c4e17051abdea70a20b3a29728051de91..a3f0250aa7846d7409fbba5783e9256d86cf2190 100644 GIT binary patch delta 64 zcmX>wnepUgM()$Ryj%=Ga6RV6M(#v*m2~}#{M=OiqJq?tqWrSVC;?3>smQDc2C>GoR delta 55 zcmX>(neo76M()$Ryj%=G@G)@pM(#v*={Wt2{M=OiqJq?tqWrSVCyt$dh$*DOxiFv8{rNx^$Sx&M509WM? Ah5!Hn delta 36 rcmcaDcuA1^G%qg~0}y-+T)mO|F*9S#CEWVk=$(w~(PO<<1(vk~z diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc index b5ef0b2c9ab8f41609e0bb95663c704711f87385..0e61c67e90aa97239eab1e0c3f30a502c4d3fffc 100644 GIT binary patch delta 62 zcmZn`?-A!d&CAQh00h@#ZfxYf&#aQBpOK%Ns$W!)T2ho>mYJNPpPZkPs-K%#oSd4I QlbDy9Us}AGm8Fgy0Dg)UW&i*H delta 53 zcmeAXZx-i1&CAQh00bWcS8wFL&nz9QpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV=Nj HrH&l{zC{sN diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc index 7f145c8fcee9f6c70534821fb0b88a41ba0332b8..b4c1ee8128157180e458f2b69b659ad529ae3369 100644 GIT binary patch delta 41 vcmX@ic!rVtG%qg~0}xz~xiOL3m@#{zy%=w9W^rvcW^wYwq$~iF!U=u= diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc index ef32b4df6b01caf40ac244a80a16238fd4b93e3d..7ebea022a9aed0009fc346c9b5b24c3d0a7d84bd 100644 GIT binary patch delta 62 zcmaEF{==O6G%qg~0}xz~xv`PEo<$`?KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsQWh_90JA3+od5s; delta 53 zcmexi{@$GXG%qg~0}y-+T)mOIo<%xdKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9r H7B6uC>`xMo diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc index ef5dcf561bc0983f3bdee28404095356d9b25020..6e140c208fa45bb999b3b6142af1e5d8300d38e2 100644 GIT binary patch delta 62 zcmX@?f5xBtG%qg~0}xz~xv`PEfLSF|KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsG-h2D0H$pgMgRZ+ delta 53 zcmX@(f7qYb8xE^z3BR4OHN|t^`er~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E SYEDjKUTS`6@n&O=n=t^obQbvl delta 55 zcmbPwim~SuBll@uUM>b8_!zi)BR4OHbfSJner~FMQ9)`+QGQuwa)y3#eoCsoZ)S1w JW*d&1F#z-b66yc| diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc index f53229cb0e7093713f578c73971698bb36a306bb..1386ef16d672ca3aa41da92d6faa562ad9bc968c 100644 GIT binary patch delta 41 vcmX@fc!`nwG%qg~0}xz~xiOL3n6Yr8y%=w9W^rC delta 32 mcmcb_c#@I(G%qg~0}y-+Ts@K7m@#9by%>vcW^wYwqyhkygb9`a diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc index a5c157b2d28d4cebfe83781e18300e64c654d02b..ede521b8caf6b94150cc493a16a2489ee72d615d 100644 GIT binary patch delta 45 zcmZ2#v(AS5G%qg~0}xz~xv`O3n1!)yvOJ3zZ*FFBa%xUaVqR)~Y4K)DmN$|BG4T#v delta 36 qcmZ2yv($$BG%qg~0}y-+T)mN7n1wNSvOJ3zi*IIe@@6NNHtJG%qg~0}y-+T)mO|JF|4Yenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z0 HmM!c68A}Wk0AE@Zv;Y7A delta 53 zcmbQuJBgS3G%qg~0}y-+T)mN7fJHi0KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--@ HOAHeLrLhpD diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc index f3fff1db5648506c8686b23ea3bc3005239f251d..891477d5f8716308703ad9624668a83e42aac06c 100644 GIT binary patch delta 62 zcmaFP|AC+TG%qg~0}xz~xv`P^J&Q`Yenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$QPxXL0I>WPpa1{> delta 53 zcmeys|D2!uG%qg~0}y-+T)mO|J&Sanenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z4 H)=NwP=?xN* diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc index 05a5042c1dfd6150cb1c32203857cfeccb2f850d..51fc3a8c19d4ee35fdf64bffb96ce62c2527324f 100644 GIT binary patch delta 62 zcmeB|n<2-2nwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*D{~Y#0Dh_!+5i9m delta 53 zcmbOs*DuF?nwOW00SG<@uHMMq&n%szpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV%>=Hw*irRJ9wZ%JR^&SG2>=L54OjpG diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc index 03e5cff2fe5f6a0e295ff0e791edba7a38be312a..06cd725fdaadb6bbc664dc27a2f9098b297e3ea5 100644 GIT binary patch delta 64 zcmeBN%QR^%6ZdIeUM>b8xE^z3Blj;Mm2~}#{M=OiqJq?tqWrSVC;>`-enau#yk{4b8 delta 55 zcmbQVmZ@_s6ZdIeUM>b8_!zi)Blj;M={Wt2{M=OiqJq?tqWrSViQ%>W0+6HfpD diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc index fedd19fc58a9b8564c5474e1e997dcff492b4d76..b82afe351eec44cbef27a1bffcba0541531fa813 100644 GIT binary patch delta 62 zcmaE;^-7ETG%qg~0}xz~xv`OZ4wFib8xE^z3Bll!h#>~m{S;csBGmDc`b8-^%Qu9lTH}7P%uK)m- Ch!HFR delta 38 tcmZqt!}#MjBll@uUM>b8_!zi)Bll!h#)Qf9S;bgvcW^wYwq$~iF!U=u= diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc index 98571b3726427674bff57dd8cfa65903735b82f5..02da6a39ebfbfdf184641d47e5b41f982ad808a2 100644 GIT binary patch delta 62 zcmbQwxq_4XG%qg~0}xz~xv`OZ52H%Denx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMM~r)!0D}b% diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc index 7952fd3b262647bd4375b7be1a08ad550d31b299..7650c284aca54625cdc091aefde4bd57fe2c20cd 100644 GIT binary patch delta 41 vcmX@bc$ty=G%qg~0}xz~xiOL3n6YT0y%=w9W^rvcW^wYwq(T6d&k33U diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc index df561fffd4a7cc97fd79708c98a8b14cb053c458..e2c71964eaafa8117c633b965585c3ed813a8637 100644 GIT binary patch delta 64 zcmcbC;?1tyLcRdjDi*f@ delta 55 zcmaE`f${nVM()$Ryj%=G@G)@pMs9g-=}i5M{M=OiqJq?tqWrSVvcW^wYwq*4Hu`w6N5 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc index 1abd1fec19d27c467ee2eca917b9aeeb76851527..99b04360c2d1839eb166ad711e7ffc7460cd408b 100644 GIT binary patch delta 62 zcmca)f7_n>G%qg~0}xz~xv`OZ4wFieenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMLrem)0IyXRe*gdg delta 53 zcmca@f61QvG%qg~0}y-+T)mNd4wH1Ienx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{NV HOaih1=&BNM diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc index 7982ddb26161894a4fe11f6d3f343c788cfb2c8b..3966f80e819ca29f3fcb3e18b5df7090347c47d5 100644 GIT binary patch delta 64 zcmZpF!Z_gxBll@uUM>b8xE^z3Bexx^N`-z#er~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E SYEDjKUTS`6@#YlP#W?`a2^VJo delta 55 zcmbR6gt7ezBll@uUM>b8_!zi)Bexx^biRH@er~FMQ9)`+QGQuwa)y3#eoCsoZ)S1w J=3LgrIRFI>6IcKM diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc index c1318e691b9ccf0359b72e3bd7ab2d85ef58d8ba..fb8d5cf1916e98bb3c0d165420c3bb8413d69f83 100644 GIT binary patch delta 64 zcmccohw;uIM()$Ryj%=Ga6RV6M(%WWl~VnT{M=OiqJq?tqWrSVC;?2G6$I1W)ofxkG delta 55 zcmccfhw<_sM()$Ryj%=G@G)@pM(%WW=^Xux{M=OiqJq?tqWrSVq=XA{3wi diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc index 12f61bcc1dca70d0a6394a04c9d91339463d68f2..2d495e979153c7aa0a9217906b05366c7b130d7c 100644 GIT binary patch delta 62 zcmbPgI?t5*G%qg~0}xz~xv`PEhE1hTKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsBDT-M0E_b$o&W#< delta 53 zcmbPdI@Of>G%qg~0}y-+T)mOIhE2LuKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=AG Hw$H)<%GMH( diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc index 0da0d636e2d8755348dd85f1266b0ad657501a38..bf62e95f88822684ec00525a84beb8dc2e52de3e 100644 GIT binary patch delta 62 zcmbOkv?z%CG%qg~0}xz~xv`PEjYXwQKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMs8kSrY0GhNGa{vGU delta 53 zcmZ1!G&6|%G%qg~0}y-+T)mOIjYT?FKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9{ HmRuD8*F_R! diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc index 065d802049438069457e7bae84457491c08feb66..6e6ad33ae9c1012937b4f16f967f084de7682379 100644 GIT binary patch delta 62 zcmdn2zDJ$=G%qg~0}xz~xv`Pkl0~IVKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBJWB{a0E9Uei2wiq delta 53 zcmdm^zFD37G%qg~0}y-+T)mOol0`aKKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mh HO9(#z#IX^2 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc index f9bb3319b477270c05e8e44e35ec18e50776c80f..cf54c3c1305dccdcefc0ca606989beb22050ddc7 100644 GIT binary patch delta 62 zcmexT{t<8 delta 53 zcmX?>us4DGG%qg~0}y-+T)mO|9-DNoenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?zf Hc1ukF^ve=X diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc index f9c1c88b6bb89e1a5f7002577ac3670eaf244930..943e6316710320bd05356198eaec0c65ff961451 100644 GIT binary patch delta 41 vcmdnTc$|^@G%qg~0}xz~xiOL3m@#dly%=w9W^rvcW^wYwq*MTsO9^TK diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc index dfb47c21c2a817e2c2cc5e5e98c7c76fa6d8edc7..a057a004d4b4f6e868994671960742f0a914a97b 100644 GIT binary patch delta 62 zcmdm_yjz+3G%qg~0}xz~xv`P^6q`zxenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM4{V3H0Gw7A0ssI2 delta 53 zcmdn3yh)k+G%qg~0}y-+T)mO|6q|ISenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M$ HY=^i2*l7~; diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc index 10c405ea42df55d962b7dd3cd5e4f8e617306439..5ced39a0dce5f8da1ea3728a774ae83caa651445 100644 GIT binary patch delta 62 zcmX@gcaD$yG%qg~0}xz~xv`OZ7L!Vnenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM15B+f0FVq8O8@`> delta 53 zcmX@dca)F&G%qg~0}y-+T)mNd7L#00;~avYNwi*IIe^5#aSw`>5&Tni2W diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc index 391769160ad66cdb9a461efe2f966f8f852a71f3..c1e6b0fecb36fa1e206fb74343a89857c2f9947b 100644 GIT binary patch delta 62 zcmca6c3+J9G%qg~0}xz~xv`P^Jd;X_enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMuS|&?0Hx#>$p8QV delta 53 zcmcaFc1?`?G%qg~0}y-+T)mO|Jd<>^enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{MW HOoq diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc index 90a2e210e7e24e81d18bf666bf5e9f3e9751c259..98a24bfbb31dfeb6cd7c9ca59d2435d8f1308124 100644 GIT binary patch delta 62 zcmdm@y;qz2G%qg~0}xz~xv`O3j#;HZKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>3v+=G0DzhmbN~PV delta 53 zcmdn1y+xb*G%qg~0}y-+T)mN7j#)ZgKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--# HbAb>5!RirZ diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc index 388d51818da618726acfd8052b0d9701ad330272..ca54f411f3f393888edb7b6c93d2ab21a68bef40 100644 GIT binary patch delta 62 zcmZ3^x`CDZG%qg~0}xz~xv`Numr*56KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsWJVE20CcGpF#rGn delta 53 zcmdnMx}25!G%qg~0}y-+T)mMymr*)aKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9N HMiE8;w=@wV diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc index 732757b5c34076d0e75474f6784c47135b90d5b8..5ba18b980e1eb483ce4ad209b89e9e194756bda0 100644 GIT binary patch delta 62 zcmcbq@JNCCG%qg~0}xz~xv`Pkj7cR^KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB4AWm;0Fhi4R{#J2 delta 53 zcmaE)a8rT%G%qg~0}y-+T)mOoj7d5{KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m_ H(_dZy&`1(U diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc index 329e11173385e5c05e2eb2a961f567ff4c740238..bded29820a4858c051e3742b32e48e8a893f6593 100644 GIT binary patch delta 62 zcmX>teMOr4G%qg~0}xz~xv`OZ8;eSTenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMJ1p-x0HLxMDF6Tf delta 53 zcmca2eOj9PG%qg~0}y-+T)mNd8;f+henx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M; HEblo0-6Ink diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc index 142db4aa27d367243b25475a62b48def32b3d284..4d33e543486aa2fad585a1289b4257f3b1fd1dca 100644 GIT binary patch delta 62 zcmbO#IZu-NG%qg~0}xz~xv`PEky#~8KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsa^`m&0DDXpLI3~& delta 53 zcmbOyIaQMTG%qg~0}y-+T)mOIky$!cKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=91 H=64(byzLS* diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-312.pyc index c83aa14eb52fc252303a2988c69858b12ae1d9ac..53875b696a84b1efa425abfdb75880ea69f5696f 100644 GIT binary patch delta 62 zcmaDU@JWFCG%qg~0}xz~xv`Nuf>k9=KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB9qVf*0GGTLfdBvi delta 53 zcmew)@KS*LG%qg~0}y-+T)mMyf>kuV+e)pinc diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc index b92e064627ee7c3d88314b336ffc8797b298040a..2db4eea126a073c88e6cb330041957eb0b058ae8 100644 GIT binary patch delta 62 zcmbOtKUbdnG%qg~0}xz~xv`OZC96uNenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM^Q=cX0fnd*djJ3c delta 53 zcmbO$KSiGVG%qg~0}y-+T)mNdC98CTenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{LH HtVcNk#aa?< diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc index 30040ae5c0342dbabd116f02df7b9709aae8b306..83242d13725980755f40d344ea8388b2d1673572 100644 GIT binary patch delta 62 zcmdmNxyO?GG%qg~0}xz~xv`P^G>b}xenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMk1U780j94PIRF3v delta 53 zcmdmEx!IEYG%qg~0}y-+T)mO|G>de+enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M0 HEQiDa;Xe~9 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc index 46e77c86839664bdb49ae74944732c349b74941f..478b36cb51b454b72950fa141ff767622b10438b 100644 GIT binary patch delta 45 zcmZqYoWRL_nwOW00SK0V_K*0B;5qn*aa+ delta 53 zcmX>guw8)rG%qg~0}y-+T)mN-i&Z*GKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--r HD?2j)vmFqO diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc index 2d250bbf0b44052c3693ccfac8a4105361f28d35..33c813c3120bdc4d5e0a8dc56ac8729337cbadc1 100644 GIT binary patch delta 62 zcmX@8d`g-7G%qg~0}xz~xv`P^G^a|Qenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMkDM3S0i^R6Hvj+t delta 53 zcmX@5d{CMDG%qg~0}y-+T)mO|G^cc`enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M0 HoEO*u->?%X diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc index dcc59046f1581eedaafdbbbcdacbedea4229c50d..5f950051a6092ec30f4fab02656ae52c38bdb5b4 100644 GIT binary patch delta 62 zcmccYe8-vlG%qg~0}xz~xv`OZBeP0|enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM>&$;-0k#VlmjD0& delta 53 zcmccPeA$`%G%qg~0}y-+T)mNdBeQh8enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{K| H%ztD7?wS*b diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc index 7b937bbff2fec3b5edd7fc5208ac8f1ff3f2ff95..1fb99286e07f6075c666b1c7afcfbe278cc1840a 100644 GIT binary patch delta 45 zcmbOkw?;y{ diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc index 2f966439c4f7da56077dbff860cce284a73739e7..ce577b7115ae3acac92a240d581d8fd6a0ece79a 100644 GIT binary patch delta 62 zcmbOsuuy>eG%qg~0}xz~xv`PEnn@)|KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsLZ)(70Ck@gssI20 delta 53 zcmZ1|FhhX*G%qg~0}y-+T)mOInn^lRKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9b HrgByQxDOGV diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc index 14009047c555afc364285cf61dca467a8c625bd7..f8596f6845c4584a9fa977729bd9c0d3cae2f886 100644 GIT binary patch delta 62 zcmeyZ)S}FNnwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*0n=?h0Fe(Ch5!Hn delta 53 zcmZos{;kA)nwOW00SG<@uHML9#UvfCpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV(}Ex&CAQh00h@#ZfxXcXHiMk&&bbB)h{YYEh)+`%S_JDPtH$C)z8f=PEO6q QNz6;lFD>4z%W_i)0A{fjP5=M^ delta 53 zcmeCzYt!RC&CAQh00bWcS8wEIXOWK5&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTp+B Ha#IKZtU?h# diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc index d2584fe108228a1c7687753f7eb11c378c35ff84..f4c533ab572bf73d9ef9bfb9eeb153aaf2f68b99 100644 GIT binary patch delta 61 zcmbQpGK+=#G%qg~0}xz~xv`PEfKeqyKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z PCowNIzqEMrG{$NGRMQnr delta 52 zcmbQmGLeP*G%qg~0}y-+T)mOIfKfVHKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=7X G#%cha0uep{ diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc index 5a8206c05130ae1ae5377277142485bf1bc71455..43b5a26ee1f9036dbb0a9e374c95e5ab3e0b5b3f 100644 GIT binary patch delta 62 zcmX@Ceny@9G%qg~0}xz~xv`Nunpq`DKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBBeRnL0FJa3#sB~S delta 53 zcmX@3epsFRG%qg~0}y-+T)mMynprwhKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*my Hvy%V-&6g3k diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc index a067b893a5eab56e1e8f7943eb576779e8cb90ab..bfed7c001aee95bd686ff8043be5340894b2e65c 100644 GIT binary patch delta 47 zcmcaOh4I!DM()$Ryj%=Ga6RV6Ms877#`wvKtYW;mnZ?PeIXQ`WsrjYFo2^;JTmfJv B4zB diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc index a5fae64eeaa45eb37f1deb085e332f2debe1531c..de969d6d701fac3619351816ca64960eba56b9a4 100644 GIT binary patch delta 62 zcmdmGeb}1&G%qg~0}xz~xv`P^6thaIenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM56q_|0je$+P5=M^ delta 53 zcmX?Xy~~>WG%qg~0}y-+T)mO|6ti@Uenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M$ H%%>y)q{LT{e diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc index f98a8c28813fe7b840808620aa2ac4f13981836b..49b323239e4ebb26856aebd4a238464798a9b478 100644 GIT binary patch delta 47 zcmX^2i1GR(M()$Ryj%=Ga6RV6M(#%}j9HW4v54{JW)>%>=Hw*irRJ9wZ{}ci%K-qQ C1Q84X delta 38 tcmccqi1FMbM()$Ryj%=G@G)@pM(#%}jER%qv52wwW)>%J7G!nH0RRpl4E+EA diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc index b2c3a11c27a467f068c0f506d05abbd41a03d62e..6ed5abb0e398ce4dfdc9f6eb69277b4b56f9ea94 100644 GIT binary patch delta 45 zcmX@2d{&wJG%qg~0}xz~xv`P^GdpA4PwbU5j G{Q>}@YY{{M delta 42 xcmZ2@h-1bf4(`*uyj%=G@G)@pMs5~0#<YGN$DnZ?P?#%kM*)fnIX0ss+N4L$$> diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc index 347299394f75518fdbec90450bfeca5b9c3cc655..aa0201d8049df7ef95e378d54d07513c3dce6613 100644 GIT binary patch delta 44 ycmZo=?_=ja&CAQh00h@#ZfxXU%*2>Ic|DUDZ*FFBa%xUaVqR)~Y4PNfOnU$!g%0Te delta 35 pcmeBUZ)N8`&CAQh00bWcS8wEA%*2>9c|DUDi*IIe^5jcQdjP7t3fcew diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc index 1c2f68cebd58c6b8b9545ac7a7b524694de36172..92d3ccda647321a9381cf2e727f33f85d3b08c17 100644 GIT binary patch delta 62 zcmcaAa!-W&G%qg~0}xz~xv`PEfJr4?KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsG^YLR0F&SrWdHyG delta 53 zcmca7a#e);G%qg~0}y-+T)mOIfJr(|KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=AY Hrv2;y(ex5l diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc index b83a5b9962fb87f8ee691ad0f5aa96f857470cc5..e683c0a74681653831057cdc7e9cb8fe4a82e6a8 100644 GIT binary patch delta 45 zcmdmQbi#=HG%qg~0}xz~xv`OZDGOuP1JeQ?0F5RU$N&HU delta 53 zcmaDU`#_faG%qg~0}y-+T)mN7ib*mYJNPpPZkPs-K%#oSd4I QlbDy9Us}Aml(m%`0Epfd-v9sr delta 53 zcmZox`lZNynwOW00SG<@uHMM)&nlg&pOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV>Y? HwUrwH$F>pB diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc index 2fa59f2df82b0e9b8e11b882d2bf2fd498d98ac5..0cc184cf2025b8acdf290d433c901177c49e76b4 100644 GIT binary patch delta 45 zcmZ2#vd)D2G%qg~0}xz~xv`Pkg^4kDasZPUZ*FFBa%xUaVqR)~Y4PSPrrqKIH^vU0 delta 36 qcmZ2yvebn8G%qg~0}y-+T)mOog^4j`asZPUi*IIe^5$Zu-Qobg9}15E diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc index 37ba7447a1cd9182ac892b2e2dbea46922bce7b1..5025ed6f838d87dbcf8c06c979897a7f280a6bf7 100644 GIT binary patch delta 62 zcmbQBIa8DSG%qg~0}xz~xv`PkmrW%{KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBF`FzO0CtWQF#rGn delta 53 zcmbQKIYE>AG%qg~0}y-+T)mOomrXiZKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mo Hn=Bsyxa<)k diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc index 102593bf261aa5c0f273080ac598233880879619..4856f3d981b09fb518cabae08e48e43e0f89892d 100644 GIT binary patch delta 62 zcmeC=oy5z1nwOW00SKAaeyX0DoK+ZU6uP delta 53 zcmbQl+sVs)nwOW00SG<@uHMM~nn^lQKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--a Ha|JU1zSt38 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc index b3873739bf8a3572a74b692bc74a60ee85c0406a..13027d5a99cd75d634161e61b1fb2b1320002743 100644 GIT binary patch delta 61 zcmdnQyqlT(G%qg~0}xz~xv`O(mr1BR delta 53 zcmexj^V){{G%qg~0}y-+T)mN7ibXnGKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--t Hi?$R1-j5Ns diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc index 4c2ed914fb66a05738d814306fae5db6de558f28..a6c2fc1ed7a06968f5256744ee3eec22a2f59420 100644 GIT binary patch delta 45 zcmdlex=WP%G%qg~0}xz~xv`OZ5+h^j1z-G%qg~0}y-+T)mNd5+h^ImYJNPpPZkPs-K%#oSd4I PlbDy9Us^o*4C4*}S#lNN delta 52 zcmeBWZDHj;&CAQh00bWcS8wEA#wZ=DpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoILp| G;|>6zff3OF diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc index 09bb30e5a807388dfa775117e860c27b219b9bef..11fff715da0fb6f8b4a69d40be8d37a3afacc25a 100644 GIT binary patch delta 62 zcmbO%IY*NFG%qg~0}xz~xv`Pkhe;(#KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB5tAYp0B!yi0{{R3 delta 53 zcmbOuIa!kXG%qg~0}y-+T)mOohe0 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc index 3e8bc02857332f4dca52547887350ea7cfa07d3e..9d712f8cdaa32a48eb6e6d610b42bd62dcb25399 100644 GIT binary patch delta 64 zcmZ4TglX*)ChpU`yj%=Ga6RV6Ms8nDl@$Gq{M=OiqJq?tqWrSVC;?2dJ8&?1T-d7j@ delta 55 zcmZ4cglWkWChpU`yj%=G@G)@pMs8nD>1h3o{M=OiqJq?tqWrSVB6 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc index bd4c0bb73cf4e75962b3e85b26dfcb7f662af2d7..f14ab836de6af35c5b0aa8d20f5057c80f9ff031 100644 GIT binary patch delta 64 zcmZ3xk8#^RM()$Ryj%=Ga6RV6M(&4fD#`j8`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu SsW~}`d8zrO#hcmL)gl1ps}~9Y delta 55 zcmdnCk8$ljM()$Ryj%=G@G)@pM(&4f(oy;u`MIh3MFpuPMfqi!$r<{|`6;RTzL~|z Jn+4d_A^;Ys68iuE diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc index 172a374a0037ee8568ac61395249d8cc83fbeb2b..ec630e5b8a51edd8e02c484db12f2f3c8a2b3d31 100644 GIT binary patch delta 64 zcmex+mAUO2Gxup;UM>b8xE^z3BR3DbN}7H~er~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E SYEDjKUTS`6@n$3T(~kk!0T<^0 delta 55 zcmZp>#{BmxGxup;UM>b8_!zi)BR3DbbgX_xer~FMQ9)`+QGQuwa)y3#eoCsoZ)S1w JW^4A-j{ywt6W9O% diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc index 5a0f4aa6ac8c31b40f46d56415a18e90a45cbd04..bd868a6ca4e37c50ff0f77eedd6fb5f66ec47339 100644 GIT binary patch delta 62 zcmbPIHn)uXG%qg~0}xz~xv`NuoK+=FKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBE$ehs0G?$QfB*mh delta 53 zcmbPRHl>XFG%qg~0}y-+T)mMyoK-qjKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m$ H>vU59+V&E0 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc index 0f68e4796dd22651a863a415f72d90e59d469aec..5a614b451a336c71445c777f8bc7cfecea1bc9e2 100644 GIT binary patch delta 62 zcmZp)>9XNI&CAQh00h@#ZfxY9&7_j5pOK%Ns$W!)T2ho>mYJNPpPZkPs-K%#oSd4I QlbDy9Us}BRAd`bM0EKK7_5c6? delta 53 zcmeCOX|&-!&CAQh00bWcS8wE=%_JS8pOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV@uI HlY=w>#U~Nx diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc index 6bf934822c934ba633781ea51d09d07d5ae2e963..83323427be5c52ca3604e6a8cb0c2356bf5c61c1 100644 GIT binary patch delta 64 zcmX?hjOqL_ChpU`yj%=Ga6RV6M(($)Drx!|`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu TsW~}`d8zrO#hZoMjOPLX`5hOz delta 55 zcmX?qjOo}hChpU`yj%=G@G)@pM(($)(y{s(`MIh3MFpuPMfqi!$r<{|`6;RTzL~|z Kn`PLH=K=sHI1{P> diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc index ea1e3809f718eb54c01a6a55071ac523108b1640..0e226e307c10903ef1977fee5aea99281c97ca72 100644 GIT binary patch delta 64 zcmZ3}&bYOmk^3|+FBbz4T#vc2kz0&KC0#!wKQ~pss35hZD8DQ-IYU1=KP6Q^H?uf7 SH76%AFEzikc(V;lfjt1JaTYWH delta 55 zcmdnm&bX$Xk^3|+FBbz4d<%>=Hw*irRJ9wZ{Edu>i_^~ C^brRD delta 38 tcmbO`m3hikX71Cxyj%=G@G)@pM(!z`j8T&paEh_`W)>%JKFoRR0085>4EO*5 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc index 90b30755bb05ec97ccb00413a720438231160532..8c10b9d2650db57f56c4cbf504655de802a45f5c 100644 GIT binary patch delta 44 ycmdnbeuAC*G%qg~0}xz~xv`O(g_$vRvH-IfZ*FFBa%xUaVqR)~Y4Kz&=BWS`aSc`g delta 35 pcmX@XzMq}@G%qg~0}y-+T)mN-g_$vCvH-Ifi*IIe@?>M?sQ{(r2}b|` diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc index 1d42e96ea88d88e19a0c93c395dcea0121c41f88..d956887572697ede96fb2d38715ae67cc14507f6 100644 GIT binary patch delta 41 vcmZ3?w1$cMG%qg~0}xz~xiOKug)w#F1To&+%;MzKoSekG)cn%oiK{9B1icPR delta 32 mcmZ3(w3vzeG%qg~0}y-+Ts@Jyg)wI01Thxh%;MyUn=1j7KMFhm diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc index addc537fbf630649ae0920765e6f71e94af638a0..ac465aa1e5a503ba207d51c35ea0ba882ac89a34 100644 GIT binary patch delta 47 zcmcbzg!%3gX71Cxyj%=Ga6RV6M((fdjLDN3ImCE#GmDc`b8-^%Qu9lTH%oI^o&o@U Cd=M=F delta 38 tcmcb+g!#%6X71Cxyj%=G@G)@pM((fdj8T&rImB3eGmDcqt8iGJ0s!>?3?2Xg diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc index 302151088344a35794c4431fb9144beefd12b0d3..e3e31bb25b0418a3dc1b425e68e7eaccc9b1147c 100644 GIT binary patch delta 61 zcmeys_LGhKG%qg~0}xz~xv`P^0Fz3xenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- PPGVkaerfUKXG~oHkh&JX delta 52 zcmey#_JNK2G%qg~0}y-+T)mO|0F!i-enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{H% GOkDuaM-s3A diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc index e9f1d2e68d8e2f67a44022387fd6df5a22f22001..b41385200c14de1c81ff57b240ca05287061beca 100644 GIT binary patch delta 62 zcmeyV)}+pTnwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*Bl8J<0F&$%lmGw# delta 53 zcmZot|Eb1(nwOW00SG<@uHMMq%`6?RpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV#c diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc index bce3cee854ba40c675c7d921acf6dcd765b5c9ad..9e17782afa303b32ec1dbfc048d68f44a149dfa8 100644 GIT binary patch delta 62 zcmaD}{h^xsG%qg~0}xz~xv`OZCbLSsenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM{mdFx0Mb_%KL7v# delta 53 zcmexR{k)p{G%qg~0}y-+T)mNdCbM*yenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{L9 H%oC;>}+#&f5e4=s+2{ delta 55 zcmbQ!$u_x@jr%k&FBbz4d<ra!rK$G%qg~0}xz~xv`OZD+^=F3d17fc E0G9I+KL7v# diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc index d91ca942799aa0e73e8bc6d0d64b940ca60b68c4..e49d26caf243baec079f38c07cb3470415503698 100644 GIT binary patch delta 72 zcmex=gR|o%C--SyUM>b8xE^z3Blj^Wl@$Gq{M=OiqJq?tqWrSVC;^sF}?Qf(Qx4)5Mnj8oK*UTH} delta 63 zcmeCU$@%{VC--SyUM>b8_!zi)Blj^W>1h3o{M=OiqJq?tqWrSV3b5A diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc index 4c68fc5d915734126ded6a01643ae63764460d27..05b132dec1f70960a34a336e69d37207c07e3adc 100644 GIT binary patch delta 62 zcmaE9|H+>FG%qg~0}xz~xv`OZE|W^4enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM!%S+j0JrWIv;Y7A delta 53 zcmexl|I(iOG%qg~0}y-+T)mNdE|YYGenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Lf HOlqs761S;bQAah diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc index 211b38f84f66b479d96fec92cbdbcf3ff5d22820..5a3d8b00d487abd2e2480d88914c3216826f5e6b 100644 GIT binary patch delta 61 zcmZ3>vW_DzlZG%qg~0}xz~xv`OZKa)y^enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMr%W9J0Jc;YDgXcg delta 53 zcmeyS_EwGiG%qg~0}y-+T)mNdKa+I4enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{MO HOdSFM?xYhN diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc index c026561d4b83be41ba3b887b378c07d905412bac..d5dd66b6c9e33d2c3cb97ac70b2f4b44ee3a069f 100644 GIT binary patch delta 62 zcmZql>GI(|&CAQh00h@#ZfxWhV^+!1&&bbB)h{YYEh)+`%S_JDPtH$C)z8f=PEO6q QNz6;lFD>3|!+cZ;0Cor!p8x;= delta 53 zcmeD3Y4qVf&CAQh00bWcS8wDNW0p?T&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTuUw Hd{hYlxf&6W diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc index 6804a85fa5418912aee4363554712c92ef773a3f..46e10bc9134a171b1197892edd3cd2018f67bcae 100644 GIT binary patch delta 62 zcmcbi{7{+uG%qg~0}xz~xv`NupIIeKKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsRAy&B0Gr+x7XSbN delta 53 zcmaE;d_$T0G%qg~0}y-+T)mMypIJIlKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9t HW@kPC*pm_n diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc index f14f94df578722b41c7aa66878fbb0d74d8a0704..5daac20c95201fabc6f51ce0e8f0d40df95ac907 100644 GIT binary patch delta 62 zcmZ3VyCawTG%qg~0}xz~xv`Pki&-UIKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB0kgd^0Hd}REdT%j delta 53 zcmdmyyFQouG%qg~0}y-+T)mOoi&;8OKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m= Hv%N6@-+>Yy diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc index edc00ecbfdadf15b31cf72f3b7b0ae4c4e361f8e..fdad94c81c2f8f520f3e91d4b0906f89e7de8770 100644 GIT binary patch delta 62 zcmZ1>xlxk)G%qg~0}xz~xv`Nuno%WNKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBBcnPu0DK@7SpWb4 delta 53 zcmdlexk8frG%qg~0}y-+T)mMyno&AQKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*my HqdGSLy`~XL diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc index 758b9d6680ccf8ab3e9a85545c561819c27c9769..2daec816aa0ab56bcde98756b8838cf5e4af04d1 100644 GIT binary patch delta 62 zcmaE6``(uOG%qg~0}xz~xv`O3j9Dd1KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>4f85#0G{O*DF6Tf delta 53 zcmaEF`^=X6G%qg~0}y-+T)mN7j9EHSKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--x H^D1co+v^e< diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc index b24cdfab9abbe62ae8a460136e53bff2e337d1c4..8245fb03bf652329e7e7a9cbba54c135199ba8e1 100644 GIT binary patch delta 44 ycmZn(>kZ>R&CAQh00h@#ZfxZK!pxXHiA9VzH?uf7H76%AFEzikc(WACD>VQ*>JIDx delta 35 pcmeATYYF2%&CAQh00bWcS8wG0!ps;qiA9XXH?uf-vogyoH2}UG3f%wz diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc index 3e2097f0ddd74167bcdd0bb127fc5adf6ab769f5..8f3b8b4e2893e7fe911332be8926f4b755c8b16c 100644 GIT binary patch delta 64 zcmbP#m~q8nM()$Ryj%=Ga6RV6M(#T-DjE72`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu TsW~}`d8zrO#haN}=f(g4;jb5J delta 55 zcmZ2+m~sALM()$Ryj%=G@G)@pM(#T-(((Ek`MIh3MFpuPMfqi!$r<{|`6;RTzL~|z Ko4Hx%#sB~lU=v#a diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc index 5c344ae2da4ba2260d3fa45c4ebad139f94373d7..83588ffd63e0bd664060558ba395ca93ef38f737 100644 GIT binary patch delta 62 zcmdm~bV!N&G%qg~0}xz~xv`O(n@J@{KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>Arl8b0C|rT)c^nh delta 53 zcmX@4v{Q-uG%qg~0}y-+T)mN-n@KuZKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--b H69+#4yZ;cz diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc index f893478a3c2b7de5ef94c83dd17a1d8f880ebd9d..04dbe93e0b5e9b6c46c0d14250a3486b0f01b04d 100644 GIT binary patch delta 64 zcmaF8j_J!fChpU`yj%=Ga6RV6Ms8_ll`Q>?{M=OiqJq?tqWrSVC;?0iCT2lbra2Ff^ delta 55 zcmeyej_LI}ChpU`yj%=G@G)@pMs8_l=|ugE{M=OiqJq?tqWrSV%M()$Ryj%=Ga6RV6M($s%Dk=II`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu TsW~}`d8zrO#hVq_*7yPd?`;>x delta 55 zcmcb*obk+ZM()$Ryj%=G@G)@pM($s%($V@E`MIh3MFpuPMfqi!$r<{|`6;RTzL~|z Kn>E?i_yPbP`4hJQ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc index 7a7382700a5bf20b497e8e6813e723b2d0c39eb5..eb9cdc63af9f11c533ca68d8201527ef4f8b03be 100644 GIT binary patch delta 45 zcmZ2wx!IEYG%qg~0}xz~xv`O3gqbmQvI4UhZ*FFBa%xUaVqR)~Y4K(&<^o9oFd7Zi delta 36 qcmdmNxyq9JG%qg~0}y-+T)mN7gqbmBvI4Uhi*IIe@@8k|0!aY5RSCra diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc index 4a91cf8596863c6fef5150aa7485b4b00a775b10..ae7b195b32cc93c8a10e2ddcaf99f61e058cae64 100644 GIT binary patch delta 47 zcmZ2GgK^soM()$Ryj%=Ga6RV6Ms5`r#`MVsEMmO5nZ?PeIXQ`WsrjYFo4r^by8{4P C0}pTj delta 38 tcmdlsgK_N)M()$Ryj%=G@G)@pMs5`r#<C;^s}F?VCgyw{H?16$k{M=OiqJq?tqWrSVC;>|%EO0EFfpcdKy delta 55 zcmex6i}A}WM()$Ryj%=G@G)@pMs6Jr=_LJ({M=OiqJq?tqWrSVAaE0IAFtT>t<8 delta 53 zcmcZ|d?}dwG%qg~0}y-+T)mOomsL7ZKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mo HYrF~oVP{O6?7%L@o10mjoSKu9n3tMgTD&=mT|*lHI3x}e delta 36 qcmeB9ZA#@n&CAQh00bWcS8wDtVP}k;?7%L@;+t8Vyg8X&LmL3Th6)4# diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc index d1805fb27d0771b74ed5d4473c8444d25df5525a..4c025a87e9df4486cac70f59759c110dce6150ac 100644 GIT binary patch delta 62 zcmX?RcixWsG%qg~0}xz~xv`O3npGu3KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>BP**E0FTTSQ2+n{ delta 53 zcmX?acg&9aG%qg~0}y-+T)mN7npHYpKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--d HE2|U$&eIV> diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc index 40c4adfacdfe08ba9fe71fb4d10ef7451b0ba69e..a24a192b9207b770f2eed65002b85a73b83c7ca4 100644 GIT binary patch delta 62 zcmdm9dZ3j1G%qg~0}xz~xv`OZ8>33Penx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMJB+t20KjJ#!T4ibenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M; HjJGWS_gE9N diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc index 98b12e551f1590c9bbb8577659d38618d600c848..cb58215c4824188927fd986b34570a5d8c487861 100644 GIT binary patch delta 61 zcmcb@a+ihsG%qg~0}xz~xv`Nuk5MH{KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z PCowNIzqEMr6vhJpbrcpF delta 52 zcmcc1a)pKaG%qg~0}y-+T)mMyk5M{NKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=71 G#sdJjX%Y(n diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc index 6bf138d2d8e5b3d64537b41d71ae95c420e3092a..bdd7f38dd2581fb4672f75eaedcf68394e3ca850 100644 GIT binary patch delta 62 zcmaEn^)ZY4G%qg~0}xz~xv`OZ8JkL$enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMGi-D80MK$5vH$=8 delta 53 zcmeyE^&*S=G%qg~0}y-+T)mNd8Jl#Xenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M? HY;*Jg1r8IV diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc index 0314f4262af2804561909fb196c7b7a451d5231c..252f9912d4e48c541b68b5cdcbecfbf4f070c788 100644 GIT binary patch delta 62 zcmZ1=y;7R{G%qg~0}xz~xv`P^4x384enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$CiZ!30FIs&$N&HU delta 53 zcmZ1}y+E4#G%qg~0}y-+T)mO|4x4nGenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?zv H_IYdo%l;9$ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc index d0600e5a7bfa692b617134d7420ac34925a3847a..44d775034e582494e4b316baf2ed7ffb24250f80 100644 GIT binary patch delta 61 zcmcb|`ka;fG%qg~0}xz~xv`P^Kch;zenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- PPGVkaerfS!Ri>Q)kjoZH delta 52 zcmaFPdXJU+G%qg~0}y-+T)mO|KcjSC;?0xUW+nmv-Fp|p delta 55 zcmdmbfpN_RM()$Ryj%=G@G)@pM($iT=~(@Y{M=OiqJq?tqWrSVpa!G{yG%qg~0}xz~xv`Nuk69&6KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMs6y~jL0FAg7KL7v# delta 53 zcmca4a#Do*G%qg~0}y-+T)mMyk6AiaKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=A2 H=B;c1%(fCR diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc index 89c945f2522fedff221bccbf27d21715dffa9923..0367fbe0435119195c67386201966948758427eb 100644 GIT binary patch delta 45 zcmbOwxmc3>G%qg~0}xz~xv`OZD-&bt7NTG%qg~0}xz~xv`P^0i#NWenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$Hl{2A0JxhLPXGV_ delta 53 zcmeyV@C;>~uf%o72{eHM}c delta 55 zcmX@LhUwrMChpU`yj%=G@G)@pMs5jK>1h3o{M=OiqJq?tqWrSVia#n=razuptG%qg~0}y-+T)mMyfkiq-KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*nF H%RW{B$f6Px diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc index f1716a9ef8646423e1a0c2a702079bfb954837b0..1ecbf1aa4bdd82fb4a773d94382046fb7f8fc82d 100644 GIT binary patch delta 62 zcmew;|4W|xG%qg~0}xz~xv`P^0+UL*enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMZ%n^=0Jz;3v;Y7A delta 53 zcmew*|52X%G%qg~0}y-+T)mO|0+V!{enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{N> HOuu;m@Y55e diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc index fd26629a6cfd9bed85704fbe17d73e31c4b1d1d3..dc34de227fd0754de2b743259aa8225ebbb7a0b4 100644 GIT binary patch delta 45 zcmbQDI#-qZG%qg~0}xz~xv`Pkl$kMYvLmw?Z*FFBa%xUaVqR)~Y4PT0W=nnmC`S#W delta 36 qcmbQMIz^THG%qg~0}y-+T)mOol$kMhvLmw?i*IIe^5zs~OMU>d8wr#E diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc index 680b5a4901a57618dd41ed2bb174bbd2cb907072..6a9b34f4503c4538180afd8e0fcd33b560971bb9 100644 GIT binary patch delta 62 zcmaDG_brb5G%qg~0}xz~xv`OZH@ixPenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMhwMG-0MOYNw*UYD delta 53 zcmews_co6EG%qg~0}y-+T)mNdH@kGaenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{L@ H>^f&CAQh00h@#ZfxXsX?iTgA!FBbz4T#vc2k(;|rB||?WKQ~pss35hZD8DQ-IYU1=KP6Q^H?uf7 SH76%AFEzikc(Y;IAua&iw-=-U delta 55 zcmey<$n?39iTgA!FBbz4d<&fFj&CAQh00h@#ZfxZ4Vo^!f&&bbB)h{YYEh)+`%S_JDPtH$C)z8f=PEO6q QNz6;lFD>4@fhE`o0Gj6(XaE2J delta 53 zcmeClYtG|7&CAQh00bWcS8wF*Vv&y0&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTste HCD;f6*OL-i diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc index 7786f8956d03606fd9b52b8b821ce7edce791607..eb72f1c2ade1e7e5ea010498d4ef541840ec41b2 100644 GIT binary patch delta 64 zcmcb;g6YW$ChpU`yj%=Ga6RV6Ms9Hym3;k-{M=OiqJq?tqWrSVC;?1@!auWd2-4@>f delta 55 zcmaE|g6Z}OChpU`yj%=G@G)@pMs9Hy=`{U}{M=OiqJq?tqWrSVcs%akqapR diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc index 8cb124764e42a263c1fc14321ff9a0eb584d5a5a..6eda06575042583b0d44b778a6cd34a9120acc70 100644 GIT binary patch delta 62 zcmeya(4feDnwOW00SK{VG%qg~0}xz~xv`OZ2dhfHenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMd#uOA0G&-20RR91 delta 53 zcmZ2$xx|wDG%qg~0}y-+T)mNd2di|Nenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Np HtjELv*-jGj diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc index 82f3bfa95a29bf782594fc1c65fbf020fe6e585e..732538efd8f688ca77d9fef53c8bbd50bac1c3cb 100644 GIT binary patch delta 64 zcmeA?$TaC76ZdIeUM>b8xE^z3BX>5lN}hg3er~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E TYEDjKUTS`6@#cxlKjr`c#{U=i delta 55 zcmbPqkg4+^6ZdIeUM>b8_!zi)BX>5lbgF(ver~FMQ9)`+QGQuwa)y3#eoCsoZ)S1w K=2^@?<^TZx3lr%8 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc index de0d735bc7d6388680f6a8c0fe41e08954cbf4cd..8644cda18ffd05f5319ddb68910cef98edb55ea1 100644 GIT binary patch delta 64 zcmaERpYhv$M()$Ryj%=Ga6RV6M(%1Bm0bOd{M=OiqJq?tqWrSVC;>`)?6fXb( diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc index f8b2372a6bb8c1cc84a624d16f0081850e91cadc..ac31c66f6dce64ee4df7e73c57b8f43bd69079aa 100644 GIT binary patch delta 62 zcmdm}ze}I{G%qg~0}xz~xv`P^1G`GDenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$F^<{%0HpsGNB{r; delta 53 zcmdm`zfqt2G%qg~0}y-+T)mO|1G{vJenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z) Hj@kSG-qI2| diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc index e5ff778e641f3cf4d6f8b9061b95bace9a254862..beb2143a5503af3b0af4872c06673bc47025c170 100644 GIT binary patch delta 47 zcmX>-m+{J6M()$Ryj%=Ga6RV6M(!=FjQNufu!`~KW)>%>=Hw*irRJ9wZ@$I)(E|X5 CNfCem delta 38 tcmcaIm+|ymM()$Ryj%=G@G)@pM(!=FjA@e(u!^zxW)>%Je$4vO0|5DB4RHVf diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc index 92c6fdca93642e2211136f6418b4220f7a6c0bc6..ecdd9d5291a6d4acfb47b1936ee9bbe2e87e9b52 100644 GIT binary patch delta 62 zcmext`p1;}G%qg~0}xz~xv`OZ2eV4Penx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMd(5KZ0KZli;{X5v delta 53 zcmexk`q`BGG%qg~0}y-+T)mNd2eWjVenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Np H%%b7|_9GJ1 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc index d9e94fabfd1fcb16abdc27c25d063915d6d952fa..b4caa26b4e66a0bc0966e9996eca6ee5dd438b74 100644 GIT binary patch delta 62 zcmbOszEGU|G%qg~0}xz~xv`O3ok^usKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>FOxSX0Bhb9`v3p{ delta 53 zcmZ1|K0}=QG%qg~0}y-+T)mN7ok==JKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mz HlQ$;-uo)2S diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc index 497e47a923b2c816de7e0a0b2b381e5fb1b922ce..8e7e125605721cf8879903067b2c9de7cf0f0ae6 100644 GIT binary patch delta 45 zcmZq3>CE9i&CAQh00h@#ZfxWZ5@yVu949Qso10mjoSKu9n3tMgTD-YJxI+m5L%R<7 delta 36 qcmeCoX~^L|&CAQh00bWcS8wDF5@t-9949Qs;+t8Vytz@hLkR%JkP7Mm diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc index bd6151c77ff595bc74e7c4bb2949f4f3fea1c11e..0c67b8f2900db80bae4f98480938ead86a8d7bf3 100644 GIT binary patch delta 62 zcmdn*e!`vmG%qg~0}xz~xv`P^29rvzenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMe@q7z0JYy2kN^Mx delta 53 zcmX@%zTchuG%qg~0}y-+T)mO|29tD(enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z% H=7S0V>JAcu diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc index 755c320b5bdd0f5fada5407af99d55ea3163fa33..0bb4b3a3aeecd46b3bec282a67236bf6945aa4e2 100644 GIT binary patch delta 62 zcmX@Adrp`8G%qg~0}xz~xv`Pkj!UITKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB3RfZz0Fe|G)c^nh delta 53 zcmX@7dsLVEG%qg~0}y-+T)mOoj!QaIKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m} HS0WDp&z%v* diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc index 585ed6093892c84fa89c3e18047024c0399e9b57..d9099c8a8e1b1e7bd1afb76969c84c9d70df421a 100644 GIT binary patch delta 45 zcmdm5wy%u)G%qg~0}xz~xv`P^2s2~p0B$4@ AeE9zwnwOW00SKC;>~6fi~9lQb{E6| delta 55 zcmaEKp6TX!ChpU`yj%=G@G)@pMs5KK>16$k{M=OiqJq?tqWrSVaenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMzpOgb5e%;G%qg~0}xz~xv`OZ3pZol08vQ~ ArT_o{ delta 36 rcmX>pb3lgsG%qg~0}y-+T)mNd3pZoxiB}er delta 52 zcmaFI@|1=9G%qg~0}y-+T)mNd1EX}Renx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{H* GjQs$~1QLV* diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc index f00b85f4b10c9d86aae80cc34975350bf249687e..24f3ebc9f0efb8dede8b7a45ecc7aadea7708a2a 100644 GIT binary patch delta 59 zcmeyy^q-0QG%qg~0}xz~xiOLZgi5Y{Mt*Lpeo;YcNl|`TW^#spa(+sxer{%Qa%xUa NVqR)~Y4OB&9RPA57PSBX delta 50 zcmey*^o@!8G%qg~0}y-+Ts@Kdgmj92Mt*Lpeo;YcNl|`TW^#spa(+sxzHer6^2F~Q E0JTOErT_o{ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc index eda3281c60d5494991f1c397f3048a6a162a84dc..752fe047889dcbab3d5ff7d6b6268e9514cd4881 100644 GIT binary patch delta 45 zcmZoI?kna#&CAQh00h@#ZfxXEVPedlT)-s8o10mjoSKu9n3tMgTD-Z7>9-jGNdFI+ delta 36 qcmeAxZY}0M&CAQh00bWcS8wD_VPZ_0T)-s8;+t8Vym>OyZ!-YO{0oWz diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc index 3516ec817714f275e13d883b0c15e9b3de679c5d..f36f8419ae95263cd54a06c898f19ffdd346eded 100644 GIT binary patch delta 44 ycmdnNagc-iG%qg~0}xz~xv`O3h>5XkvK*5bZ*FFBa%xUaVqR)~Y4KtUCMEzEhz$b( delta 35 pcmX@ev4eyAG%qg~0}y-+T)mN7h>5XqvK*5bi*IIe@?u9OCIF_v2=o8| diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc index f1dd25cd5f3455e367f3db879cb5f2a4a8bd096d..9d266c076269bdf202b3d95d5f911f4604d2ce66 100644 GIT binary patch delta 62 zcmdm6aIAp)G%qg~0}xz~xv`P^JF`lyenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$Sr!Eo0Lz9Jq5uE@ delta 53 zcmX?Bu(yEwG%qg~0}y-+T)mO|JF|3&enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z0 H76lUk|3ng# diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc index dce9c8f9cfbc70c0269a13f6c6dba84d2328fa7d..6642429318c46a28d771d818359183f85dae5237 100644 GIT binary patch delta 45 zcmZqG?9=2v&CAQh00h@#ZfxY9%*mKOc|NBYZ*FFBa%xUaVqR)~Y4PTroM*TII#Lfv delta 36 qcmeCvY}MpG&CAQh00bWcS8wE=%*hxxc|NBYi*IIe^5#RFXSe{p+Y31W diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc index 1589a22ce5aeef0cb18f46a73d8e77ba0a036d61..98b6a12a9ce84d888af2f6264c88cd5d5a983246 100644 GIT binary patch delta 44 zcmX@Xa*>7mG%qg~0}xz~xv`PEg^@99@&raP-rUUME=K%n>mC;>`tYOEUn{vKM{; delta 55 zcmbPtk8$cfM()$Ryj%=G@G)@pMs6=Q={Wt2{M=OiqJq?tqWrSVKF?Qv0DFlQ$^ZZW delta 53 zcmZ2$vBZM=G%qg~0}y-+T)mN-lSevIKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--L H&sTl`z4;Nl diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc index 427c6e1ebc74c81ab79fb9169da6982b95a3cec3..57af0c1c42f7336c9ad015c459c70404c23f47cf 100644 GIT binary patch delta 62 zcmaEy^CpM;G%qg~0}xz~xv`PEn@uHIKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsMz&4*0LcCqh5!Hn delta 53 zcmaEp^Eik5G%qg~0}y-+T)mOIn@u`OKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9f HwoUo~{__)e diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc index 544575460f3f6901fd67d43be95e54aeda08cf8b..3fbd979d63bf4140dee79af4a3ae7b69f8686c8d 100644 GIT binary patch delta 62 zcmbQmzL=f+G%qg~0}xz~xv`Nuf=MMsKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB9g_?r0Bd6u`2YX_ delta 53 zcmZ3?K8v0EG%qg~0}y-+T)mMyf=N1BKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*nB HlMEvOuX+&b diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc index c87811203a227a9a941f4a4d069f869e9e78f337..c05481c452aade70c4fac611e81d6bbee286d970 100644 GIT binary patch delta 62 zcmeC>oyN<3nwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*4%0_w0B?2`{{R30 delta 53 zcmbQn+sn&+nwOW00SG<@uHML9#v~o1pOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV%>=Hw*irRJ9wZ+^vkDFy(L CDiO{A delta 38 tcmX?llyT2dM()$Ryj%=G@G)@pM(!i5jIon1u!^zxW)>%J{>*wQ1^@xV4Zi>Y diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc index 203b81b8f2ea41487cb6901aeab40bd4a1261ed9..e9462ddb5e3067b95063133022bd4ad9fb74284f 100644 GIT binary patch delta 62 zcmZ2yz1^DoG%qg~0}xz~xv`NulT{^CKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBKdZb10Fmz%&;S4c delta 53 zcmdmPz0R8ZG%qg~0}y-+T)mMylT|uFKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=97 HR(S~k&=nEE diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc index b2f5654fa050c6e029318f6072c1fb6d5cfb9d23..eaf380451de6d5fb49547453a6a9359883f14862 100644 GIT binary patch delta 45 zcmZos>{aAG&CAQh00h@#ZfxXEVq;96oW~}{o10mjoSKu9n3tMgTD-Y~?GGmaERGJv delta 36 qcmeBGY*FMs&CAQh00bWcS8wD_Vq=V&oW~}{;+t8Vym=zqA5H+Yg$lL+ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc index 03d8c1a40236c08ffc3e047e25478e7ba7a082f8..cac8a48cc93c83bcdd852001d6fdf2e38dc0fc9d 100644 GIT binary patch delta 45 zcmbQkv6O@RG%qg~0}xz~xv`Nug^@93asi_lZ*FFBa%xUaVqR)~Y4PSR#z{;7D-{k( delta 36 qcmZ3=F^7ZuG%qg~0}y-+T)mMyg^@9Oasi_li*IIe^5)5mlb8UrOA0#x diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc index 95883550e5b5769311f8caa43b436de4a2304d1e..3888c8e4c9c861f6ee236abdda8e254c6bf87eed 100644 GIT binary patch delta 47 zcmbO>m1+J|ChpU`yj%=Ga6RV6Ms8LP#?;Az9Adn=nZ?PeIXQ`WsrjYFo3%Nzn*d6h B4z2(I delta 38 scmbO~m1){kChpU`yj%=G@G)@pMs8LP#+b>19AYfKnZ?PQO*pcf0MA|up8x;= diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc index 0407c65db168de67c6ea8e9dcfd330fbdf15506d..929d114ab63ef4e0a37142f67562fb7bbb14c60c 100644 GIT binary patch delta 62 zcmZqYn!v?mYJNPpPZkPs-K%#oSd4I QlbDy9Us}Amka-#-0AlJDJ^%m! delta 53 zcmbQh)y~CznwOW00SG<@uHMM)%`6?KpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV>Y; Hc^V@CsB#f6 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc index ae55111c0621c7ad067201ee5c41b574f89b9e04..e6537c88278366cf092c3e50f9bfaaf328adf01d 100644 GIT binary patch delta 64 zcmaELlkxLSM()$Ryj%=Ga6RV6M(#{@m2~}#{M=OiqJq?tqWrSVC;?4c+(WwCcGZ+y7 delta 55 zcmex-lkwF}M()$Ryj%=G@G)@pM(#{@={Wt2{M=OiqJq?tqWrSV=VonwOW00SG<@uHMM~kY74UKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC-;x HKnFVj&vOyV diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc index 5c78fe9523838dc2b3580307ec1f736e9d1c36b4..c516fff2691bc14800f24f5a33923c8fe34dd024 100644 GIT binary patch delta 44 ycmeyU{Y#tsG%qg~0}xz~xv`P^3oB#hBsMYL+|1(S)SR5eywv>C;>}WQ*ZBcazYk6T delta 35 pcmeyR{ZX6yG%qg~0}y-+T)mO|3oB#7BsMV?-^}9V&B|=o`2o-u3qJq= diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc index f5304361ad932ccd409f90f47f2bd0102b0011d3..5956fe59d2f3071752e01d1758acb073240f0129 100644 GIT binary patch delta 47 zcmcaLhw1SgChpU`yj%=Ga6RV6M()F$j46}PbBgiiW)>%>=Hw*irRJ9wZ+^*H*a85I Cvk`~@ delta 38 tcmaDnhw0WFChpU`yj%=G@G)@pM()F$jM0%J{=`|>0s#KZ4S4_n diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc index 2a21950f95944919666f682d68b0ec311eee004f..b8e17c67ead6b6025b7422c237341ceb525c052b 100644 GIT binary patch delta 61 zcmeyx(#*{l&t4nwOW00SG<@uHMLfoKZSKKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=6n G#$EurOA=N9 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc index 3961476db428dac6166a7d36a1026e06e964a33a..6aebccc4ab250c51e148064867223d46aee5fe1a 100644 GIT binary patch delta 62 zcmX?Xdd8IdG%qg~0}xz~xv`O3l1U{?KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>JyVMW0E&tgt^fc4 delta 53 zcmX?Odf1fvG%qg~0}y-+T)mN7l1VyIKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--N HQ;P%u%7hW0 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc index 043510a312210cfbe91bc807dd56992e9108d660..7fb84d4be0c94150c629109def5637c3c56da5ad 100644 GIT binary patch delta 62 zcmca7@?3=bG%qg~0}xz~xv`PkolzxAKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBF5^cI0Ff*fSpWb4 delta 53 zcmaDZa!-W&G%qg~0}y-+T)mOool!bbKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m! H<3|nv&;1fg diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc index 42aec84cf9f16d49d62e9daaeb97edda8abf22d4..1ce75553b382270d2c9565a5ad53ea3c20097b53 100644 GIT binary patch delta 47 zcmZ2^igD8^M()$Ryj%=Ga6RV6M($*0#;nQt%woK`nZ?PeIXQ`WsrjYFn>(2oBmw|- Ct`J}V delta 38 tcmdmVigD#BM()$Ryj%=G@G)@pM($*0#>C0_%wjCQnZ?PQCowNb1OV^_3{e08 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc index 8f44c2a0794bd80b47c090735f8368e2c9566f55..3a5090009870876b2e9ccb8b5310fbc43f827974 100644 GIT binary patch delta 62 zcmeAQnjFM^nwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}9bm+7t=0D1-$w*UYD delta 53 zcmbOn)D^^inwOW00SG<@uHMMa&LkbLpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV?kL H>8=_8ysQzZ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc index 34c13f741c4996467b10cc232c2ea5889099fe1c..82af142d7cad8316ee7f29ec80085eaf7fbb7c71 100644 GIT binary patch delta 41 vcmX@Xc#)C&G%qg~0}xz~xiOL3n6Y4@y%=w9W^rvcW^wYwqyo8ziG%qg~0}y-+Ts@Kd5@X)PdtxlUnZ?PI85!3A0Hu8jasU7T diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc index d5f864a1c9f979bcccd477f2743b3fc6b017ecc5..cfb2556ebb40165147ed5f9ab1436e0a4d8de4ed 100644 GIT binary patch delta 62 zcmaEE_Q8z%G%qg~0}xz~xv`Nuheah>KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsB$lgU0IQc4>Hq)$ delta 53 zcmexh_S}s7G%qg~0}y-+T)mMyhebL{KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=AI HmaAd_<~b7E diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc index af30ba74d69562691c79d6257626200a309f3b5c..edb3bd9aafd1f5e8404d8c4530a4d1244ae89fbd 100644 GIT binary patch delta 62 zcmX>Qa58}VG%qg~0}xz~xv`PEokJyAKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsT8_!`0IGf#)c^nh delta 53 zcmX>Za3FyDG%qg~0}y-+T)mOIokKcGKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9n Hj>+->x=u{lj{rj&7|Q?v delta 62 zcmZ2@l4r(A9`4h;yj%=G@G)?8BX=t|<5q4ab|>jL{fzwFRQ;lY)RLn7vdrWR{p9?V QRDIvf;^ggSPE6U40BT|syZ`_I diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc index 3a7a0a00da3db15c7f88ba56146bffce81400509..4f09c4d77b6315672b244b057e7085c2a91793c3 100644 GIT binary patch delta 62 zcmX@edy1F)G%qg~0}xz~xv`OZ93d9yx~ GAtL~?w-B`e diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc index 922676e3cb9957287e5eb128c3db18bbfddd0bd8..90a8dfa818d9ae2df9c2f4436f99d3e9d8be1851 100644 GIT binary patch delta 62 zcmZ1*w=ItQG%qg~0}xz~xv`O3k69&IKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB2=h&C0G9$5VE_OC delta 53 zcmdlMw>FOZG%qg~0}y-+T)mN7k6AiOKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m{ H^G$64)gKa4 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc index 1c931af4f8e1ffdf1ebf181d13fc8506f723e5ec..ad8b8439a83bd7936e6600f6e95f4b6321427aaa 100644 GIT binary patch delta 62 zcmdn2ut$OWG%qg~0}xz~xv`P^JhMugenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMugrEl0GcEgd;kCd delta 53 zcmdm^uvvloG%qg~0}y-+T)mO|JhODHenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{MW H%yv8g)@>4P diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc index 1dad02089b72981c9307485db4a8c9acb0935f18..24e9707d347fa4407881051a57794ad96344c5f3 100644 GIT binary patch delta 62 zcmcc1_l%GGG%qg~0}xz~xv`O(i%BIxKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEL>0h2B(0DpTG0ssI2 delta 53 zcmaFHcbAX*G%qg~0}y-+T)mN-i%B|MKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--r HlP)U&!9Wo6 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc index bec90494c588299bcfe7ff8583b1d349830e5437..500c7548ac0d37efd10f9d621ad6b8f953675018 100644 GIT binary patch delta 62 zcmZ1?vsQ-tG%qg~0}xz~xv`PEj7cR`KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMs9HtI#0D!6$>Hq)$ delta 53 zcmZ20vqXmbG%qg~0}y-+T)mOIj7d61KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9@ HrVefZ!F& Gj9&r6xf0d@ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc index 7961b11e784e8db3aacb81c9fdcb7664c2d5ebbc..fb01d207ca6703bdebcdeb7bf36024f47dd75341 100644 GIT binary patch delta 62 zcmdmJyvvyTG%qg~0}xz~xv`PkidiL5KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB0&}1k0ELSci~s-t delta 53 zcmdmGywRBZG%qg~0}y-+T)mOoidi~BKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m> HbD$Ui#tadC diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc index 908a35574e9b822f1e2fbf95766369aaf5ceb3e8..0ea6e3435dc0a264e33d930fdc4f771a4b3c59f8 100644 GIT binary patch delta 62 zcmeyJ){@SBnwOW00SKlKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC(RN HZVej%`0)~_ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc index 01bf90203236fd19578347e40af6f4f39dbfd154..b6940753bce90a3844baa518a97e020a15344199 100644 GIT binary patch delta 62 zcmX@gevX~{G%qg~0}xz~xv`Pkmr*5AKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBF=G%D0DYAeYXATM delta 53 zcmX@dew3a2G%qg~0}y-+T)mOomr*)GKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mo HV-OPnzbz45 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc index bcf54a584112e138c57f75bc8dc6db26c739c047..a6e72f834641baf108098706dd2cbe4bbb923575 100644 GIT binary patch delta 45 zcmaFJ_KJ=BG%qg~0}xz~xv`P^6C-2dK)TCWc- delta 36 qcmaFG_K=PHG%qg~0}y-+T)mO|6C-29syi1t-G%qg~0}y-+T)mOIk4ZX8KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=A4 HCN_2e$u<$4 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc index d23e59c3f12b55d23068869e45ce154d68c1a3bf..bf23477e9dd99206aff63ebdaf870ab652688f11 100644 GIT binary patch delta 62 zcmew={ZE?vG%qg~0}xz~xv`PkhD#+!KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB64zvA0G^2!FaQ7m delta 53 zcmew-{Z*R#G%qg~0}y-+T)mOohD$nGKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*n6 H*JNe@+ddK@ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc index d8dbeb43989517eb0d81a16a1902b8d53621466a..4a558bf7fc416cf0c8ab5e531cbbdcee0edd90d0 100644 GIT binary patch delta 62 zcmeB>nkdD6nwOW00SKFw0jC0EF!p1ONa4 delta 53 zcmbOz)gi@wnwOW00SG<@uHMM~mRUMnKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--S H%U2En!_X1+ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc index 4d2747e2313ab88e67eb24deb736b6844ead1d1c..e306936853eac327eeef75fa08177be003ba9bf5 100644 GIT binary patch delta 45 zcmX@XbCHMpG%qg~0}xz~xv`Num6h!V1X% diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc index c8727f119869bbe493150144ba995c9c6a67e372..736bec9fd75b30b94b5f13a2700822fad844f5b8 100644 GIT binary patch delta 62 zcmZ3KusVVJG%qg~0}xz~xv`PEf=eYqKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqEMsJgyd10HCWDlK=n! delta 53 zcmZ3Tuqc81G%qg~0}y-+T)mOIf=fDFKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=AO Ht`=1Q+^iCW diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc index 0968200fea62483a6a9aad08d9b92d9d116d6b35..ffc2be42f17cff10bd38b9507dbff8e9da1616e8 100644 GIT binary patch delta 62 zcmbQ_KHHu9G%qg~0}xz~xv`PEgH@6 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc index cc91c13acb44feb537275fff181cd536cc89a7c9..f1e1698ac9aa4bb02cf61d9f8e2b31437b560789 100644 GIT binary patch delta 62 zcmdm>xKok)G%qg~0}xz~xv`OZ8LLX1enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMGptXz0Fm4l#sB~S delta 53 zcmdm~xIvNoG%qg~0}y-+T)mNd8LM=tenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{M? HtWUTA&+rnr diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc index f6200446fe4569591959e51e106d3c211bbc7d7a..44533e3d4e7a0a56a2debe185137c48fdb6245fa 100644 GIT binary patch delta 62 zcmcZd6xA diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc index eae0f842641d4e34f9054dfee1d205d3cbc7b5b3..c4226bfea621f31d8c725e45c5d3e77c1cad51fa 100644 GIT binary patch delta 45 zcmdn3dqkJ}G%qg~0}xz~xv`O3iG?v?vL1^VZ*FFBa%xUaVqR)~Y4K(cmSQ0QG>HxZ delta 36 qcmX@2yIYt0G%qg~0}y-+T)mN7iG?wIvL1^Vi*IIe^5#I6Vj%#$i3#%n diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc index 86e53ab3842882bd73a720d28ef9102cc49a3aef..4760c70a6e4ae4136aadc235112690672fe5d759 100644 GIT binary patch delta 64 zcmX?gp7HW|M()$Ryj%=Ga6RV6M(z!~Dhc`-`MIh3MFpuPMfqi!$r<{|`6;RTxtYbu TsW~}`d8zrO#hb73eu)GC`7jvW delta 55 zcmcb7p7GRqM()$Ryj%=G@G)@pM(z!~(&73U`MIh3MFpuPMfqi!$r<{|`6;RTzL~|z KoA2{}i39*Bh7`^K diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc index d00605b2a0abb1cd0290e831bbcd25976157d9b1..9e1e4b5870264d64a8fea3c2729c43471b03a1f3 100644 GIT binary patch delta 62 zcmZ3=yN;LpG%qg~0}xz~xv`P^1EWfYenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$F{XYN0F6@>!~g&Q delta 53 zcmZ3-yOfvvG%qg~0}y-+T)mO|1EX}jenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?z) HrhXOx%Eb}2 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc index 70174c2bc04a71266c3b45dc50adcacc3bbf43af..96445b51f03b41d401053a196e00043dc3f639ab 100644 GIT binary patch delta 45 zcmeBnp5n}XnwOW00SK#uH#f65IW;FIF)ua0w0QGO&QIb1K3@-3 delta 36 qcmbQ@-0jSLnwOW00SG<@uHML9%*hxzxt>#u#W%A!dGjL9PvQW=6$?iI diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc index 0c47975282a9ebd8721431ba5c82e2961585dd59..10f4ba7dc0a28a5f6fdc9e7bc6bed18ec30d1407 100644 GIT binary patch delta 68 zcmbRDgKhZ_Hty5Byj%=Ga6RV6Ms5yXl_dR){M=OiqJq?tqWrSVC;$}VG?Rvb7AJzc?H^CUE delta 59 zcmZ4fgKgdqHty5Byj%=G@G)@pMs5yX=}7&I{M=OiqJq?tqWrSV-{=Dds_)&T%qs}!LC diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc index 34f7be36d55cbfc4554784ec5a944404ab106afe..650ca3590b82665d795114b0de8b477e5fde34bd 100644 GIT binary patch delta 62 zcmca4cw3PBG%qg~0}xz~xv`P^1(QmOenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$KIS8=0HYcfN&o-= delta 53 zcmcaDcuA1^G%qg~0}y-+T)mO|1(S5Nenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?zy H<|C{C-F*@} diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc index 7aed17b4802615afaf8bbb49e8a5dc2061eea4db..1ef750ffb841c469b94c5c454b60cf155f550348 100644 GIT binary patch delta 62 zcmeD8nBl>FnwOW00SKmYJNPpPZkPs-K%#oSd4I QlbDy9Us}95oOy==0Dk)w)c^nh delta 53 zcmbQ?(eJ^1nwOW00SG<@uHMLP$SfVBpOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV+=K Hd4~c3z>^Wi diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc index 010290f4769602465a0cd7d9f919471fadf2c0c1..546b8d061847d8ebccf826efcc8be9536d58c06d 100644 GIT binary patch delta 62 zcmdlOx+|3XG%qg~0}xz~xv`NufK??)KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB8LPD_0Gb08`Tzg` delta 53 zcmdlLx-pddG%qg~0}y-+T)mMyfK@tDKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*nD HtFK diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc index d473bcc660c90c0fef53d6fa02be70fc48b3a50a..38e61b1acce80c60fe6a6a187192c6ceb336bc2a 100644 GIT binary patch delta 62 zcmZn->R&CAQh00h@#ZfxX^l2*yo&&bbB)h{YYEh)+`%S_JDPtH$C)z8f=PEO6q QNz6;lFD>5OAkE4P0EwCvlK=n! delta 53 zcmeARYz^c-&CAQh00bWcS8wEwl9o=;&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTt%p H&B_Y^$k!2s diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc index b6b3750155786ac93544e0846c3304bf9805a906..7e3411bd4e316ac148457954ab86ea523bba080b 100644 GIT binary patch delta 45 zcmX@leS@3(G%qg~0}xz~xv`P^5Hn-)w-rUUMw7T?U`mYJNPpPZkPs-K%#oSd4I QlbDy9Us}9*F-tl-0CRd3nE(I) delta 53 zcmbO%(IvrsnwOW00SG<@uHML9%OV}6pOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoVBWso-0E%@LqW}N^ delta 53 zcmdnuyV{rgG%qg~0}y-+T)mN7npHYhKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--d HYnCDa%4QLi diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc index b07e1bad51c29eacbe37d80f71544a591e950e71..1c5ba5b56dc48dd74a3a39019b277ab2000b1fc6 100644 GIT binary patch delta 62 zcmbQNKS!VYG%qg~0}xz~xv`OZ2d7HBenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMdz@Q&0giwcsQ>@~ delta 53 zcmbQEKUtsqG%qg~0}y-+T)mNd2d8wHenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Np HoLhMT%+nH@ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc index cd44a080c3800aaf0ca718a09c3dbf397c347a17..0cd7a6a979b037ff51ed0999fdd6ca5debc38337 100644 GIT binary patch delta 62 zcmcbq@koRFG%qg~0}xz~xv`PkhFK*^KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB67zQf0F^=(YXATM delta 53 zcmaE)aZ`i)G%qg~0}y-+T)mOohFLmNKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*n6 H^LGIN)1DGs diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc index 233a6d0e86b993db0b5dd1d5c435cf5d3de02a7a..f013e1dce411076f4cfd6a42b7901621773ba296 100644 GIT binary patch delta 64 zcmew}m$7voBll@uUM>b8xE^z3Bew;cN}_&7er~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E SYEDjKUTS`6@#Z+Tc^&}5`WE^C delta 55 zcmZpj$M|P1Bll@uUM>b8_!zi)Bew;cbcB9Jer~FMQ9)`+QGQuwa)y3#eoCsoZ)S1w J=5)4s9sv2N66*i} diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc index 00cd4cff72488859c46cc20bc94462292dea95b5..f08d73f23d21fd2f84ce31b1972b55b680d75141 100644 GIT binary patch delta 64 zcmX>;mGQzC;>|%ErA`3A_ZGSU delta 55 zcmcaGmGSsgM()$Ryj%=G@G)@pMs6Jr=`j6_{M=OiqJq?tqWrSVG%qg~0}y-+T)mO|0E={lenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{N( HEE5y}0Av%4 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc index 8a8f1000dbce50ecdab3e07aefda25c06269c029..0d5e3d115dd40b966d1c0ecb69493deeda2ea9aa 100644 GIT binary patch delta 62 zcmaE1_|cI2G%qg~0}xz~xv`PkoLMDFKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELBEOWOg0G{0zDgXcg delta 53 zcmexp_`;C;G%qg~0}y-+T)mOooLM?jKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*m# HbGIk}+qM!L diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc index f142fbe9a7fa59db3bb70f63cdd7fd6955986dd5..70bb5e8c86ba73e90482ecde9a260449b093b269 100644 GIT binary patch delta 62 zcmca-{@k4VG%qg~0}xz~xv`P^4Xa9$enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$A+|H30K2*t(f|Me delta 53 zcmaEEe$SlyG%qg~0}y-+T)mO|4Xbpdenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?zq Hwlkst@@x{r diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc index 2338b1fec2c636f4e69b1d1b52cea92b4bde1f86..34194a23bb6c890411e7e9b8c976625ca61838d8 100644 GIT binary patch delta 62 zcmbQkx0H|jG%qg~0}xz~xv`NufJr4mKO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z QCowNIzqELB8PhTr0B({Mga7~l delta 53 zcmZ3=H;0e=G%qg~0}y-+T)mMyfJr)BKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*nD H(=rwSvU3r2 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc index 63deabc28b045f7f4a0f71e0dd0111d19d2536df..0a56f9b6cf14746d1eb600539d4e11d2918a7290 100644 GIT binary patch delta 45 zcmX@4d0LbEG%qg~0}xz~xv`OZG81Fc@Z delta 55 zcmaF2lj-SBChpU`yj%=G@G)@pM(*95(h>R@`MIh3MFpuPMfqi!$r<{|`6;RTzL~|z Kn_qFVOa=fd!xS?B diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc index c545acde8a9372c142d720328401d067b87181c0..f315d59e209200dd3f9aed333554c25ab4d340ef 100644 GIT binary patch delta 51 zcmaEVisi>C7Vgu$yj%=Ga6RV6Ms6W~#^lLz{9?ShnZ?PeIXQ`WsrjYF%@+LIE%+JD F#Q?y-5H|n- delta 42 wcmexyisk(&7Vgu$yj%=G@G)@pMs6W~#;D11{9-J=nZ?P?j{Msl`5Dc{04fd)F#rGn diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc index 937923e03c6c80797a0a3cf8456e41272d91d9a4..ab85184defc8af6e507a70c00d0cc3bd3a471b17 100644 GIT binary patch delta 62 zcmbOlI4_X*V7;&CAQh00h@#ZfxX!$EcF5pOK%Ns$W!)T2ho>mYJNPpPZkPs-K%#oSd4I QlbDy9Us}9bgy{_n0DHC-&;S4c delta 53 zcmeC=YvAKP&CAQh00bWcS8wEg$0!}8pOK%Ns$W!)T2ho>mYJNPpPZkPs_&awoV;0< H=?x12yYCUf diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc index 3d1d810111a0e03a7599b6f40036e5f40d645c91..91b072f438a9fdbdf34518d0f0af354e97f76f9b 100644 GIT binary patch delta 61 zcmdnaa)5>VG%qg~0}xz~xv`PkgHa_>KO;XkRllepwWKJ&EHgPnKRG`oRX;bgI5{;Z PCowNIzqELA9^**>Ur7~| delta 52 zcmX@WvYmzdG%qg~0}y-+T)mOogHbv{KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*k8 G<4FLfPZ5Ox diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc index cefba5e8105a96b12a185f10e93795730bec4b06..caaf38a6b51369b51445d0e71256ed1e07e900e3 100644 GIT binary patch delta 62 zcmaE0{L+~FG%qg~0}xz~xv`P^8jDK2enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUMKP*o~0Jzf+(4G%qg~0}xz~xv`P^3Oi%cG%qg~0}y-+T)mOomP0y1KO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC*mp HM<71{#?ujm diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc index ee34a8d37aaa32a2229c5eb4b5e2caf0691fa0e1..3cba63253c9bf180986f63e28118df5466d895d8 100644 GIT binary patch delta 47 zcmaFW%JjLFiTgA!FBbz4T#vc2kz16RF=4VIvlwq~W^rA10HZAx(*OVf delta 53 zcmaDNa9e=;G%qg~0}y-+T)mO|Hj{LOenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?y^ H<|tMG-H;K) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc index 80673c0436db983d4da1f6823af9149ef1ad492c..6d7acaaa33930672a92434007fa601ff92817bd8 100644 GIT binary patch delta 47 zcmZ3!gK5JKChpU`yj%=Ga6RV6Ms8+d#>C0|!eYF+nZ?PeIXQ`WsrjYFn>B_1bpQZg CSP#Yk delta 38 tcmdn6gK7B=ChpU`yj%=G@G)@pMs8+d#)!%M!eT7GnZ?PQjfDSo007_i3%39O diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc index 5fe3de415bf38b21f5e90ed8a143e8a7c4b11b56..5cd787f9ece2827ad5f9a0f1f33eeb3464b98d98 100644 GIT binary patch delta 47 zcmX?bh3VuKChpU`yj%=Ga6RV6M(#r*j0uy^iHPy$W)>%>=Hw*irRJ9wZ+;;XF%%J{wNYL6#xg>4aWcg diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc index f6f3e4e197afafc4c2780fbcc1ade93327f9e3c4..b88b38686d23f39db64ac503380c06e125db0207 100644 GIT binary patch delta 45 zcmbOtHCKxJG%qg~0}xz~xv`PEg@rM5@&pz!-rUUMC;>}apJ)Qvo`t=xb delta 55 zcmaEMoB8f-X71Cxyj%=G@G)@pM(%ue=`j6_{M=OiqJq?tqWrSV%>=Hw*irRJ9wZ{`*-%mx6Y CoDm)X delta 38 tcmX^5jd9O6M()$Ryj%=G@G)@pM((HljM0-n@{6(fW)>%J77;Ma1^^Gv4G#bS diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc index 0ab9799cc1aaa235b615a33e2ce73c715e9a7f62..7d1020da28f5a269ab7baf2e884e1949043959c9 100644 GIT binary patch delta 45 zcmZ1l5w( diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc index cebe067b572f82dce0e11242143511f4394f832c..d32c0d24412892f011f0aa07918d1cc49ed3d46b 100644 GIT binary patch delta 62 zcmX>icUF%3G%qg~0}xz~xv`OZ9lJ`renx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUME9?dA0Gk#Th5!Hn delta 53 zcmX>rcSMf+G%qg~0}y-+T)mNd9lLaxenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{N7 H>;>!q*Od}? diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc index 85fd090c50de7138d116510da8a845b69fd64e4b..de134ee707ca9922608c40fa5cffa329198c8acb 100644 GIT binary patch delta 59 zcmaFI^qqbOwpBRg8W^wZ7B)$wI0NkPr761SM diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc index 0f29fe25aebe55e9b5e388e5513983890729f4b4..b285a19d08b34df2f470dfe193e56f34c0ab2d9f 100644 GIT binary patch delta 62 zcmZ23xIvKnG%qg~0}xz~xv`P^EVD|cenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM&&*p`0FGl8vj6}9 delta 53 zcmdlWxLlC?G%qg~0}y-+T)mO|EVFcienx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Lr H%v)Fh%!v}D diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc index 3edfc2898d3716ecacc34b406cd10c41243187b0..95a0365faf481d2f79892524b8c54387c2cae0cd 100644 GIT binary patch delta 45 zcmX@jafO5XG%qg~0}xz~xv`Pkh>00<6vK^Bci*IIe^5#UQ&x`=N)e3O{ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc index 62b5220c1f8487984b004656a8f7d3fd480803a6..af4b1253f7bcacb54e5dc46836d1f008fb6bffa3 100644 GIT binary patch delta 62 zcmZpx?yu%P&CAQh00h@#ZfxZC;8e-f&&bbB)h{YYEh)+`%S_JDPtH$C)z8f=PEO6q QNz6;lFD>4j$0=a~0GD+Y+W-In delta 53 zcmeCLZmZ@#&CAQh00bWcS8wF@;FM0#&&bbB)h{YYEh)+`%S_JDPtH$C)%VRTPTpM3 HDPaNt)bJ6^ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc index f60f3e100e6c9fe1c0d09a0f8ca73f0cd7c66856..b3dd070928e5b9d0a63768953125bdf7c345f244 100644 GIT binary patch delta 62 zcmX>rbxn%D+dQF0D~(P5dZ)H delta 53 zcmca6bykY|G%qg~0}y-+T)mN7o3d17T2 E0GNUiO8@`> diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc index 5e1dbaee77d06a8b14380017a705633540cf9bf7..0c874a54d389f1ec3a7706a0710d66c75980c361 100644 GIT binary patch delta 64 zcmdl!pYgzaM()$Ryj%=Ga6RV6M(!?Vl??ri{M=OiqJq?tqWrSVC;>{bF*?j=dP8S6L delta 55 zcmX>wpK<$qM()$Ryj%=G@G)@pM(!?V>3IE&{M=OiqJq?tqWrSVC;?4COw_5?(s2BhM delta 55 zcmaDnhw0WFChpU`yj%=G@G)@pM(#)s>16$k{M=OiqJq?tqWrSVbwBI0SY|; diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc index 6ae719adbd9e0623c7f7e772660276880c987a67..c2abd239cb7c2e3a8fdaee2288eee0752bedd572 100644 GIT binary patch delta 47 zcmew{pRsWPBll@uUM>b8xE^z3BX=q{W9H;SZZY26%;MzKoSekG)cn%o&E4F&E&y*@ B5B2~6 delta 38 scmZo%!1!Z6Bll@uUM>b8_!zi)BX=q{W5VP@ZZQ_$%;MzDQ@C?o0O)uN=Kufz diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc index 2ae0e54f92c0ba4379d46be2ce0ddb871c4b4ca9..97227dbd84ce63c14dd604e00975dde8cd2a6323 100644 GIT binary patch delta 45 zcmexo(Qe6onwOW00SKB~3pgKQ~pss35hZD8DQ-IYU1=KP6Q^H?uf7 SH76%AFEzikc(Wc$Qv(31a29v~ delta 55 zcmdnl$+V%9iTgA!FBbz4d<I#xd;KQ~pss35hZD8DQ-IYU1=KP6S)H?uf- JvpGvs0|4e|5@`Sc diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc index bf72e2b6fc4931994d003764167a86c791b08af7..d69d4ba158d2cae1021f94b0b3b92df2669abd3b 100644 GIT binary patch delta 41 vcmX@cc%G5_G%qg~0}xz~xiOL3m@#jny%=w9W^rF#0wfKY delta 32 mcmX@lc#M(zG%qg~0}y-+Ts@K7m@##ty%>vcW^wYwq+9@$qX~)t diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc index 9b998dd210ce420bc896adff779c96acea8e5fa5..6e27cf8f5c9716b6cc3bf51b9b350c8319b42867 100644 GIT binary patch delta 45 zcmdnNcaV?!G%qg~0}xz~xv`O3l##JwvLd4xZ*FFBa%xUaVqR)~Y4K)j#=oopDHjeS delta 36 qcmX@ew}X%SG%qg~0}y-+T)mN7l#wxivLd4xi*IIe@@5ytzpMbW!wM7t diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc index 0f67c67497b5c5ab33eb3706becbeca32901b5a1..38237878e5f20b7136af5198e013e7af75e67730 100644 GIT binary patch delta 45 zcmZ1zwKaqZj* diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc index 4e61d12c449794b4a6202fd0e32b676df7610fc5..359257d85f1867af4eb838408afb0219e093eb71 100644 GIT binary patch delta 47 zcmZqL&)B`6k^3|+FBbz4T#vc2k^3?aWB%m(JYu}LnZ?PeIXQ`WsrjYFn}70j1_1zm CD-lKj delta 38 scmeC)&)Bq|k^3|+FBbz4d<C;?2`VyvhLJo)@tI delta 55 zcmaDgmFeMBChpU`yj%=G@G)@pM(#op=^Xux{M=OiqJq?tqWrSVE0>Tk0GK)zh5!Hn delta 53 zcmaE0b=`{lG%qg~0}y-+T)mN7o=ZAOKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--l Hmyj?3)$0*= diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc index 8f1516932c0bcdc2ceab4f2992db7b14fa7a6951..d17e175e6969a6416b2f79032553e110853335cd 100644 GIT binary patch delta 41 vcmcc4_<)i7G%qg~0}xz~xiOL3n6ZAMy%=w9W^rp delta 32 mcmaFBc%6~^G%qg~0}y-+Ts@K7n6Yf4y%>vcW^wYwq&fhZo(awX diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc index 575a86e4bcaa02ae21789ed38bd7e00d652234a2..59a052e2929eda1a0384f0de1fff3554b8944383 100644 GIT binary patch delta 64 zcmbQ-!8oUbk^3|+FBbz4T#vc2k$a-NO1*wYer~FMQ9)`+QGQuwa)y3#eoCr-Zf0?E SYEDjKUTS`6@#gLFF)9GPHWwKH delta 55 zcmbQ!!8o~tk^3|+FBbz4d<c$|^@G%qg~0}y-+Ts@K7m@#dly%>vcW^wYwq&xtW?g@?n diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc index 4533288e787832403c6ad1a21e2ff64812787a75..b478edb9df0e96a25be421d916212e7c82e11cbc 100644 GIT binary patch delta 47 zcmaEUi0Q*2ChpU`yj%=Ga6RV6M($=A#;nQxGGe^BnZ?PeIXQ`WsrjYFn^(&0YXJb3 CC=t2< delta 38 tcmexxi0SzuChpU`yj%=G@G)@pM($=A#>C0}GGZ*gnZ?PQH_7a40RRWC4XXeE diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc index 6ae68b030795217bb192660b6c6b399ec8a91f19..10d37fa36fe328bed48185f695cbc1b124914520 100644 GIT binary patch delta 41 vcmcb}c#DzyG%qg~0}xz~xiOL3n6YxAy%=w9W^rvcW^wYwqzV9++zGY- diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc index 2ada1007fb47c0660018b905169cef2266cf258b..5c99d36f8498fdedb1b8549473b4938d38c37ea4 100644 GIT binary patch delta 62 zcmZ3;w~CMZG%qg~0}xz~xv`P^E{jT~enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$W>z^S0E)5|JOBUy delta 53 zcmZ3*w~&wfG%qg~0}y-+T)mO|E{k-5enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?y! HRyigB$Wak3 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc index cfd949ca430ed27d8487fe9695340b02eeae535d..4135d644759716aa55395dbc6674a548b592f6a4 100644 GIT binary patch delta 62 zcmZ2%xyF+FG%qg~0}xz~xv`P^EQ?B`enx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM&nz3n0i#S8Bme*a delta 53 zcmZ2ux!98XG%qg~0}y-+T)mO|EQ@r7enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{Lr HEE~iD-U$;G diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc index 36861a49431764f4a5d44285f86c8585e0e505ab..0a16ab043bd252fc8711ef97e1c004ea5c19ff39 100644 GIT binary patch delta 44 zcmeyx(agzxnwOW00SK`%~00E(v;F8}}l delta 53 zcmcb}b%KlgG%qg~0}y-+T)mOIicvaVKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC=9* H#udx}$^Q}{ diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc index 8b096a93aee220f87a300f04b3e7e19943f815ea..3fad075ec6940b284143365fdf95f3c1e6c25852 100644 GIT binary patch delta 62 zcmcbv@IZn4G%qg~0}xz~xv`P^9lJ`Fenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfS$5e{b#0Iq=+5C8xG delta 53 zcmaE$a9x4>G%qg~0}y-+T)mO|9lLa*enx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq?za H4rdMk=Hn6p diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc index 01e2fac3f1602cd7d8f407c8af6d194fa6a9147d..d144fb67d301043bbd013ffbcabd27e86feed5b8 100644 GIT binary patch delta 62 zcmZ20zD=C_G%qg~0}xz~xv`OZCzDFHenx(7s(w*HYDrOkS!QyEesX?Fs(x-}adK)- QPGVkaerfUM`%LFJ0FbK|!2kdN delta 53 zcmdlczE+(3G%qg~0}y-+T)mNdCzEuNenx(7s(w*HYDrOkS!QyEesX?Fs=jY#aq{LD HOy@WN&ZH8u diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc index 6ad0eb93f82506be523592cee857504b81b401d2..84aaae238ed46976943f441b54b7b83b0387a2b8 100644 GIT binary patch delta 47 zcmX@OlJV3^M()$Ryj%=Ga6RV6M(!XM#*E2vEMmO5nZ?PeIXQ`WsrjYFn=4p60|0M( B53~RP delta 38 scmX@LlJVe5M()$Ryj%=G@G)@pM(!XM#`wu`EMhFanZ?PQ8(BO90P3a-r2qf` diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc index ecb522e6cd6e13f90956ad39dabd96d22e4591b8..5db5cd3e8878c3f2c02c8949904181f253000d5d 100644 GIT binary patch delta 62 zcmexg+EK=RnwOW00SKs^^Y0St diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc index b4731f45e6926cc4ced7e47d7bc1ffd676dfbd8b..39dfdc264c4c36dc077aea745af4acd1a5447c12 100644 GIT binary patch delta 45 zcmX@FenXx6G%qg~0}xz~xv`Nul##J?aw4M`Z*FFBa%xUaVqR)~Y4PT2#tJ?m8!0EDp>_5c6? delta 53 zcmbOmG9`rjG%qg~0}y-+T)mN7l2tlQKO;XkRllepwWKJ&EHgPnKRG`oRo^$WIC--N H>s1v1#e)&& diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc index d7c565fe2cf52e6d7cdef0d160d7d1a55d180d01..4352985f4fc5780eab2d069221f3ce5dd787922c 100644 GIT binary patch delta 45 zcmZn;?+NEV&CAQh00h@#ZfxXM=4Z^Btj{mTo10mjoSKu9n3tMgTD;kl-%17mGDr>O delta 36 qcmeAPZw}`^&CAQh00bWcS8wE2=4VWttj{mT;+t8Vyg7*9N(KPB5((A- diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc index cf9ca79ee9dfdcff3c4f32e7a9c284cb47da6893..2f0cacab14d1306695da3e03d44ccf72f8cbc77d 100644 GIT binary patch delta 45 zcmdm7y{DS{G%qg~0}xz~xv`P^2p418=3.8 -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: charset-normalizer <4,>=2 -Requires-Dist: idna <4,>=2.5 -Requires-Dist: urllib3 <3,>=1.21.1 -Requires-Dist: certifi >=2017.4.17 -Provides-Extra: security -Provides-Extra: socks -Requires-Dist: PySocks !=1.5.7,>=1.5.6 ; extra == 'socks' -Provides-Extra: use_chardet_on_py3 -Requires-Dist: chardet <6,>=3.0.2 ; extra == 'use_chardet_on_py3' - -# Requests - -**Requests** is a simple, yet elegant, HTTP library. - -```python ->>> import requests ->>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) ->>> r.status_code -200 ->>> r.headers['content-type'] -'application/json; charset=utf8' ->>> r.encoding -'utf-8' ->>> r.text -'{"authenticated": true, ...' ->>> r.json() -{'authenticated': True, ...} -``` - -Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method! - -Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code. - -[![Downloads](https://static.pepy.tech/badge/requests/month)](https://pepy.tech/project/requests) -[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests) -[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors) - -## Installing Requests and Supported Versions - -Requests is available on PyPI: - -```console -$ python -m pip install requests -``` - -Requests officially supports Python 3.8+. - -## Supported Features & Best–Practices - -Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today. - -- Keep-Alive & Connection Pooling -- International Domains and URLs -- Sessions with Cookie Persistence -- Browser-style TLS/SSL Verification -- Basic & Digest Authentication -- Familiar `dict`–like Cookies -- Automatic Content Decompression and Decoding -- Multi-part File Uploads -- SOCKS Proxy Support -- Connection Timeouts -- Streaming Downloads -- Automatic honoring of `.netrc` -- Chunked HTTP Requests - -## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io) - -[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io) - -## Cloning the repository - -When cloning the Requests repository, you may need to add the `-c -fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit (see -[this issue](https://github.com/psf/requests/issues/2690) for more background): - -```shell -git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git -``` - -You can also apply this setting to your global Git config: - -```shell -git config --global fetch.fsck.badTimezone ignore -``` - ---- - -[![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf) diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD b/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD deleted file mode 100644 index f34e5a1..0000000 --- a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/RECORD +++ /dev/null @@ -1,43 +0,0 @@ -requests-2.32.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -requests-2.32.3.dist-info/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142 -requests-2.32.3.dist-info/METADATA,sha256=ZY7oRUweLnb7jCEnEW9hFWs7IpQbNVnAA4ncpwA4WBo,4610 -requests-2.32.3.dist-info/RECORD,, -requests-2.32.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -requests-2.32.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92 -requests-2.32.3.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9 -requests/__init__.py,sha256=4xaAERmPDIBPsa2PsjpU9r06yooK-2mZKHTZAhWRWts,5072 -requests/__pycache__/__init__.cpython-312.pyc,, -requests/__pycache__/__version__.cpython-312.pyc,, -requests/__pycache__/_internal_utils.cpython-312.pyc,, -requests/__pycache__/adapters.cpython-312.pyc,, -requests/__pycache__/api.cpython-312.pyc,, -requests/__pycache__/auth.cpython-312.pyc,, -requests/__pycache__/certs.cpython-312.pyc,, -requests/__pycache__/compat.cpython-312.pyc,, -requests/__pycache__/cookies.cpython-312.pyc,, -requests/__pycache__/exceptions.cpython-312.pyc,, -requests/__pycache__/help.cpython-312.pyc,, -requests/__pycache__/hooks.cpython-312.pyc,, -requests/__pycache__/models.cpython-312.pyc,, -requests/__pycache__/packages.cpython-312.pyc,, -requests/__pycache__/sessions.cpython-312.pyc,, -requests/__pycache__/status_codes.cpython-312.pyc,, -requests/__pycache__/structures.cpython-312.pyc,, -requests/__pycache__/utils.cpython-312.pyc,, -requests/__version__.py,sha256=FVfglgZmNQnmYPXpOohDU58F5EUb_-VnSTaAesS187g,435 -requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495 -requests/adapters.py,sha256=KIcecscqam6reOCXRl4DwP4jX8Jcl8sd57ft17KR2cQ,27451 -requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449 -requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186 -requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429 -requests/compat.py,sha256=C5w_DPLSurXPgcdWU78fora0APmbYkX2G89QvH5xzPA,1817 -requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590 -requests/exceptions.py,sha256=jJPS1UWATs86ShVUaLorTiJb1SaGuoNEWgICJep-VkY,4260 -requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875 -requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 -requests/models.py,sha256=k42roXzC8u_OagAPQi9U4MkfO7i4r2FdaqvMqstPehc,35418 -requests/packages.py,sha256=_g0gZ681UyAlKHRjH6kanbaoxx2eAb6qzcXiODyTIoc,904 -requests/sessions.py,sha256=ykTI8UWGSltOfH07HKollH7kTBGw4WhiBVaQGmckTw4,30495 -requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322 -requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912 -requests/utils.py,sha256=HiQC6Nq_Da3ktaMiFzQkh-dCk3iQHHKEsYS5kDc-8Cw,33619 diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL deleted file mode 100644 index bab98d6..0000000 --- a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.43.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt deleted file mode 100644 index f229360..0000000 --- a/.venv/lib/python3.12/site-packages/requests-2.32.3.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -requests diff --git a/.venv/lib/python3.12/site-packages/requests/__init__.py b/.venv/lib/python3.12/site-packages/requests/__init__.py deleted file mode 100644 index 051cda1..0000000 --- a/.venv/lib/python3.12/site-packages/requests/__init__.py +++ /dev/null @@ -1,184 +0,0 @@ -# __ -# /__) _ _ _ _ _/ _ -# / ( (- (/ (/ (- _) / _) -# / - -""" -Requests HTTP Library -~~~~~~~~~~~~~~~~~~~~~ - -Requests is an HTTP library, written in Python, for human beings. -Basic GET usage: - - >>> import requests - >>> r = requests.get('https://www.python.org') - >>> r.status_code - 200 - >>> b'Python is a programming language' in r.content - True - -... or POST: - - >>> payload = dict(key1='value1', key2='value2') - >>> r = requests.post('https://httpbin.org/post', data=payload) - >>> print(r.text) - { - ... - "form": { - "key1": "value1", - "key2": "value2" - }, - ... - } - -The other HTTP methods are supported - see `requests.api`. Full documentation -is at . - -:copyright: (c) 2017 by Kenneth Reitz. -:license: Apache 2.0, see LICENSE for more details. -""" - -import warnings - -import urllib3 - -from .exceptions import RequestsDependencyWarning - -try: - from charset_normalizer import __version__ as charset_normalizer_version -except ImportError: - charset_normalizer_version = None - -try: - from chardet import __version__ as chardet_version -except ImportError: - chardet_version = None - - -def check_compatibility(urllib3_version, chardet_version, charset_normalizer_version): - urllib3_version = urllib3_version.split(".") - assert urllib3_version != ["dev"] # Verify urllib3 isn't installed from git. - - # Sometimes, urllib3 only reports its version as 16.1. - if len(urllib3_version) == 2: - urllib3_version.append("0") - - # Check urllib3 for compatibility. - major, minor, patch = urllib3_version # noqa: F811 - major, minor, patch = int(major), int(minor), int(patch) - # urllib3 >= 1.21.1 - assert major >= 1 - if major == 1: - assert minor >= 21 - - # Check charset_normalizer for compatibility. - if chardet_version: - major, minor, patch = chardet_version.split(".")[:3] - major, minor, patch = int(major), int(minor), int(patch) - # chardet_version >= 3.0.2, < 6.0.0 - assert (3, 0, 2) <= (major, minor, patch) < (6, 0, 0) - elif charset_normalizer_version: - major, minor, patch = charset_normalizer_version.split(".")[:3] - major, minor, patch = int(major), int(minor), int(patch) - # charset_normalizer >= 2.0.0 < 4.0.0 - assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0) - else: - warnings.warn( - "Unable to find acceptable character detection dependency " - "(chardet or charset_normalizer).", - RequestsDependencyWarning, - ) - - -def _check_cryptography(cryptography_version): - # cryptography < 1.3.4 - try: - cryptography_version = list(map(int, cryptography_version.split("."))) - except ValueError: - return - - if cryptography_version < [1, 3, 4]: - warning = "Old version of cryptography ({}) may cause slowdown.".format( - cryptography_version - ) - warnings.warn(warning, RequestsDependencyWarning) - - -# Check imported dependencies for compatibility. -try: - check_compatibility( - urllib3.__version__, chardet_version, charset_normalizer_version - ) -except (AssertionError, ValueError): - warnings.warn( - "urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported " - "version!".format( - urllib3.__version__, chardet_version, charset_normalizer_version - ), - RequestsDependencyWarning, - ) - -# Attempt to enable urllib3's fallback for SNI support -# if the standard library doesn't support SNI or the -# 'ssl' library isn't available. -try: - try: - import ssl - except ImportError: - ssl = None - - if not getattr(ssl, "HAS_SNI", False): - from urllib3.contrib import pyopenssl - - pyopenssl.inject_into_urllib3() - - # Check cryptography version - from cryptography import __version__ as cryptography_version - - _check_cryptography(cryptography_version) -except ImportError: - pass - -# urllib3's DependencyWarnings should be silenced. -from urllib3.exceptions import DependencyWarning - -warnings.simplefilter("ignore", DependencyWarning) - -# Set default logging handler to avoid "No handler found" warnings. -import logging -from logging import NullHandler - -from . import packages, utils -from .__version__ import ( - __author__, - __author_email__, - __build__, - __cake__, - __copyright__, - __description__, - __license__, - __title__, - __url__, - __version__, -) -from .api import delete, get, head, options, patch, post, put, request -from .exceptions import ( - ConnectionError, - ConnectTimeout, - FileModeWarning, - HTTPError, - JSONDecodeError, - ReadTimeout, - RequestException, - Timeout, - TooManyRedirects, - URLRequired, -) -from .models import PreparedRequest, Request, Response -from .sessions import Session, session -from .status_codes import codes - -logging.getLogger(__name__).addHandler(NullHandler()) - -# FileModeWarnings go off per the default. -warnings.simplefilter("default", FileModeWarning, append=True) diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index d5e5762b1fa616d167472805587a1f66bf822b31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5408 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 diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/__version__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/__version__.cpython-312.pyc deleted file mode 100644 index cc7f5a8f894a04706e108c8739062bf05600e0be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 572 zcmY*WL2uJA6n5IJP1B{T7@`tFA{Qj8u^77Bgb;|!Bm|nKammHX;gp+`9E3kMgXz_N?<{QBV-wZZ+C+P5Q(B*bu^Ip(fIOG1(R&ZW`Ho6=gEleyk z@9^a0$UBqDJ1o*L(|&nBHYV2xl&+8Z3PNsT;4;#_kY#7$@8b~dNsE;3E66f1v3Cr@ zl;ute&u7<)Y6M@ZY3aPk!zhNmiT}79y_ESviD_)S|D44Z7W3=lXK(fC{WlN6o3XR( zF=m8GfU)H^V;pp(L~ex4Q0OvNs02Zuu{kJxt)kGgCPyYUsoa` z$aHhL%UHBd3#pAXoZ-e7a?y19@-2;J3RLA_l$?twrjg`8Uke>ke-7E4CgPOlR}J0w zpG>G02KKP%SvUoK(@6SHrhdM-w4bJu7YRI5Lwu$AR(*ies%=@;$|3Hk>eaV)tL@s^ nt-AH#PSvlsZlmVjtDJi0;ZHKG2X|2%jw`qBKX_-W0owlnR#>}0 diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/_internal_utils.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/_internal_utils.cpython-312.pyc deleted file mode 100644 index 4e379f30824dfd4174c761dcc1203a1c2dc6b972..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2012 zcmah}-D?zA6u&dGpR-9eu|`QWda-GA`>{(SQivZA>$V!IhD|JF)r_+d?`W^3Wfd`z6?a#?a+rn>4R@+(}&_y&z=1UriNaaJNKOXJ0JI) z-@VU!dv$^%Q+T%UOO%k`rO+9X_G0U2Ts$H+DH2<;)rwM7Y|V~T)T&n06rvJP784z# z?Y|v;rnIDDbXQEQi;3-uiFYyaT``F+CIK->EU3jKL?MoU9a5k}4tRCg-EaSbjQA3)vw)%l5Q{JNakHTU|vqbmdd zQSr0{UYof(-^Q%YkFKP5C@d90ogtZ^;kPx7(<36Yd=bt)D;8K&~F&y_M^-7CiiT5dknz&Z66sA$wc>O`Gk#Z6q* z7;tcfDSLnjo#F^P(e~rGC<1j6umm0FEjQp=o-JdU*asWh!g3iHgl=50Fs#WMSvp&G zFdUa{<#cl9;|mvP-3^lxUIm+oA=t9RAsZTIv_N;b<8ooT7BE@5AQlDy+={g_3lg39 zf#%f(m&L{0U(MypUKMhz210VpD(5gU$X#@}mCIs@mU0!Rl&dvF*>jI)KOE0-N5BU) z)4FBe1fJ_KIk$t!*;*s$7oO27gd_`|ob_PPA^dm_r#s|toh0@=9Q*dY7xA%X-`;!L zW`gvj?@Vn*NqX=>^?r5j{CeLz_q2^v-<_!~G$B@(2PkwovWUX}afGW=j^p? zwug?>8|d&xixm@0(r$v5Jg*WO1W}V)jx+m7Cc@uG8+JuU^O0>QBH`Q&F8&Kc+PljrgU_2Y?xfp(VLw&CRnEBQTP zy@&~m{VH*)>e=-QoMA^$C%eEO;)DfLlt0y&64~k_iv9;VyfJcgt?;Dyxac1{y*_fr zC&SHyV{2!hOg*0RkDgpVc*-Y3%|pl5N>3IaFZ!8N>xWMJgzgeKN4sqbupH;T~l$%=llEcO}UH diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/adapters.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/adapters.cpython-312.pyc deleted file mode 100644 index d2b727a188838d6b17c1379e11a9331008a27419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28352 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

U*p7LzB>WaU?^<&8U|U%S#aZMC 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 diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/api.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/api.cpython-312.pyc deleted file mode 100644 index b83f5bd1342c46f45cb8c248aba231b707613cbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7192 zcmeHM&u<&Y72YLRzeqc7o!E(+)ScEqDB2WhOLm+Rj%-DeYp0GZSaOlLDYUEIp}5v^ zmz`OPp{q1Gq(FOVb7)~5ib97TZ200s5BVQ@q0jm+Ale4dH-!5j?kU`doHTk<4I}%^wcn)# z|8b4WQ0A?woBC;iL4?`696Od{-kG!Le+bwnT^6p%?n-NF=X7g1BW04)CHCk0hVB9 znrdqG9_F+ypDl8@?C6MULjQ1vMkqOQN)AYnCEpo)=#U)C*nuA+W-f${D$n4REo2F- zaQt(Ppw23mk6^+fG~zNQu3K){ zkqVBv!bQhBD2{YCu*i{~ID?8J5apOfDlP#`p$Z=e@`9I&<}l{=5tvylLXHV|U1y%S zHOo%LH92lZ`;6sR2I+{e<|ju}U{tfjslxKSpuQlu;|i?S>qM^yfyXUB&!&~$3XUt4 z{*WjNagHcZQYauF6lN(E6rvPRpriqTqeedFrLL|eQZ8Q z@w#4i?J|5?g299e^CIvhnvrKQxXxKLQ^!0M5opN>3K_}fFU&EUi_k5(wiWW&)KNNm zz!DXJGKwEnD=ha;3m0IM6Kt{OJHRrwS(E&s`i(dN*5X#BXRj_r#m>%OLbE^F}6%*If}CPVlfMkPo*~O)N3EP*j5>M6o?Wk05vz z0mhi1wueQ8>9Vwzc(E8gFg89uP8>Brq;J0vE1!DM#9r%k>%V&c7w9`WosD9ds{-{)ew47;?g)yt3nXF2IR zhu=9InYzd>#?da0Q{dAlFArg3r;-J0Dw zi{YVzgqQHJ6A3QPTy)w33KG{ojUnK(I8Qxc96UvK7>JmO6kmf{ZYClNDCN^}v#tAQ zQ5RIu>0j+Vie>Mt#P4E5am9?>rbawq^ z6ip)=!KecRyhwpT#qsZ;veSE45yPDXzL+FqA5L1IbOD7}<`Z_||HI?`fXw!XKKRA|xiOwNNOysRSBR z7}cr>72*p0Ir>9M9U_Qi4cmu}qA`TDCE*#2IGoJ(KD zKlj(vrwDm{I2GIFE$oSJ5vY17^S23aRQzl^-cGJfZlq7FJI(Zo8zt15#)*e;w@0AN ze{Y|fqE9$a7->(D+WQ^u@sO)@BX&@pL!@ykeoY$hCd#j_&26NQti!pFG>s!l*;#!i zo0yr&rdtCG3r=7!EVMLrT#xvaI`Amwt%#ILTPlJmv#OQopHFHkbws*9cq;lw*|>Na z9q@il7E#bm}q#Gr2c>p4w-{$BrxKA~@#dRl+=-b+LJ$h{YK>4!HD?$XcbR34|| MmCb`gdc^1d11lO=;s5{u diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/auth.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/auth.cpython-312.pyc deleted file mode 100644 index 2608c72f80886358cc78a6b64eb8e6140fc04a0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13911 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 Rp3<_d^Jjm@;FD7Oe*p0xV7mYS diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/certs.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/certs.cpython-312.pyc deleted file mode 100644 index 3542f90d3a71cc4413e4efcad7de6e89f137c0d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmY*XL2DE-6i#+#ZH?BWU{CK-?7}*OdXpj)K@hY;>8Y0?J9)DcI+Kh^W_5ZHFM{G# z@HfcfFY)HhJ&B&YRf?CMOlGGCA0)i@c;ENlmzS?wTa0*{94?D5l#pK`xU_h1&L4Q^ z6A|Q?2r5Putw+ZrN-z@9$u1%0#)x3-&uBRSmYyjXC0*RblX3glCz!z(Em~)j9F4Q= z^?%0LQ7J9dTC^2{LDw2(!IcP&!2*pz0Rju&Rt_HRL!gj_%(+9DwMtZ&z)^_?L0YC& z)qz$BKi_=pDS}@ZUH8ENg@)%Re2xiYPYURC3*4jENvY;=I7~YQOyofl zAf$Ch&RQq6f<9k!<+%hywU9=u8kL*&q&ZiRmt4)UZ|)^XCD*tDUWSQxC*bs;*mtnl zaN}iF9UCSTLGU1w{SNOsp{sF^U2SGr&85n+gUNWck!3>XS+?33G@8g4hZ@Ce^!agP zq;j9hc_6uXo|d}C)HLXfUdX&mb1g7^CT*T33$G-t03V9GhL?Rm$kRn=%MzcF@@0fYf-D|T5 zqD9I9Bp;kpenAcq7Y_LY;hGcTfYZbYQj`O?1qXqAO7+azfYQ=@{Z(~U&2&|D&u{s> zhG0~`y1O)%Lg;TP?2dS!Y+DLKKOzfZWGR;FC|C(+6{}Y4dxT>uBC)%DV;m>yN*HrD zOf=&pNm|Mn2v+>c5rlARPfmn#+DhU~CVGR0vsMacNQS21O=(AS^Ej^{4{-Y-WhYp@p#A!&VF|SPq>qK z(mj9=xKnt_)v@j##0TAJJndF+#ht-3?jd|Av@bgExQFp!_Xs}XR&mumijTU-@G%9E z2;h>6>*OSvg?WygTvG6SN39@|n?W=cMpB(D&R$0Ad<|WTT}Eq4wRHVg zjgqTv!Z@oneK#<<_TAe<(=N4arn|n?c8DHOvTFNn=5+Ly=~)i3blVMl%Jsq@)Qfin1h(aKrPdYdZFILaQ;6iINLGA|FvB zDul?M3)B7?r9Q2yRDPF;fgO{3(e<4R^_K6FIt>V?{;J(<)tkOW>I*h&)@!T8Tdjj} zJqYVEUz?q)GnY5GCb@{fG&Dc zD0e^KES`Lh5{cp4vCVAx&PP2pmLL9Oyz*##_QCk%f>gE57ZCG>|~n zIb!O~ zGA2gfl-_i#zAI9OVfjtNph?K0h_Ma}%NO-eu79Qs+gtKQ+yGG|ZDs_Wx&Aya3N4cv z40_QaE6n$V_6Cnj4NXI2T9DzhP2X{%wr8{`HN`hHHM7yAC22e$L3WUc%RSLSN&97y zt)G)xxSnL07#)(Us_oNZNpg@qu&IS9q5D8ZM{jpn3hnHFarR&;#6cCZQJ3tXGkH@=y0f#)-V~|pR!p*%!cwLmK@*YBJZ_b zLhYu(I{}#GVTmjg8i_xnZ3v2}{BiB1KZp+XhxB6*<+Z~e0l+CxlxJ#6iEo#YGVmB_ z&kGr4@YyKJPCwKt-BhoD(%DC;kq4=fZoHR7TH&^NXJq4(I}_dH|Hbl1x6o4)O6hTY z^2zX6ckx*o#mj%jkNy*%d67iQ%wOo#19WN&9o|AyTWI8^mQ~c3Xb(n{iuwWp^h&5$ z?q)ZWnQm+|o$n?#3j-VP-TtPlZ4L}=kd0h7FG=!N{#Ea51*He!SSzKicu{Vsw8Lb% Te@ULMp!=1~>3ZybO#%E5px|Be diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/cookies.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/cookies.cpython-312.pyc deleted file mode 100644 index 68ecae03bc57d14d07bd7e4c512cb11b5fec066d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25264 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 RAbjru@1qX;_w6Fe{{tyznOy(? diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/exceptions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/exceptions.cpython-312.pyc deleted file mode 100644 index 7a9d350153c561c6cf2eb53ea9a2c6e187c7ad86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7573 zcmbVR+iw)t8K3d)+T+DHFkpkhK6b!niSZH~NN@sy7)XF{sWDCEs#?Z7$KHY6na#{B z##=W@Tci*vjYy@I^r=>r2U1iee?TAlSg9{dsRUi+p;Fa{yi`f-s8OE!`@XrbcI+lw zOY`lV-?{zfJKyD;*q(b=D7`!a~pg~dYD26hn7^=~bR|^eO zF;y{QMm!%cB&Hfwr9pW~F%mZwqfs{9i~0SgG)9|%YEp~PW=5NVCS|j1ycZ8zCm7!V zyrrgf1EZ}#+iK7xqwPRDWHK6g1EU*(ZmL0B80`eQxn_h`Mz;XnDwDD`V$sHU7w~SN z;g_^Cx((>|nlU;U?E$)@2HnW$PN2O~lbfQEI~nf-E>_^18Se)^uma!0_!Gc)t-!Z3 zz8mg)ji&_uNKa+a4d?o3~*Gkg$+!1*rD~q0KTW<38 zZ~i5dlR48B1=}d)rO4Wrr<;~5yqpxS^n^VlCW3*6#N&}hlAn@^rPONk%afBAPdbk6 ze5(3L`HWEOSz#= zB`ztcShXXV|77?X<;lsMbflQmU7=e-FHD=WCA;K4rU&*qsAsb!$B~AZ%bEDJoNgI; z)0!3YcF9Q!`~?~s;X;{NoOJO<{Q8mmeo2zOecD~vaG)^^EV@12@5CMlGWVI=$ySnE&)r4CrN~fx2GA5?ZWKxN0+?Dy6 zYFu|_-D=~tIr`*67Gn2hy(T$$dpVdrhB}ZaQG-rfV#`IPbJ!{PMO5-~J;NjD##V zqS`eezIs#nN9VRXWABZy(kP-Rl$oUWB&K803)hfosqEYafMRH|{| zw;*h7YCBM~GR`2zC{uAG2AP6KZENiy zah)gdLS3xsmJ7PcczDM;6ai;5Y? zF0kR$Bo=|ZRYaVT&UI<{QJj=;J{7NS8Mg~X-CLyr_6N2ghwL>Kb12C3yTi^*dlQV{ zy9IlH_o^Wx=coMTjzYYtEVn7my$`j)``X~5SJ6iA8V|J5PqglbTK|2me-SQx9};i4aR$G_%(@50aAocr6Mg*NYZlTC)CaE6_T%TcriD2aQaR*BYU;P|QnO_Ea(yC6f+gkRuDWM`tf2 zHN+qO=3oa(r!t&tV0IMUDN9DmMTQlwKEsjB!5(=2e|>lwU*?dwh$s5+Soq;oQ`d)Y zs8M|R7=5+RUKpmqVcnI;>AHnDt$RA6EC*X81>5szM^La!7N0c9j7HX8tfz=GiC6JN zq2-LfRD{>Z`0~Unq*N1uxxDwd;617UNLvcBMyj9{Dd@;QdT8hCaC@4#k@z8=D9AkR zb0fSiZXJ`heGbZ+kOo*FeU>|cfC=;mRz9{4lS9OWM5dO>PM-E$>H z-dzw>7q8Aso+I@F5~+)Wg5fK4loXov3G=~$^Ik-auL1MuX3X;`l{&7&lM=l{;tf2J z68+;C<8_fuPEextIrfa>|B5%*)@H@l$TsxtiCN3m?Udv^-uQO2`-Px$n2uDD3w5EbSZ?pH#y9H98$zF6bdew+zN->Y17i3d6Adate0Dd{{`Yt z;+M7jCw=~e*Tp})t8`CDkTtOA^Wm2q!*KxzbRhQMaU3V);;fT^*@QTh07)sLO-o4OLXr~Tdj4ZM-X+;!@4B<= z1apq$o?3}kCskEXI;8Rgs*|7ssr>BEBHh3Ku>+OO5>%w4Kj<$XPzh00>YLehFgfTQ z9ed~F&3p6Sym|B9o4+iY&a)TL`3y-YjwD8Mq?tHFTSweM zTQ<(pmWy+=b;g~vb;Vs0D{*OehL3v~8y-*>tgH)5oh)Z1GUJQO^L*+jCDm*6A2i4vUx2ltGb>}*;3QS2{jAfvt16NV}lUW zvI67nrM};FQ~+I3Jp>yl>gUx!wnf3He+L=|rTJW9SKJLqcGuwRuoY?(^^w)C_+B5A z(Kk@Eyfa1X*C}oi?Lb*)zJXi2h1Xb1-1%{^l|{+95^RK9{lfpT;*#U0eM0MudJr_~ zddE|E4`|%)NaK+Bb*6v{jO4lESU{pTc^`y0B^x=c3Q0jyQiiam7|VjOEDQ5_V_8!L zC6i6d85s=L01Fh7swCLf81s4sDDzlbRU}zh)NDdEnUN_TS;fW`v~zD?lrEFXVGLZ- zc>(8Cay4SYF%1jyg(MMRj{v-XWQ#Nw`_A6jn4l10(`mRqM1H!+M^VB&dPs=cxY0dA z)E0q2Ez0P+E&AzYMHkNH)D+#?nugEo!lJAjjm*e~=kxRVLxQ3T*(5fUR4$#wdkFh1 z))teC={)5vX|k?HK_*y6I0vGiuoMWr<5*KI*i%S%wU#oFY&s4m@PKg zCNW(^o}^*2dR{l%viWR&%b6+aIn8utk}Dd9Mp3OMg*DTM<+PmCp<0#GCbyy~M3|Os ztU6yG*VomFzY~O)v!lyeMjpjk*&r60S{^0Z8a=7#snOV~tgeoxmBmp@T(FCMqq<_q zBiUr?d~!+FN3r~~9Gs9oO5AcRn>YR2wu-~RryoB3pHLOi^G?(mt^~)*!Lhr+1I5`# z?%?I_4R`-$M65(6%8`k?kwaxZT=j%5AKdUns*&MJWM4V5Z`0?m_#$Orq~eQ~ebN6t zeCFo*y(K>U+>3y$Nhmi7m8Ow$)5z`6M$<%fXY~39*FLDWMQ;Tv;*qj=q$0jw7T+(m z&OBosyIRV={+bJUyDm>`cn01e*WnFMbQ}0lWC++j>gX?xPL-REmAGTiV7KVC(9;Y6 z8hU<8LyzZM2A^*ia3to6V;vbD!j+U)fkhqcGXdZn{*Wc4NY?>UPL?4i3p zZ|tb|BwljX!yW7%lBWi&_uhi#Hi$vf@+O|^yu=pxpK}GCa90`02`hU@Fe!Kp!6rl! zmt$V(3`VHnEI0~Wfi1WSZjggq7+Mq(gnSEV02!`243aLMBvA09rw-}Ue;U%KUDL8E zEhn)q8wqe?nPghIC}WbR%|V{FurOn~ zb2tsoX3TUGJV`dp_BRmtQaGPAv?ZL(F6T{7k<_Hg*WHlVZb=j&Vi6^p?zx32j3FwJ zW5Xh;i0F8jR6|e!hRvTPLUhi}Pt8Inadzs|!tuG;#O#@qKb$&cx|d{#?FPns2;m4J zWMQkAB$Q@roqt_JqStJ$$AAR&;DK#!m;!zFjxChX*LQ18_-uVxBZwXK^Pr2gH7dkH z@er-hF5XGx^S?S93f0N!lB~okxR)St)Lgy~-6{BV338qys&O8t_i<xBJFmDLh)@ zdSL3ZQ3?zeXCAt{s{?!P#ePtU&6Hy^rT&v8zVD&CrP>wwe7rJnusm?E6dJ$e0~g%U zRqg2oHl42G?7w)#`yYqGmC#T*G_=_-ULU?TT;o=TbH8q`u>g6gw}G}l7LRtJ zFS=TeitHD=8K_^29OUspBga|jG(K7k0;Oez81=SdY&|;a(SSVkZn1TrchgR-;W@{N z&Wqwaq=-g-B#RZ*N~XxGiy8%_CN!(ng%b;Nv$4}pAcBE+Fg-fF$P(veNLpNqDnVj8 zD{`ijt^f&%uBoO|%4M=TCT8_Vg!`bett|sjJN#o97va;1doH5Koad9l-wgd~=+o1` zJA2i*J$cW2@Ne8WB{TU%g3!S0E@-+F2}w&O5}25(b>;Vy9*GR}a#}Z?n7MV`50DLd%l;DQO`j(K*c3;kbDoAPZQYImR|CQGxXtl z1kb_*QLg?csA??3F!#~Om#FCh@;^Xc_|tyNKhQ{xW0~*+uD8asOyB|6UgLQt@Q@Rq z`H-^<#!p=WGtE3b#!N7-r>l&EnE(ste3ve6u-*4L{+GvUEaG;&sAX8hgNZRrxZ2&j z!F3X*;eP|J1r1bN+CQEqz(5_yGF{cyj^c5cDNa`dp^s;3Zh*bi$3W&kaFcE5IyV_c mw_KCM=uVq^vWLC1gCTX;J-M5`6Js}TH8WJVL+>{U=}O(AR-7YTE!MxPld9b?oN`iX?C5NRwEn9 z!9%J=BOclx;6drh-{D0?BrXbqf1sL^J^5x$1RuONZ@>5E&CK`Sz8f605s0&}SNvum z^h1C21b3azF>rQJfR<2zOLz$f#$&W(1SVLsW;vOMZ*9TuZ7`{1p&W*5(th-x*!G<= zmt;K*HY$unBHZKwlSGvn@o7X?d4($uKrs|4xX>*|(qcr`q+;s@+rG9*d4;Y*jWlrC zwpUgJlTlceY-Q!LO+fBI(H=%htjYvcEFeV@t`kbSl?!&#-%Ij)&(&)N)CBEJf!RR{ z>|dEi&yjC%&}GP}qZpOYOJh500a8bcU~%f*M-HBIaFV$(KYwdcJ8Y9xmDF64gL$_c zt}|CeOlcDQvg?Ndb8mC$y9LPGbSr$-jcTeKRxcJVOuCXQc0Qv18Z9yD_DF^*Kyo3f zC4&Jg(v6DpQbc6olp#>5JJvv7PoDjpb9N`1Gp)(3*^fErz2*FdMOqNvvd1i6zqKcQ z;N(C%4KO<>#t*QBS8-IwF}~Xy28i_}MtfaUXNXhkU$qzqcn`)&i!fst8l5qXmJb7W zmVsg`Hj0>|*nEYb;d&;{H1PINoT(ez!xoB-MdTO{r>41#sPfA&88q!U3AnF#Sf!#y z!YY6z4y2KYGoO*LNCK*8A?f!#pH?cK=NO4uVk*g&siaB?r!O(rSS`t`Y=A%Ih4br>GnSaDjGBZ diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/models.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/models.cpython-312.pyc deleted file mode 100644 index 7e308b59e0a91971b174adbaa840efea8cc07540..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35394 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 diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/packages.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/packages.cpython-312.pyc deleted file mode 100644 index 2b9f4d3b5f2757e08532a0ed2c2be3e8e6fc3992..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1127 zcmb7BOH30{6uoa|Ivx7~!p~~0F_NfFwWBn_51)lqFsYCT3r!PghM5N)I_;Es1Ej7X zY$|CYtbh;#%Q4;P%B_1hR6;D{(zsw(V8_ZC+a^Sa=v}*gp{Q^0gj z6=5+mWHcs5+0JLg4Jqn7d?g&i*H{ zm|KSp%Jx?1WuPE2fT{TfOii|)){MKYiO%;g)cm{u>fil;{^K6(Ie!LgSN_w!v0HK$ zsVZ)5_8T8-&SB;<5f)$~NdWEE8u6k;-pOmx$k zR;6hL6O*!zpL14{|J)~VsFB8d1s9NG4dhxw$M z#=Mrrx~9&^Vv-kC3G;(85&7^8&dl)EBz!ifC)G?Oe6N!yvX1Z6r{L>z@pUAe%^h<+ zReOD!&yukBL)xvgLL-E}gX<^wD=uj2I0P0Apt*rP-}SBgJHF6-|H8BU%M#Oez(P~& zQgkJ@9NTHUb_kSX0OjejyJ6|(V&Ce>=IGj}F>A;lri{Vez@u{4!%wM?sm~pyuKs-V zUF>aa`|@pL#CW>x>-_4895`%x<$|qMX#&Q*O}Cf5t>3(Xk~c8dzt`AuLdoHX)|756 z_O3=Zht`ISapTp87e>$Sl?P@2)y?j;?k!UChw{(MU622iD|sU2=C+m4a%gpYOWtX| Wn~$DRWt(!_N)-n%7qxb4KfeGdH4EDS diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/sessions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/sessions.cpython-312.pyc deleted file mode 100644 index 5e2cde3c566cde110759a34a52a1309bbd3a204f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27870 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 diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/status_codes.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/status_codes.cpython-312.pyc deleted file mode 100644 index 24578c8fb837e38c8b21e124fc85c9fc37f3ac46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6019 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 NNz+r`Yf^RP{{fr&EoT4# diff --git a/.venv/lib/python3.12/site-packages/requests/__pycache__/structures.cpython-312.pyc b/.venv/lib/python3.12/site-packages/requests/__pycache__/structures.cpython-312.pyc deleted file mode 100644 index c1baf875c93f6afbae3d36ba10168391ba260ccd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5605 zcmb_gU2GKB6`ni0vmWo-EQt*wBx!~iSTC4eN*oHop|~~_V&V{R(`r*RJD$0=2hZ+o z?#$xYF_l$W3YOABl(aUYRa~i3L8Map)YtZ@4}I}wX(20Kszz$_me`4!^3-$g{CI6I zG)jA=Jv%e^@BDoCyXW3Nx3f|wJD zU_^|l6)8k>(Snqdh#(5z6%1)sFk)1?74iD!V*G0pe3fZa_*I^4PRFNSiZl9JnL4hM zaa>l`-7=$2{6`y0Jl?OlnzGtRamO@QDcKX0DQAN*;-BE0X;MqP#0<))(QoSRCj`HV zwWb%fQpqfi);7IZcC}%P`tQ1@rzb`KWn~Z^<^?L`2pBCIBo{GSjmRv?MUCjJkdx5N zT+E1pY%;*OoNUM-n~i3WaU%{gVI)Ad7%d=MC&l!pT3esy&{IW+79GtwgtYgM^Lv023yd zpxZ@PGmD=4!?+3syW(b=$YO5$6$`C7i ze#YS~({VdNk2JQgnqY*^mGdhiegN_zZYL&yNA<&vSf6!C#py;jyN)18Ef7fKp4!JpjX zx&?zs9)(p|^nlF?W=y*X@W#G4B3}rIGN&uFc$h*P6J`Jil#2!(F^i}ex_nR%=mc2> zxfJ+S@VV4QTH@uF|359L;kRirBbve`A_~*Og{a@4?r)lm5L1{Ie@%W$h%hPKB!lS) zL&~kiRPLNw%(FiosYM-Xjntyx4XqX%pU~K-<6u)z@%~VD%r4L@D^V90ygrtN2-EC~ zrlV&w;ERh{%N)*@CV^s#&t$%NAPdYz_mwn#TpOiMHlXk9Dl=zFlQmgY&7$e5>eLer zK%WU}J3$Et#f)%o>yCE{m!oeb?k19#d+xSvx!iwOZo3@4l9)?e?XSok3n!{_#|pF+ zbksd4=o@@r`3{JAVVX>fBO>@LsIU8TKUt94l z0+dpdIKTt%vCUYKP*sPzNG@RSQ;qEn8)Tq|^8|<);nSF~tMkT|#U9_vzWGycpL%ED zLs?;uLW4EnLB;FM{2B&6216JGcIAz^Hx{0G@9?$5*N;?^sT-t{+_NO_d5B@pL4Szf*D&n)Aa2|mw!MKF28Ff% zu=s=x4q|Wg%8YQgW%HGRxq*eqd(GFHuO})kJ3aNoMGCFLxb(vdBuHfT0VX^Qdi_EG zLLM1?{RHURmE9l)1j2oEzF81{2$8x(E{W6R=VTr@;H^Xy{P5ez1o1E|z`_?Z`)jfe zo2-G@O%z~9p~gl8-*4_2rA64p+0m(NWBh{mn5t%aEL+zs=V&JEfMIjCfcU5I<&5xQ za-j0$frTF|zVWVHNe=wmL6H48`3On-ODg8gev@#>IcTX5Hlz$b$pa~a*B-|CFvK&$ zN6CQ?jNgy{Zv4){la+%fs|R1Wog7$_2ObioF6cNftWqz)t)8Nx7RxO^0Ove9fZQ=+ z(IV#rhGD2zyGd1{4i^67jUC4;kz1TcKmgArAHCW=f8^~W)#Ou4@>37d_5X*csmy82 zA<@0K3Lt6fQijVeY~bz~&mcL^Ax^x+XAJ-`f*gSR;UNUzrg{KQi-v%_G9vm=+ze18 z2t^1x_8(*lNFdT3@K=-hbco0p2mr(yI%PYd&D4QL-sJtfJhTB4d!e864G=yOhbnt| zZXEjHm8;tp4leGvcKG(@U6tgJ56SkeAtWX4lMR7Q)XmnI)$1C38|U^=`M2cIAEJLs z{4sIo*vpk;FISJ9yPX_bl7}`@A0`Bx)8B`OHK-(Rh{{|36KipU!x(IG0YWd4V$6*} zcV5m3E)auP0NJ>Qtt$b~1pVvUldsZ#95(A(+SdYhRQJ+L~YZc?S>~5YCzrr zXAFku0W|QPRI(xAXKS{an5xp(U=?j|6nC$#4@PiMz#>X)xk`WW{o9FM_u5l8q-uL+ z?)b-TJC<5@e4K2%+JC2gXQh2-wS9LrnR>tb1L@BG-pc;o>i)j!-u_Cme@X7&xa<(7 zJQye8VNEa^QpANY7|`4=>zx}oD#HEZ(Z?=my8&uZJbX=UYh*gsoQ6I=xb7vNUhMwO zkzXCT)A4MjO zK3=>K%m`Np*?U&_Oxz~PE8S6P=WnYsMr$q`VwZ&xxM`Vfh6{%XTp&pN+5s*c;NpSgnl1~k zlbj6cLQ^e%+P25bCA>?RdJH9;{xvqKSp0KvbI>@OL|^=j@U&S_L(Nv%XAV;7p8%%> z(8i;N7ZNyCpxbMbQ-&)C?s7j-W_>VFtyxudIMO?i8>69$GUK@xV>8vZs&aT1cm*nW zA^~Z{-S%y86llIDx6UI zH6^BwrU}<{J$^YL)@kS=HWz3J3O=%Zp&PT?g}sYsmy(@Ja;K+Z$lK61q!i4N9+cWP z$_uCA&MDx1f7#s>aS0%k0_-D@bZq0RFiv`kMGXs{umrq)}^vv$9sU+_k&^3s5z+sm=8>`)DTnmPssM-M z@RjFw$2_cJM!`^=!wTHqznm35igtVv6{W|QWg?}QV-czAUqA&}PXlZ`2rA&5wS;b4 z7Tk;BZJd8J=a&zzw^<@XdrZT*luxGMw~&YK%RL^|9c5>rg22z&2jbHRA>=brBGE54 j31sI#guS1~`UpuaWIh-0|MS!0W|F-#u#xaX#v?bX7PM$}%hfGF?UpTj zJv-8v4b9panrk-JWc}Td@Azhzy|Wqb-rZc=<4Go)o7`?%UdcV$TxR2B;(W;$8Q5<; z%-(E%|5K0d7LDz_$tDFlb^6pfr%s(Z=l}ly>%X(xEgX)ge zAuY#U;{-6=ZAQ9)(mObSv#a^$8==Pwv{3^~S~Lr(nG3kHATc+pT1%Nza0+YcqEN?}= zcBpoIS6Iif(9zb_&H~xaBlDwI_q^FxjFODP@N?KO>wnRZG}C_mr8aiT$}c#RwC5Gs+@ z3ROt=3e`yW35SJsukl0sg&O=iAgo7v(5o4nQQBwDg8Ma2eV$HX18Q~&wMe^#jYxZh zO-Osu-)8jJC)DBBA)y}WVW9!(5up+3W5O1sM}@6Oj|ojkj|6z=}Dmt>42~u>7dY#^pvmz>5~r`=gzEgJ|*lz&8LMU!Xp^fGs15C8WJ88_TbmE z!cn0E=hMPols+fyL;5XY|Jc9DJhPG$4yYr0-ut|8Frw$UArEqovfLS=6KzL?E~J9c zjnpgjARQHYk)9R$ke(9`A@xmZ8jnTZw20mpCcU9>sAV$j^M@=i8q3VX$X$L}6NCzIn7)LDz=b9&hRMm}vlAqrj*Pv9E~H)_(hanE`0 z@Hwvsg`u>0!Xt*f!;_-_b6St6t{?G=;n2HiH#~d+RnXb+aHB3=Hhj3Zv#0mu@ROa# zj`Va6o;W!$-1W?0Z`zgT!oc9kbm0T_)6Q^kIN%BUE_jDSVbK>j+h|A|k9#I2kWE|8 zctV&%Do*RqOohFnwB@WfjQP6cL*A9i4|@X_d}1&#?hS;~c3&XuJ?j;R!&4L95FWrG zJ<_>wcw*S^3xykvX&ZGiHGF1j!V?Om%d>yw^re4cG8p!zwHQU(I2n+BnJ_u5%d`c# z$pFh^eu_te!Sg=vm`5BQ6@%l$0?n^3?dbFQy~l%s_bHDUpl7ik30&~_eZpZj>>C#gH(R2zqzAWev8WW+lW#*>Z&1&k;n7c?scX+6f! zzI{Z7NVIS`1O9+FxVz(~WTrcSs_ zxx-kwlFOW<`Y0E6DZj@mm0afhLwbuiLj2`18oQ30(ro0yGQQ!sO`NFZxo1#%k-MaQ zhP%k)s!eLcap3|bH>!ECW@ACWNFDV>_6`I`!xsU&P9OJ;h`|tkoqh@nJ9sg4x_eR- zu~VOvfXV42*smhiuY1574r4in($>dmd-eus7o_d%#|eNQ5n%E@zc*ss=MRo}{0Hvy zErSuw>6SFVC8BBBa-TogSdcbg>5AU7Y3q^kiJ%x}8z^l&G2smy^-ci>9qoN)xck(} zlfC_e!>0y%Po}ki7irrQlU{M^NsoWh+j|Ms2c&iusf0FRS|6GKcupHeeSVBLZ4|u| ze$R+Etr5Lx{kSJQaxTpu0sPXP3j$nh!z`qo1d8a`~ zj{7HC&jrW5t>OgsIB>+sxmN5~Z|iYiXr#5}0$^UN-*={UVk&$t7-(;4+ujP;;BB7p zjGXtJ^@ds%9MsBiP|L(rx|H2e27AMpB|t-QO6o-_>i~1qoaAJquS=6CYiwX-n3$CAN2ca6DCWGBGfi ztT~mcd~){a?dsa4>egg+Yhv4@AGD^b*|q9Zv&X+MaL%glH!l~LuUfc@x~1~1$?~lW z-Kp|zv-Z!*YvSSg=ThbEv-aC18}4u>bMG+Q>o&usPWL>gQ?;zvpp-0!Yh~NF2!|m;hnA@^nAbP6G!8xWmT(%=zq0{bC!O2 zubaoP?r^-h;!})Fi}Ua9m2-vVH!2g3O*!TFLmM$&uj;xRxbJj2x*y?w)?n`5q5WAq zPw69$9<%mi6OS|pg3`WY5VQ-KYg|}`t_sy9TEp_*R{aMCZh&v@Muj`IpF7K^m3-0tpk-jVYmB0MflxkK`% zbD#B~HOK-tAurZb7FK1whbFuuzEPi7Xi~cZwL$k(mQss)p;0Rq;W0%5MQnjJ^5OyH zL-f>cv73qsWJ(ELa3h)F?%6n7?V@$#O7Yf(o>XyL!q)a#al=AMs<*Zco51?Ojk&AtJZ}BVIQl zbWlGYamqb8A$Y>xmb4x_Zaf5RYdq%(dBR~)JdCb^V|gL#Nz+bhIt*e*=}SC{YA@k0 zRD%SMZQx9f*%xB(ya6;c0~D^-K_OSrA_7x zYD=Xhc$%xPx&x2vdHRTIF7YAIE7%zFNn?R1<+5K^fTq2MR!? zB*vhI6Un4f*DC`abk$s8wjTPGakZY3Y+A{#&io_;Nfv!0ML!V75Jhs2#{tp zI~zX7cqXt`A%HQ)wSkuc8NzCw@v-e5@b_I5A@LyzT~Hi?@YwaKXtiYg2!w-aYE~oMrVeCaTuh;+8l=&y?E_?!X-m zN|0zI2#RSWyk_7n?jsp?(B0m$t;rploFJ+Pck=rQ`itK2;01!|0Msy-qiw{p93VQ(YL|XW117i!k;t~81Pf$W^Mp_dLrS+qz7)~2Q;bEGh zw2q!4Z35j#oK7gMqnt>KBdzfT(DfsNvWXUVF3K2peM8{A}+Kl^w6+{zJ zz=iMNFEokOF~fmim^rpwR-Vw6d}b_+ZFqBF(O5yC_wd}|*i$Lnx>^1+V*x;NFo=5?!@GIP;#sq5O)SD$`kXtwWO0asn~ z_U`Mu=lfDsTbHWZlU41r$5u_8vpBxt?YirANr!vIS$@ra)xN6JS8PZ9(oKI?Y$+@P z-NDd79mce(DK&3iUhjVU!u1PpT_U;*mA3-EMfY`*ssAa4wa~CBiWX5WVn)9K6vGq#b%sFaN#wh5BeV?|P zISmIERb{!y@}RM(RcSSCi`vBEs7-1uLL3m_1=F-WYKz*bKU^`>c~mnXF|jJ@2b$3l z6AK16i;a!y!nMli7^1?QMR}2bbuGKAqN{LSPJL}IW87$o>{Td6!YeAy2~SP{BX}ld zt3)MJ0|;K}a(9NLcPx_12Cr8^(R16%{Pe-4ro&@dS6TihRM zzQBHkeNxM zqG1|bi^NEW4z#FMQfL?=D(HXZzMSE1Rqk2f<0t3&c<9FNWaXaUGVuDUqY!cr)WQ86 z{Um^ToP>rF%dBV5c9#f@j)jGz|A0Io3NM zlI&dcQzhL5IFhslBliRZh&RQw2`s#H7lZc_c-N3fREc zQ$UXjbnhGSgo9#;VHL5B+6*Is$UuwtlD3H+9|WIX;`JeDWKS_nC^PMl@+5O0G!tT@ zW8+k+(LhCrhk&e53lafe+Qg&1Hu{5SeSu+Li0D_KsH{W6Rt34QNBlk1 z`?vTD0onp(fk`MTxmJ0#@>bG&)Z@vF)`?>)O%xo6g~ zs%zHU|GjD3?b3=_%k4t<9nNZY&33M84EC}WSNXNGSI;iF8j`Mtg~F6;YfKB&<1D&z zaqi-km*!rIk9^{&U0&yoSw3}^u9l(YYBgtbC9La~ZAG8iOJgIiKR2rZ5-BO4HLY0j z`$qesb^UVL`fD#=eR=+3s;qVP@G=3A6Vs?F6S{d(1 zmDVkl?o5{MyeXtg_s;fx>L|Uve)GKNpG{rgu;7Uqmmvwbc-^t!y2-y=ktl0VINMi^ zoYVE?y>_mo8VJc=cE`jOmH{Ez%YL`o!4qtPMb0di$p)8<3XXen0d)X80$Lo<{CN-TEHA=EIHak^e|*q}1AJ!|7+`TYE~m zkM-uBBK^k&8kBro$Wy*ZOZn1*o;vNv8+poa<|$vt${QU$d$k{T@JMsExQf62Iqtlc zX9YV(AOOcS2ywA1OC-RDb<&j#X)d-!p=u)-DCo{Vf+gL6-+K1jz<#gK;In)zSw5C4 zs$xfis?xtkV`ST)2(denq-UYpAmxOYk(2??xJnyKU@mxO48$skOS&YJ?EJpZ{{hE+ z{s4*ivCbK@%yurJMg|td5OQe~iT$Z1poZSJ~}~? zAuRkQ$NHpWJsLSSCrq1HwOlVh$lt6r5LxN?rX)xPEkimD z;ltPTpz`I)eX=D)rvZzn!7E|8Onps1d5EFR>kbWlP3wH4P#23l3T;#y;sZMceoU3_ zXN_4K)tt@98)(Gtues+}kC?wa6t*h;=Oetbo=%$QOznJ91odek^M8oGK;h#__Rrp& zN}P?AC?m<7mDq=r8_~~ zMvY^1n>7ctDR~IRsQPvlA`Yaomi{TD1j%I>V1FCAHpl}nLLxzgnp7@n92#|zk55YS zb$1{L;swYa7~D;WR#uDpp=@*eedoOdh^D~kj(V|{z%RSUz2j$)2fOZZM|=|&LbD!m zYI2iK)gSV*p2j92UKJ&|D}g{r zQAvF(u(7$Jx;^I!oF#E9-S$4;DiY7x-&l}=aN-4&NTi8wEa(fQjTj}PG{AZfD|zFP zZ=#RL4->&S(p#oYkTuE`F)aEa_{6joRL!_=WSGPbP~3@-NTzinqz8>A37{I~hff=% znMoTaFk8M$X%p@xkvQ7$_(ZxuorGaVdD&=8!+0FQ4^0ip^XQT2kf@asxjqef!Yi;w zA#on{Yy^yZC7Q*;nJnMA_{zoay!6UTF=5eIb=y%qYrSXU$|{Hoxi)@ve7+~;YJBCy z%%RyQKeZIzsC~Qsdi~oi*IO3K7AxCUDy!M==IhN1##H6DImfIq<^r*>YT)ce%Y`M^ z%CDBkcg^ogRBlNXZk;uJVdtzx*`0h+*>=y#*@~B}>yp-W@w1E8I_MK-t)Dquj5@iP za@5ZpUN+iePcN0$B}?m4r431A1FHj)%UZl-S(mh|OIg;>bgz__U3=>4Q`b&kJw3m1 z{=yHU-;dtxPSx*zzbIM1FIBpKrhmn1zw*f3BUcX09f-FqS{pyJm&a>T_J&oCH}6_F zwrne3vehJQHStStPhX#2crvwq$D(cLZ=i12b=z8z&{fPHK=K>C`ul!}_{pEQZs@Dl z{@2y|zB*$LF+rGUBmV9_hWVEGg^V0HA2!Mm5&879d7-u{^LNXL2P^@0_@0)ym$fh;+wch+Bkk5aBF-bv-i_R)S(Rxaiu^zR_vgP_Abyoy_ZNUPZBYMB({Up%S88P z^$V~?hhZi%BKju4#sC=LH@jk`oU%=cLR7b?_&xk$3GA4-(C_0HW9zW0-3?3YTa)Ws zf712SLq9(B(-S{F@xkHLj^m5#`;)rrnf};eunPKu6}#igGjq?xUYZZ5>`kXt}e)gochik+7C->DBsSb_`@Cg&i%#@_ZU&~5$Eo5Yd>;pQJ%B( z2{mf*cbCu}vB{8p@R`M*tpv#|`V2iBR#g!dn5u zg$*q0P1A+UyeRt}ihqKu2WXFzh!raQO`OR-GYu3*vhB?`Hr|+gM|iJ0QL$~&*tSwo zo~Y~@cV^C-Ufa#XR`frr zlmFj$ZO#s(NtL#i_+Mag0IjN`KDK4VAE7jB#}MupNdceMQB(0Ive_HQv1ZQ1KShOS zXxlu747N?}H{CXDSCuUqH$cX8&3e`PhJ9B5nbi@~zrHu2b3M3~RCz%RH@e211;3t+ zsD3Cff2b37f|rL<0{UJ}C5JO?u`9U|%_R-9I@3eU(}0I841jA26l4j9Gy>{Ys+1Z@ z1M+G6vnig-k3e0(1C8lD8UPz6#AOQ^c!_kTL~EP@o;ow-KH1mp-o9Ui+@XzvjPcgG0OY^dXTn(F6e zvVgFoG(0Mm$JbCF4xaZ0n%uSW46@#8WnBKObd?B(?927fNrc8=&^-e7lTpr+RdL3m zC}#w2&sn^*CbC?nRl(*!o-hGc%j#Sy_R#V}cF?LCce5dNu*V}hL*nc`(lU~6E0GW# z(u8#6KD~4^8IjYIvx0s}Q$Za2eZIC)n=S%VOXkp7bCWKS?SSR+5gj!Na-QB?K!DlP zkI|Ya_z>P(7CRRARI~I<2#k*rui`M%2?oX`sE5-E-x`ihe#05*LxE-c&rh+T>IeSd_#8kCv zL|aJeZACN3zXB@#m$>gW&i8Pr1fV(%m=x7O>SYiBTcVnMXmi;P2^Brvngh^FmG>}C zQA;LP5`@B8N3bmmrs;EFn#xa-4U(YCf@$ajch<5yKPJO2gXdcy!5^pB#9lrD6*ur} zle9vxxP9U`foHVI*dhQhfnf(T(ijIY0uk?VOE#9w04!_Y2=4vz-eKll(yD8y9}Ncg z*Rq{A>Ie>C3aDm;MO}#?kQwb}$ z&+pFy4dPF++Og5!$BBvY#6P0~9ozZhzoyc^pyb;~)|jKnd!IU%!H4((YF(qfKaC9b ze$$%0Z`7}&yRLRcS-sQ6DS;K7jyS7ya`d@`~Oob2F zOzNRSj&}E5k|PiUAogKTGE02g%_J1g+QnM7i+2XJkmN$6q3m<)d|rgDJo$Z9a8CH6}kgx7fa;VrzFP=!gVsjVK5oUyx}AnX@y|K37{b3 z7KXEOfcNu_5~^UfS3(d7ml#PIEdimTxdihG&%jQ}eUbcf&WNyON9T-Gfb4jZr~|fK zB=a%aVRWwym5__Mi0H{4E{O|d)S_T6iO&T2!n$QDcP36|E0E!;ex_E35&Y@<{ArwO zVP;DbrACc}hpdn&DDmUFF>GrYJU6mXBG>a7d9dx z^>5+KvDIR(&=vELBvdOs z8`0z8pgesNk(V_w9qt|i;SzF}-XBv&%Uv);tqv*o9jL|9Hmb%$;%)pKC-J{QlD#j| zr5-_GFM_PKrQ6e-{}t-}k2L2bXMiOJAm;k`iwj*TYimN+`rxuwXY^ZWls%*QmaULS zu@A{LiC@?;W>RvQv!KZcGV{sZ-&5%?aGGD;lkOY$lx4Y`cTD;p;%Vt%{#mNMwp2!G zMmPhy@hh)KsJ8a&>tIzHB7S96Dh_PYPL!>#WNTlU4P3J#B}?EA@VF$hWt>FTzRvAW zT2+yE!1F4;HImgWneC`mCFeiorV42#^O7y@KA?s1AW>rhUgW!R{>q-2b#2`b< zFfth;S4%hk`$C~fuY2S6?YnlhxEmOjk}Yf%KaXI57*mON48m}?r7^N$-#CmcTDBa7 zLK3ubc>lAtr|X~F($KQ?U?XJ@JO_=1o-B||3G+5kA7mN@TOhCaQ?q|P7$??(ehuQ-dn1y!Q zB99)skN#WDztjD_I&S`t-97M~bcdg4^_iBpTtCufe3 zeq?BFC{cbeWq)+0Z`Ht=i9qsgjmikhN2m?o&e4b9|3d0xgS2Ei!8l!I(U8_kh_u7C<s#Yh9?ZCs6qzG@RRz6XoJYb2W~?c9s8P`h-B#(u)Ul3NN5G= z?h=w}!lOt8ABhh8Bih<}xOtK5I->x=Z7|zLJ;AJrw2rn;TJMKbg4k$D8>Ato4NQy^ zO55a7Fqdek|LI=vvXssY+ocN_i^*J+SdV&kF|7r=CsC3P+`)TkI5;{CCkc3*he+AW zhRza6woxg|OS12$FgkyW`Wg^zp`RfGvc<_2lw3JIcRIc?Wv`j(TdvwLzv23xw+~)F zn5t@87`^$#&65jbu{s#(7nRH$T`sMf>AzhF>vEI6c~+k#c1>HI%M~^Ax>)IK_j1j~ zx5uuJEfl{MoISDZsF|<5<=C|Bs!CKh#fKBF<|S8q($#*mKjrF}6z#md(q;VpuTi;**5mWYFfzKNis#)S!6)3dvl%rJwP$(1RKz z(`+`A6vaRfnCBs4!K5r{VyMtXC|!!T4mAR3E@&A%eyLkgyoecU)Td2K-)SCh-10?) zxi~i&I1k(H3n9Y2SwO7@s~Dq$@db5HWXA-oz!4)O;G+WZ9;)x4h4dOS%)Vk3-kj#| zL|%y`;8IjvwPdPFknuyo?SfsGN0$n=Bn!4IwEd{#-HuejuJ?V51&3!2Ef>OELaQ$Y zXtS4G`PSUG;=L(*?W`tkwFCaWewYNgr4Zy=3a`}6)x@>)J7#NA=7yQBPmSi~0_W={ zSge{X;Ly$0_nH0vt3{pr_&52^4&8s+sYUj~idv)(uzhR^{wEl?v;+laM*0e%h`^u6 zf`Ci1yhyT%cjB+}m7m8fi0dJwg#s(e6CSVRq-DZ>gWxCC)xhvIKB z$X|fTRm%k>v4ME| ze08#_IZ@DZ%ha-Lvd%`6rm9&jw!F1Ks`-hf>bJ~*>(x%KQ_p{>)gaBWVo@nQ(%=8b zv5HKcsM0~eJ9!w!tj(?w%V4%;#RIbg^;M0#Qiiv!nr#h`v+LK^W&i~siVo&5nr(Q`53qvjki4^mVas&>d1FGRxqb~Bd3Z5hJn#`p^1F4W< z%=`qJLV54z;yJMc9z4?K&XVx}mMGQLZb%{{w;hC4ydWzD+FIN)Vv}Sd@?9Cyvk*kt z-U4$mn7goHkXBC)BH^Y&c}%PevPA_c$Qmi)Xcd|Mab5;gZZ07?1xTr){r|7|PX@Kc z&#@R9Eg4o&{O?g966O{E7fQ-0NgzqvPD#*|x%Y^FPDSQH&b zkodo&G3mLG0<@s4a5P4-4Gbdod=4@Oj{87cb6)8vi%;8@d z9TXU&d8ODDd*RyD)v5Ug1fEFPwys*V`id1tQLOFSo~wJ}=N6_G9eWa{Jzw6l!uN#Y zMl>(ms+MfENn7pw?!UG*QD}+g-%+50Qu0KpSavwcQ|6Y5f==iwq~H>NTH3jZ`_NX_ zrGv+0Wf!mgh~p{Mm3CEYKdR!9M!Gt?x_f&24jn%7*wJIh`%gUn#L0odQ%^qi^fN=A zGb4g`^z1p`*m?hWAUN@Y7z$5bxOizQvaM}<`;MKv9@)L8xn=mJ97seEgm&=V-~Wa@ zXNCZQIEvsHJ2EEs3i1bW;b*WPc!Q`|g$vDUqY@*sfx{69e^iJjcZ`$5*@=4qlT?%W zEa;>a)SvR7Ro`OqCpADpjTVq~rik<0NN0wfxLO$^$x1FpsHD z0Lp!+M`Z*I9;z@F@C;$_!m2$JLKA4;f@0*4)H!$+GHclo-V#|j6=;SjhKI4KQ*O93 zli8e7qy&^;#ta_!sgp;b>{FtKArKDatFRAXaD+gU1XpA?bI6=75VZx^qf|tk55M1g z78(zNPGm#TInaINh$J49?d}w7gSBw!KEGMgpJR`4OfT^{&3dF(4si9bp~DF8K40IT zCP*PMwIZ3&Gqj#I`k=iDOpd3uBj>~{Qy({aP8Xc&KiP{IK2P@c3=i}UidRqw0uTOt zzBDng;ZU0QrFjJA8Cgrz6)9T$Np+G$1SJ9yS{FgU$((@91i18#9o*^KFhC=FZJuYVo!W`Z@lg8J=gc#*bAUx zKgO?=l-qbj!(`%asAQ)zo3anlT}+|`k3brO}E$8 zNagXd_yM`7qB6d7zVQ0)_?Fm9v%Rqm-#xrqg;9`prhshDV>@3b+wm{&6>}vu04;Q1 zmpies_XE!d1Ha%C8y-u!jwYN(0cLPXvMc|vu(YdQ^HF_8H>b}51}a%HfyqF$NJMOa z%d-aCP9+F4+doUL%V#9dY}u=viM;I9)OO$T-#h7*?01;OkmOi~)vaJ%3fG_* zhCp}{LZrCdiuff4C(oWEW+410*U`!}FtReX`p4I#6j6dWQ+H`{FL z&j>lJM#@22%1TJ>4%wzI@_#bbfVwLH*P)?yPX#AQwLC&WpU4&+`{BsrFe>*45ut;w zc-(MsCZjb(4>^OZM0NIF@{B_((E-#zF{VN+$P}|>pMcp*+n(($ZM$~2w6(NJm;yv{ zcImd(?K{~1Vth0uN;iFw9ZELw-=Lpu+geyptSbfIkeM_qh-mg7lVqgCz#^<8ae$K} zh|xwt8As0JcW~r99L&v#{1P69I3XY*m;xaP_bGuq;m19p^9-$^xdxI{Ey~gc-vrcv z5Dwr6%jnMdfJ^8n)n^%IRLk7jSX!#Yf~F`rtG_JzL2!~n#I2#SEJ$u?!p zx7RXMUcQ|U&43!GOW99Y&HBhI-I5^|0;OWkC5Ec?(j^LI1Fab?+)I}(wPgLRS+`i~ ztRX=9_8l2qp{&F0J9oAyf7|jiT7Cj&|M2oZysSllU1UFZBZ+7Z9D(%Rg_kO?D=4i! zA~;+Yv0&sp+?I80Rjor&wr*KOFk`ShNdAmg1I%EE$S8nkeY`M!W`4(Y-}Rb>wq$uz z(%LlBvtlh=vQ{OnRX6(HK6?EqEc}F2b$jX|FN?4&1HS#pP;}0(20 z*zX|vV}=CPMqK&#U*8~b7EJ^>T1nA*fZfcJ7-rZvkz;xdLlob{g&o841`!d7nfsXL zp+%Yp6XaVfrh+9?dD2uKuUIr~gcQx@{PLb2u6x8Mm450dAqJ^b!b7i?mUD6QChZ^d zn;zm_PhN;$FNGH*!|IcmGMYekK-QpQ2c--vUZv+HIIpZ$N%uywuMo-2bmx088T=Gt z#$-5@d@A~{y$kc>SMGurWgy45jItgCmGr8#3b%l(*<8k&tMK7}lD)_$5$e(%@)3BK z(E`M3Brh592?!GNP^M&JdH=%ZMiYp5R;5Q_wV*%s*&YiTK9F1SHJR6T68 z17V45C(>SWY+18pH0c6)$tVzWGy2HhIf&^SB4b0JFib-fn{fT#(R|-PpNvp9xt5Go zNl5IvQ%3iE=Pl!A7R8d7^_P~)wkFFczGT_9n}x};_EhnX*&ecqb%KN@YuYn^I7P82 zbqIU8Vk=s*RRcK2zqPckExE4k=El@Ig3IgnEZRB{)yU?App~LUmT}HXV(znoMLJpZ zqpYI)p;`>^d+RzIxPMdM`3U!6gPHu?+IdPJF?VUTA8B}`IZ|0#c^mO}cQ>9=Q49$D zBuQp9;`T5uNk=|dn{VA_HanSRe0BRMM>3*Y z$fz<7VnfJsw#h97$rce|zt8pq){=}`By`LaVUp@YdJ0KLYiEx!3`U<^(9`CiUyy8c z*BDAz$rV8!lt`|jzoyOf2k2N*UpTqa%K4H+$>y2kpBhV6N?g|-yZYF*{;U1-yHh1i zv*zXXn=Wf4$IqmxdR1f8xBb#&|CP0T*-@TAOf#uL{8F-{;dcGDH#KBC5#N2wxoOo5 zx6m)=_J5;#e;=yGh+f^(RmuN_u5&B*A-5IT54SdTm1#dJ(;}a1!}=O& z8xYqEAr4K5ItA)Qfu-3LOV;jyoG9O(f>$YM6!B9*{~AfL^I-;PTNPS8pVSIQt(asw zvSNzy8SIe%zDgT$f0Ty=4fFznNMz*jdi8omHjk3;31BsWIiUlpI{;TaPu?euGDCdS zd4%fdxWI7!df-8H`GRnYMX>ZR=f6g>v{}~eS>B=K;`BqN8)t=cg2q)SMN#=h<5BYY9BHLAFegNLD{UEh- zp;Niud3a0PqZY1 z$joqeigGQ#8g@lSN5WY4X>oOYC{^5mNM=%Gvzb2FaeU|HS6)sSYY?jhdLIN6 zD;ePDpI+L$Gr4)^&5`%Fr#2s0+T4@e-1EUeYV(oB;*+zc&#X`qll9V~b?b69;)NUS z?wDz%*gapqSiEJWtZ||4N3HL+zQ6mEvYt=ZZ-hP5wc)G73#L@*HuPWZ{=$I<&^^^` zdVBc#@N7R!Os>56-4~agHS?YGqdz$R{qytBCBUsa+hYZ{%Ms8`hyHcT#TC~MUOl)} zyfsMyvloloZhmWN=dt9@W4{>uWamlNGX+RP^C*}mTxQ4YI)QvobrZd<{7*}1~X8|shA1#jeI2j5$){kX8SXS?R(?Nz-_{m<8j!$UC)^FV^=~8*}hB!6V|l?(RU6 zEjJ`*F9aNsF_#F7MTE(sn(R=Z1!M)9NM{zbbbFYc5}68Q(_IP-$Yu3a^Za*twm7l< zXvv6V=8e3%XmhxcVges>-wfaYwuoe>*+ILm3QZy9v2mt#@qvZipBQ%_6yB`)Ge^bi zQJ8^9f)@Fq*psM)OAERH!^%rhnjhwkM{GboP(8CXKXHQEbcZ!Z{<=VO7y}*3w8P6L zZ+pp1Gyh0v)*jLdQMK}oomvQ*v$|<`JHt|lOi~Mo+(D>Nn6m+9nxKYb?C>n&?IPKA_NTQF%SN;v zt*sFQaySFSRpRl*eYEb#zzt$c)@h@Gt-rKUzIj^y@C+o87S=6=5kL%PmM80I`lJmN z(d*8JjkXY*A_+H&PvAa%v{1=;o-xl3*LTO`MUWmk%HrDBrzsYaW64;aG?pif?qx(t zdU5W>_-=x23x|Gm^xdQH>r+h!QjUWO(?Q5+%!OARbB_4tq-p(p!xA7A{+rsDO-?Gt zDYkp5cvBMpO`9nGi&`bJYMIQmHUmR4lJT+Nl0Lm68l~Wzp?9S6IlkJlgMv**j-A8)e&0!#bL%@iA4) zoS6aiqK2!-W zF~AfZ?k*@TyimQsh!BAiXeJbz9#6ySxHPbv^C(ps35ttV=dM@ zfhzgPF^Bx-m6jq!*?69yP7DhXP{+kpZb(!#&7Hkj3ylP<8n@1EMR+xd1Y5FiNZL0* z5hBB$j+CPgP^qZmTGQ30H(F+n{>tcFtY}#Xz5nzl6@4p}%?l&%A6~5Nops!Hu3vI) zOgcBt_br@QYC4!~I`{#gRbR?^XjcC#Sc==qWJ{b~3p*EWZIW-Cgp)Bs^&k*Ke|O7~ zkPsOL8d;(&8q3S>hYDD(1K`V+mc}0a<@H9}vL9ZTa>4sa=n(vme zHWBTX)-4gsd9Qb(8G_vlUi4-;h39JSr0Iz`I@veMntSDMl%)+)LE6$C3 z;ReC*`p+txQk5+$>vzF-V7apH3$xyAxvMqW467WRgdSWj>WqGj&ZO`zAY@=xozDXx zWAjKs5kkOM*>r6_OoV_7DnSO)=(`||pk084R6bc0JbV-^z;xk=Ru2m!UA~cIymfp$ zaFsrPe?OEr2JVanL$U7Rb(8PTs+5b}*F;a3@2;8WhX&j9)ip?Be0d*;y}Wof6GtB5 z&@WRhW^$sEyU>u(r$k^AHjTLbki2|5BYDZuqsX!1FYi#U!(-wv)Ll){KL~LFYtVNX z{l36?*>hXMkR)1=*b4TQk7Kyr;oe6d-s@;ZB(qjni(*~O9L4JTL)w1-0gV8_oSfY;(|fz5a_0DLTj7pV=m}< z;IkfL2Ak!nvMu0aXLYy1e6t9ih(S)Dt2oTh_L7~JYk6Jm+s|HqcA+@6uKB7Zrj4CK z`Ia_S-6I12t28?KR z6QftZytiIf6Vc$}V$Aj3ZepHme+Lg@$wteSMvQeBWVs}}&!3cb9n^f-R?=Oh`{xqH3wXEi+1`#dKGX_^O-e2%H(a>P$M6j0&a%M2DF@B#}ZMOwX@BkL-x3)UX;o#D@v&mA}n zMo~HxI7JLHJP)N2n2|TIyqk?2AATkbS%C>LEB{l2!!n*SRK)tlB>%{TlQ-gi&+lPtptE*Ssf&r%W!+}YBItX7L#c8{IZigF&4qry|fQMz~LuoTzW?JA}do?+R zJ2`g^<59&6=<_cL419ty0t^&z1x2yiSljH=AOnr|H|v&)HYbZVzjI-!z9U)Rktp1o zH11t?l;PU!nb{X+B5yuP^0ZsVP0NnTOyTH~t1;@IyO+?bP>>$)>|H5_Ve4r$Jr7V;6XQ z+AwW|?=9AJJ|W7qC29%5BQ|P5EeqVOiV=i851t19XUPh}Zi#AVBt4lCp90fN8#Cbo zqS~k>Y6SIRike5&kl>*Fw2^;th<(w7ERK(X#Dzf)g)aklMJEcM!s4uXWT6Ng+<`kF zD`wQNWXvfEIV7L@9wq3CjA7g)&0!R@+yVR;@GTW0rJ)ujzE6s|4XzM875K?X^7K+z zzct30_xVQ|=7XSxW<$J037HCuB&ut)G8{&q^0+uGjY;gpVib?kD%a7sL!?i5?5DyI zTBP-0oWv=_<|Ti0+?Vwjvf`2?bz!MQZQ|3y?78Khhkc_k3-cmexHygO{)QI(i)hE> zg#~4?(CgpAa^JNtscWC<1G3pf->aPOUDyqIiDWc3)Ay;haJj51{$xxyYxtGXx{8m2 zl(HDuJLX;S7T~2>1J?h{OUs2-l(Wu6N&cw&4BzRv5Wg@VUO0KP^nFvp)tRz(C3Ia6 z60-!1*s}c3k$K3n#8-ba%mM&2wOMm0;5esV9#o;!8-;Z-g>8XaO+^u56&)y_>mjoG z8AunioPIQs7nQ|GvOKm@vOf7jK@=#?02U5H4k9wA6zZK8lN#Ska@&^uv5C7P-A5Cd z6~lsWt-X5#;C2)Kq-b1_5|aW&T44rBlt;b;kk-llK&v5l#6CD9l70lNO9UrrrIA@@ zE_5s;2{S!|&uZOa}Y)%KjG;?&-h@baEBrE!ZEqk~> zE9@-LerV^B=1>;2d)UJ0LWY=PM(5*&WQ@&7W)Jxo)xrVYAVw`6WbO^|vU+7o3y$vx zFh!y-ijwU%K#0VW5uFX;sSpg*Q)n0|v?F~EPm-FzdoKj*PAN(n0SR(&$a0!u;rb;j zzpPRPHlS}fno2k7kfOChOpH(U0kp`LPYvwlONJkEWGD&k(&D8LsFzB%NC>&wkIa2ypBGoWa&)F=cJqp^B3?JYX@OtV zZISYu7YaYoZIw2xJdbQ{X_K(`LMtn0!}4Jj-zYqxXvy&Ok~a>%eD+Azd*U;kQC1su zOR8^H^EnS zz_VQkaST)fZ)yHWl}EhFPk~?QkJt}OZtV0*+zk<9qa?D7Y(!8^uyF{%O(IAN`hYD= z2#ZC2X<|4_dd=U)T^QdJ8B{!19$bAC`giTpYX~6_A9`H7_)tzuOkUj)O@|v2JKDyQ z{~JA+6wa1jq)6XUgHJq0C=&Mz_KcQQ+A1}t01M%?Oa8%@5xGgj-Wbst(q^?lt$!X@ z);Pb2zlTM~l2TfE^XMMxZ-lhndLy)G+_+M*C9$yZl;ZlMtGM%7UkFgKK0U5~=4j8;s;L{q~MZ83>iU5xk_>E8v z08J$d5Mz{mV@#rAj1oQ}qD3Nbh*Dtz5yTg%EQ%yVVk((Lk;oG}Fbm{i7$2i^{s5I4 zXi2CzX6OF*2maOZq^=F*NMEc60BQ3=Ikd{QB9#WYZ7*il?CtYBJl5y8C+&4JeHm)w z$(yI%?@G81rmT-9bdNsx9#y155gl^hV?H96y&?I{5lj0@OV<10@@YCV)P(Y8#S-=z zLQ)WY1w8^2B27j?1ECrwhmKcyP$(*00udAJjI-*gMrK(~eEP`fad@#yACj2BCtu*) zEBOFJ_z%wlK#UR489-x*)r(-tkQyr=HD&86M71dRJ%KO8BEY8uD0D8xhJqf$N1uk3 z=xFxY)c(lcPJGdZK4j+(jpl|{rkLJS?I*JJRDf)KCG`!} z|BA{8Vn2r|q0sfh6a#7Lf?>*MKFUg_M$#wuHXsM%cqfs>q<=vq1*U;Wn^_C`25pX- z9WG6Dd)A-$24;T`ElrXYFez57_uQFaS^t7 zfFVQ~d%_|KQADEuBt%5!57PJB+3PEzpYKyrN(ouLNzQAe9TADzmFQmLrzmz83x+7J zqhI)Zqx@Bq5c>`=LvNy+n$UWHQcBeoNWy%PO7$#Oc-p1N^t(HUyH54@9P1r^ymRny z+OCv8e&XcdfW*t~rY?tR7EKd=Pnc*qv4_fzQX)|Ddz4(JWFASnKz=3To&Y4UqV%zp zKcUNiO37bR@&P4(M#(QI`725mDfwGUeoe_eN`6ZTIhd!-5bj3cRfx}GLiQ~C_mM*$ zGnjBg~q_^?m9WX;FnzCFFCXF-~LMu|1H1d z9PEF=-*6j$&29Uix#GX!HvPt+=QVfn?LEzJY(^v;lD|V29lz0-=qHjdDEYf0j&J;p zMydRFHjZz)T~Iq?y>Uix;`^{ThlB{2zPEi-nx8HqCvkQ5V*YS0C3TpYv)yH^FV_V`YlYHeD8Z%#TSIcQYiXeBVyqvGO zv#XLXyR)yFFS_$6zn(9;+sC{3!aLhdD0P|mZFgKQ-f(BTi{DI1*`4hLxLRxEtM8O- z;w!#*oac(FmURR~iZn9?cPyoR<(+NC{LVW~HY7DBB}W-xr!Ns rnz>_Ghg&_$SMuHboen$lZWG^h$K~WLcj^lGPX2DKgCF1-ZvMXkGkyv+ diff --git a/.venv/lib/python3.12/site-packages/requests/__version__.py b/.venv/lib/python3.12/site-packages/requests/__version__.py deleted file mode 100644 index 2c105ac..0000000 --- a/.venv/lib/python3.12/site-packages/requests/__version__.py +++ /dev/null @@ -1,14 +0,0 @@ -# .-. .-. .-. . . .-. .-. .-. .-. -# |( |- |.| | | |- `-. | `-. -# ' ' `-' `-`.`-' `-' `-' ' `-' - -__title__ = "requests" -__description__ = "Python HTTP for Humans." -__url__ = "https://requests.readthedocs.io" -__version__ = "2.32.3" -__build__ = 0x023203 -__author__ = "Kenneth Reitz" -__author_email__ = "me@kennethreitz.org" -__license__ = "Apache-2.0" -__copyright__ = "Copyright Kenneth Reitz" -__cake__ = "\u2728 \U0001f370 \u2728" diff --git a/.venv/lib/python3.12/site-packages/requests/_internal_utils.py b/.venv/lib/python3.12/site-packages/requests/_internal_utils.py deleted file mode 100644 index f2cf635..0000000 --- a/.venv/lib/python3.12/site-packages/requests/_internal_utils.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -requests._internal_utils -~~~~~~~~~~~~~~ - -Provides utility functions that are consumed internally by Requests -which depend on extremely few external helpers (such as compat) -""" -import re - -from .compat import builtin_str - -_VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*$") -_VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$") -_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$") -_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$") - -_HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR) -_HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE) -HEADER_VALIDATORS = { - bytes: _HEADER_VALIDATORS_BYTE, - str: _HEADER_VALIDATORS_STR, -} - - -def to_native_string(string, encoding="ascii"): - """Given a string object, regardless of type, returns a representation of - that string in the native string type, encoding and decoding where - necessary. This assumes ASCII unless told otherwise. - """ - if isinstance(string, builtin_str): - out = string - else: - out = string.decode(encoding) - - return out - - -def unicode_is_ascii(u_string): - """Determine if unicode string only contains ASCII characters. - - :param str u_string: unicode string to check. Must be unicode - and not Python 2 `str`. - :rtype: bool - """ - assert isinstance(u_string, str) - try: - u_string.encode("ascii") - return True - except UnicodeEncodeError: - return False diff --git a/.venv/lib/python3.12/site-packages/requests/adapters.py b/.venv/lib/python3.12/site-packages/requests/adapters.py deleted file mode 100644 index 9a58b16..0000000 --- a/.venv/lib/python3.12/site-packages/requests/adapters.py +++ /dev/null @@ -1,719 +0,0 @@ -""" -requests.adapters -~~~~~~~~~~~~~~~~~ - -This module contains the transport adapters that Requests uses to define -and maintain connections. -""" - -import os.path -import socket # noqa: F401 -import typing -import warnings - -from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError -from urllib3.exceptions import HTTPError as _HTTPError -from urllib3.exceptions import InvalidHeader as _InvalidHeader -from urllib3.exceptions import ( - LocationValueError, - MaxRetryError, - NewConnectionError, - ProtocolError, -) -from urllib3.exceptions import ProxyError as _ProxyError -from urllib3.exceptions import ReadTimeoutError, ResponseError -from urllib3.exceptions import SSLError as _SSLError -from urllib3.poolmanager import PoolManager, proxy_from_url -from urllib3.util import Timeout as TimeoutSauce -from urllib3.util import parse_url -from urllib3.util.retry import Retry -from urllib3.util.ssl_ import create_urllib3_context - -from .auth import _basic_auth_str -from .compat import basestring, urlparse -from .cookies import extract_cookies_to_jar -from .exceptions import ( - ConnectionError, - ConnectTimeout, - InvalidHeader, - InvalidProxyURL, - InvalidSchema, - InvalidURL, - ProxyError, - ReadTimeout, - RetryError, - SSLError, -) -from .models import Response -from .structures import CaseInsensitiveDict -from .utils import ( - DEFAULT_CA_BUNDLE_PATH, - extract_zipped_paths, - get_auth_from_url, - get_encoding_from_headers, - prepend_scheme_if_needed, - select_proxy, - urldefragauth, -) - -try: - from urllib3.contrib.socks import SOCKSProxyManager -except ImportError: - - def SOCKSProxyManager(*args, **kwargs): - raise InvalidSchema("Missing dependencies for SOCKS support.") - - -if typing.TYPE_CHECKING: - from .models import PreparedRequest - - -DEFAULT_POOLBLOCK = False -DEFAULT_POOLSIZE = 10 -DEFAULT_RETRIES = 0 -DEFAULT_POOL_TIMEOUT = None - - -try: - import ssl # noqa: F401 - - _preloaded_ssl_context = create_urllib3_context() - _preloaded_ssl_context.load_verify_locations( - extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH) - ) -except ImportError: - # Bypass default SSLContext creation when Python - # interpreter isn't built with the ssl module. - _preloaded_ssl_context = None - - -def _urllib3_request_context( - request: "PreparedRequest", - verify: "bool | str | None", - client_cert: "typing.Tuple[str, str] | str | None", - poolmanager: "PoolManager", -) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])": - host_params = {} - pool_kwargs = {} - parsed_request_url = urlparse(request.url) - scheme = parsed_request_url.scheme.lower() - port = parsed_request_url.port - - # Determine if we have and should use our default SSLContext - # to optimize performance on standard requests. - poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {}) - has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context") - should_use_default_ssl_context = ( - _preloaded_ssl_context is not None and not has_poolmanager_ssl_context - ) - - cert_reqs = "CERT_REQUIRED" - if verify is False: - cert_reqs = "CERT_NONE" - elif verify is True and should_use_default_ssl_context: - pool_kwargs["ssl_context"] = _preloaded_ssl_context - elif isinstance(verify, str): - if not os.path.isdir(verify): - pool_kwargs["ca_certs"] = verify - else: - pool_kwargs["ca_cert_dir"] = verify - pool_kwargs["cert_reqs"] = cert_reqs - if client_cert is not None: - if isinstance(client_cert, tuple) and len(client_cert) == 2: - pool_kwargs["cert_file"] = client_cert[0] - pool_kwargs["key_file"] = client_cert[1] - else: - # According to our docs, we allow users to specify just the client - # cert path - pool_kwargs["cert_file"] = client_cert - host_params = { - "scheme": scheme, - "host": parsed_request_url.hostname, - "port": port, - } - return host_params, pool_kwargs - - -class BaseAdapter: - """The Base Transport Adapter""" - - def __init__(self): - super().__init__() - - def send( - self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None - ): - """Sends PreparedRequest object. Returns Response object. - - :param request: The :class:`PreparedRequest ` being sent. - :param stream: (optional) Whether to stream the request content. - :param timeout: (optional) How long to wait for the server to send - data before giving up, as a float, or a :ref:`(connect timeout, - read timeout) ` tuple. - :type timeout: float or tuple - :param verify: (optional) Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use - :param cert: (optional) Any user-provided SSL certificate to be trusted. - :param proxies: (optional) The proxies dictionary to apply to the request. - """ - raise NotImplementedError - - def close(self): - """Cleans up adapter specific items.""" - raise NotImplementedError - - -class HTTPAdapter(BaseAdapter): - """The built-in HTTP Adapter for urllib3. - - Provides a general-case interface for Requests sessions to contact HTTP and - HTTPS urls by implementing the Transport Adapter interface. This class will - usually be created by the :class:`Session ` class under the - covers. - - :param pool_connections: The number of urllib3 connection pools to cache. - :param pool_maxsize: The maximum number of connections to save in the pool. - :param max_retries: The maximum number of retries each connection - should attempt. Note, this applies only to failed DNS lookups, socket - connections and connection timeouts, never to requests where data has - made it to the server. By default, Requests does not retry failed - connections. If you need granular control over the conditions under - which we retry a request, import urllib3's ``Retry`` class and pass - that instead. - :param pool_block: Whether the connection pool should block for connections. - - Usage:: - - >>> import requests - >>> s = requests.Session() - >>> a = requests.adapters.HTTPAdapter(max_retries=3) - >>> s.mount('http://', a) - """ - - __attrs__ = [ - "max_retries", - "config", - "_pool_connections", - "_pool_maxsize", - "_pool_block", - ] - - def __init__( - self, - pool_connections=DEFAULT_POOLSIZE, - pool_maxsize=DEFAULT_POOLSIZE, - max_retries=DEFAULT_RETRIES, - pool_block=DEFAULT_POOLBLOCK, - ): - if max_retries == DEFAULT_RETRIES: - self.max_retries = Retry(0, read=False) - else: - self.max_retries = Retry.from_int(max_retries) - self.config = {} - self.proxy_manager = {} - - super().__init__() - - self._pool_connections = pool_connections - self._pool_maxsize = pool_maxsize - self._pool_block = pool_block - - self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block) - - def __getstate__(self): - return {attr: getattr(self, attr, None) for attr in self.__attrs__} - - def __setstate__(self, state): - # Can't handle by adding 'proxy_manager' to self.__attrs__ because - # self.poolmanager uses a lambda function, which isn't pickleable. - self.proxy_manager = {} - self.config = {} - - for attr, value in state.items(): - setattr(self, attr, value) - - self.init_poolmanager( - self._pool_connections, self._pool_maxsize, block=self._pool_block - ) - - def init_poolmanager( - self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs - ): - """Initializes a urllib3 PoolManager. - - This method should not be called from user code, and is only - exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param connections: The number of urllib3 connection pools to cache. - :param maxsize: The maximum number of connections to save in the pool. - :param block: Block when no free connections are available. - :param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager. - """ - # save these values for pickling - self._pool_connections = connections - self._pool_maxsize = maxsize - self._pool_block = block - - self.poolmanager = PoolManager( - num_pools=connections, - maxsize=maxsize, - block=block, - **pool_kwargs, - ) - - def proxy_manager_for(self, proxy, **proxy_kwargs): - """Return urllib3 ProxyManager for the given proxy. - - This method should not be called from user code, and is only - exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param proxy: The proxy to return a urllib3 ProxyManager for. - :param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager. - :returns: ProxyManager - :rtype: urllib3.ProxyManager - """ - if proxy in self.proxy_manager: - manager = self.proxy_manager[proxy] - elif proxy.lower().startswith("socks"): - username, password = get_auth_from_url(proxy) - manager = self.proxy_manager[proxy] = SOCKSProxyManager( - proxy, - username=username, - password=password, - num_pools=self._pool_connections, - maxsize=self._pool_maxsize, - block=self._pool_block, - **proxy_kwargs, - ) - else: - proxy_headers = self.proxy_headers(proxy) - manager = self.proxy_manager[proxy] = proxy_from_url( - proxy, - proxy_headers=proxy_headers, - num_pools=self._pool_connections, - maxsize=self._pool_maxsize, - block=self._pool_block, - **proxy_kwargs, - ) - - return manager - - def cert_verify(self, conn, url, verify, cert): - """Verify a SSL certificate. This method should not be called from user - code, and is only exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param conn: The urllib3 connection object associated with the cert. - :param url: The requested URL. - :param verify: Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use - :param cert: The SSL certificate to verify. - """ - if url.lower().startswith("https") and verify: - conn.cert_reqs = "CERT_REQUIRED" - - # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use. - # Otherwise, if verify is a boolean, we don't load anything since - # the connection will be using a context with the default certificates already loaded, - # and this avoids a call to the slow load_verify_locations() - if verify is not True: - # `verify` must be a str with a path then - cert_loc = verify - - if not os.path.exists(cert_loc): - raise OSError( - f"Could not find a suitable TLS CA certificate bundle, " - f"invalid path: {cert_loc}" - ) - - if not os.path.isdir(cert_loc): - conn.ca_certs = cert_loc - else: - conn.ca_cert_dir = cert_loc - else: - conn.cert_reqs = "CERT_NONE" - conn.ca_certs = None - conn.ca_cert_dir = None - - if cert: - if not isinstance(cert, basestring): - conn.cert_file = cert[0] - conn.key_file = cert[1] - else: - conn.cert_file = cert - conn.key_file = None - if conn.cert_file and not os.path.exists(conn.cert_file): - raise OSError( - f"Could not find the TLS certificate file, " - f"invalid path: {conn.cert_file}" - ) - if conn.key_file and not os.path.exists(conn.key_file): - raise OSError( - f"Could not find the TLS key file, invalid path: {conn.key_file}" - ) - - def build_response(self, req, resp): - """Builds a :class:`Response ` object from a urllib3 - response. This should not be called from user code, and is only exposed - for use when subclassing the - :class:`HTTPAdapter ` - - :param req: The :class:`PreparedRequest ` used to generate the response. - :param resp: The urllib3 response object. - :rtype: requests.Response - """ - response = Response() - - # Fallback to None if there's no status_code, for whatever reason. - response.status_code = getattr(resp, "status", None) - - # Make headers case-insensitive. - response.headers = CaseInsensitiveDict(getattr(resp, "headers", {})) - - # Set encoding. - response.encoding = get_encoding_from_headers(response.headers) - response.raw = resp - response.reason = response.raw.reason - - if isinstance(req.url, bytes): - response.url = req.url.decode("utf-8") - else: - response.url = req.url - - # Add new cookies from the server. - extract_cookies_to_jar(response.cookies, req, resp) - - # Give the Response some context. - response.request = req - response.connection = self - - return response - - def build_connection_pool_key_attributes(self, request, verify, cert=None): - """Build the PoolKey attributes used by urllib3 to return a connection. - - This looks at the PreparedRequest, the user-specified verify value, - and the value of the cert parameter to determine what PoolKey values - to use to select a connection from a given urllib3 Connection Pool. - - The SSL related pool key arguments are not consistently set. As of - this writing, use the following to determine what keys may be in that - dictionary: - - * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the - default Requests SSL Context - * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but - ``"cert_reqs"`` will be set - * If ``verify`` is a string, (i.e., it is a user-specified trust bundle) - ``"ca_certs"`` will be set if the string is not a directory recognized - by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be - set. - * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If - ``"cert"`` is a tuple with a second item, ``"key_file"`` will also - be present - - To override these settings, one may subclass this class, call this - method and use the above logic to change parameters as desired. For - example, if one wishes to use a custom :py:class:`ssl.SSLContext` one - must both set ``"ssl_context"`` and based on what else they require, - alter the other keys to ensure the desired behaviour. - - :param request: - The PreparedReqest being sent over the connection. - :type request: - :class:`~requests.models.PreparedRequest` - :param verify: - Either a boolean, in which case it controls whether - we verify the server's TLS certificate, or a string, in which case it - must be a path to a CA bundle to use. - :param cert: - (optional) Any user-provided SSL certificate for client - authentication (a.k.a., mTLS). This may be a string (i.e., just - the path to a file which holds both certificate and key) or a - tuple of length 2 with the certificate file path and key file - path. - :returns: - A tuple of two dictionaries. The first is the "host parameters" - portion of the Pool Key including scheme, hostname, and port. The - second is a dictionary of SSLContext related parameters. - """ - return _urllib3_request_context(request, verify, cert, self.poolmanager) - - def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None): - """Returns a urllib3 connection for the given request and TLS settings. - This should not be called from user code, and is only exposed for use - when subclassing the :class:`HTTPAdapter `. - - :param request: - The :class:`PreparedRequest ` object to be sent - over the connection. - :param verify: - Either a boolean, in which case it controls whether we verify the - server's TLS certificate, or a string, in which case it must be a - path to a CA bundle to use. - :param proxies: - (optional) The proxies dictionary to apply to the request. - :param cert: - (optional) Any user-provided SSL certificate to be used for client - authentication (a.k.a., mTLS). - :rtype: - urllib3.ConnectionPool - """ - proxy = select_proxy(request.url, proxies) - try: - host_params, pool_kwargs = self.build_connection_pool_key_attributes( - request, - verify, - cert, - ) - except ValueError as e: - raise InvalidURL(e, request=request) - if proxy: - proxy = prepend_scheme_if_needed(proxy, "http") - proxy_url = parse_url(proxy) - if not proxy_url.host: - raise InvalidProxyURL( - "Please check proxy URL. It is malformed " - "and could be missing the host." - ) - proxy_manager = self.proxy_manager_for(proxy) - conn = proxy_manager.connection_from_host( - **host_params, pool_kwargs=pool_kwargs - ) - else: - # Only scheme should be lower case - conn = self.poolmanager.connection_from_host( - **host_params, pool_kwargs=pool_kwargs - ) - - return conn - - def get_connection(self, url, proxies=None): - """DEPRECATED: Users should move to `get_connection_with_tls_context` - for all subclasses of HTTPAdapter using Requests>=2.32.2. - - Returns a urllib3 connection for the given URL. This should not be - called from user code, and is only exposed for use when subclassing the - :class:`HTTPAdapter `. - - :param url: The URL to connect to. - :param proxies: (optional) A Requests-style dictionary of proxies used on this request. - :rtype: urllib3.ConnectionPool - """ - warnings.warn( - ( - "`get_connection` has been deprecated in favor of " - "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses " - "will need to migrate for Requests>=2.32.2. Please see " - "https://github.com/psf/requests/pull/6710 for more details." - ), - DeprecationWarning, - ) - proxy = select_proxy(url, proxies) - - if proxy: - proxy = prepend_scheme_if_needed(proxy, "http") - proxy_url = parse_url(proxy) - if not proxy_url.host: - raise InvalidProxyURL( - "Please check proxy URL. It is malformed " - "and could be missing the host." - ) - proxy_manager = self.proxy_manager_for(proxy) - conn = proxy_manager.connection_from_url(url) - else: - # Only scheme should be lower case - parsed = urlparse(url) - url = parsed.geturl() - conn = self.poolmanager.connection_from_url(url) - - return conn - - def close(self): - """Disposes of any internal state. - - Currently, this closes the PoolManager and any active ProxyManager, - which closes any pooled connections. - """ - self.poolmanager.clear() - for proxy in self.proxy_manager.values(): - proxy.clear() - - def request_url(self, request, proxies): - """Obtain the url to use when making the final request. - - If the message is being sent through a HTTP proxy, the full URL has to - be used. Otherwise, we should only use the path portion of the URL. - - This should not be called from user code, and is only exposed for use - when subclassing the - :class:`HTTPAdapter `. - - :param request: The :class:`PreparedRequest ` being sent. - :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs. - :rtype: str - """ - proxy = select_proxy(request.url, proxies) - scheme = urlparse(request.url).scheme - - is_proxied_http_request = proxy and scheme != "https" - using_socks_proxy = False - if proxy: - proxy_scheme = urlparse(proxy).scheme.lower() - using_socks_proxy = proxy_scheme.startswith("socks") - - url = request.path_url - if url.startswith("//"): # Don't confuse urllib3 - url = f"/{url.lstrip('/')}" - - if is_proxied_http_request and not using_socks_proxy: - url = urldefragauth(request.url) - - return url - - def add_headers(self, request, **kwargs): - """Add any headers needed by the connection. As of v2.0 this does - nothing by default, but is left for overriding by users that subclass - the :class:`HTTPAdapter `. - - This should not be called from user code, and is only exposed for use - when subclassing the - :class:`HTTPAdapter `. - - :param request: The :class:`PreparedRequest ` to add headers to. - :param kwargs: The keyword arguments from the call to send(). - """ - pass - - def proxy_headers(self, proxy): - """Returns a dictionary of the headers to add to any request sent - through a proxy. This works with urllib3 magic to ensure that they are - correctly sent to the proxy, rather than in a tunnelled request if - CONNECT is being used. - - This should not be called from user code, and is only exposed for use - when subclassing the - :class:`HTTPAdapter `. - - :param proxy: The url of the proxy being used for this request. - :rtype: dict - """ - headers = {} - username, password = get_auth_from_url(proxy) - - if username: - headers["Proxy-Authorization"] = _basic_auth_str(username, password) - - return headers - - def send( - self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None - ): - """Sends PreparedRequest object. Returns Response object. - - :param request: The :class:`PreparedRequest ` being sent. - :param stream: (optional) Whether to stream the request content. - :param timeout: (optional) How long to wait for the server to send - data before giving up, as a float, or a :ref:`(connect timeout, - read timeout) ` tuple. - :type timeout: float or tuple or urllib3 Timeout object - :param verify: (optional) Either a boolean, in which case it controls whether - we verify the server's TLS certificate, or a string, in which case it - must be a path to a CA bundle to use - :param cert: (optional) Any user-provided SSL certificate to be trusted. - :param proxies: (optional) The proxies dictionary to apply to the request. - :rtype: requests.Response - """ - - try: - conn = self.get_connection_with_tls_context( - request, verify, proxies=proxies, cert=cert - ) - except LocationValueError as e: - raise InvalidURL(e, request=request) - - self.cert_verify(conn, request.url, verify, cert) - url = self.request_url(request, proxies) - self.add_headers( - request, - stream=stream, - timeout=timeout, - verify=verify, - cert=cert, - proxies=proxies, - ) - - chunked = not (request.body is None or "Content-Length" in request.headers) - - if isinstance(timeout, tuple): - try: - connect, read = timeout - timeout = TimeoutSauce(connect=connect, read=read) - except ValueError: - raise ValueError( - f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, " - f"or a single float to set both timeouts to the same value." - ) - elif isinstance(timeout, TimeoutSauce): - pass - else: - timeout = TimeoutSauce(connect=timeout, read=timeout) - - try: - resp = conn.urlopen( - method=request.method, - url=url, - body=request.body, - headers=request.headers, - redirect=False, - assert_same_host=False, - preload_content=False, - decode_content=False, - retries=self.max_retries, - timeout=timeout, - chunked=chunked, - ) - - except (ProtocolError, OSError) as err: - raise ConnectionError(err, request=request) - - except MaxRetryError as e: - if isinstance(e.reason, ConnectTimeoutError): - # TODO: Remove this in 3.0.0: see #2811 - if not isinstance(e.reason, NewConnectionError): - raise ConnectTimeout(e, request=request) - - if isinstance(e.reason, ResponseError): - raise RetryError(e, request=request) - - if isinstance(e.reason, _ProxyError): - raise ProxyError(e, request=request) - - if isinstance(e.reason, _SSLError): - # This branch is for urllib3 v1.22 and later. - raise SSLError(e, request=request) - - raise ConnectionError(e, request=request) - - except ClosedPoolError as e: - raise ConnectionError(e, request=request) - - except _ProxyError as e: - raise ProxyError(e) - - except (_SSLError, _HTTPError) as e: - if isinstance(e, _SSLError): - # This branch is for urllib3 versions earlier than v1.22 - raise SSLError(e, request=request) - elif isinstance(e, ReadTimeoutError): - raise ReadTimeout(e, request=request) - elif isinstance(e, _InvalidHeader): - raise InvalidHeader(e, request=request) - else: - raise - - return self.build_response(request, resp) diff --git a/.venv/lib/python3.12/site-packages/requests/api.py b/.venv/lib/python3.12/site-packages/requests/api.py deleted file mode 100644 index 5960744..0000000 --- a/.venv/lib/python3.12/site-packages/requests/api.py +++ /dev/null @@ -1,157 +0,0 @@ -""" -requests.api -~~~~~~~~~~~~ - -This module implements the Requests API. - -:copyright: (c) 2012 by Kenneth Reitz. -:license: Apache2, see LICENSE for more details. -""" - -from . import sessions - - -def request(method, url, **kwargs): - """Constructs and sends a :class:`Request `. - - :param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``. - :param url: URL for the new :class:`Request` object. - :param params: (optional) Dictionary, list of tuples or bytes to send - in the query string for the :class:`Request`. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. - :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. - :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. - :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload. - ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')`` - or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string - defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers - to add for the file. - :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) How many seconds to wait for the server to send data - before giving up, as a float, or a :ref:`(connect timeout, read - timeout) ` tuple. - :type timeout: float or tuple - :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``. - :type allow_redirects: bool - :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. - :param verify: (optional) Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use. Defaults to ``True``. - :param stream: (optional) if ``False``, the response content will be immediately downloaded. - :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. - :return: :class:`Response ` object - :rtype: requests.Response - - Usage:: - - >>> import requests - >>> req = requests.request('GET', 'https://httpbin.org/get') - >>> req - - """ - - # By using the 'with' statement we are sure the session is closed, thus we - # avoid leaving sockets open which can trigger a ResourceWarning in some - # cases, and look like a memory leak in others. - with sessions.Session() as session: - return session.request(method=method, url=url, **kwargs) - - -def get(url, params=None, **kwargs): - r"""Sends a GET request. - - :param url: URL for the new :class:`Request` object. - :param params: (optional) Dictionary, list of tuples or bytes to send - in the query string for the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("get", url, params=params, **kwargs) - - -def options(url, **kwargs): - r"""Sends an OPTIONS request. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("options", url, **kwargs) - - -def head(url, **kwargs): - r"""Sends a HEAD request. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. If - `allow_redirects` is not provided, it will be set to `False` (as - opposed to the default :meth:`request` behavior). - :return: :class:`Response ` object - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", False) - return request("head", url, **kwargs) - - -def post(url, data=None, json=None, **kwargs): - r"""Sends a POST request. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("post", url, data=data, json=json, **kwargs) - - -def put(url, data=None, **kwargs): - r"""Sends a PUT request. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("put", url, data=data, **kwargs) - - -def patch(url, data=None, **kwargs): - r"""Sends a PATCH request. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("patch", url, data=data, **kwargs) - - -def delete(url, **kwargs): - r"""Sends a DELETE request. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :return: :class:`Response ` object - :rtype: requests.Response - """ - - return request("delete", url, **kwargs) diff --git a/.venv/lib/python3.12/site-packages/requests/auth.py b/.venv/lib/python3.12/site-packages/requests/auth.py deleted file mode 100644 index 4a7ce6d..0000000 --- a/.venv/lib/python3.12/site-packages/requests/auth.py +++ /dev/null @@ -1,314 +0,0 @@ -""" -requests.auth -~~~~~~~~~~~~~ - -This module contains the authentication handlers for Requests. -""" - -import hashlib -import os -import re -import threading -import time -import warnings -from base64 import b64encode - -from ._internal_utils import to_native_string -from .compat import basestring, str, urlparse -from .cookies import extract_cookies_to_jar -from .utils import parse_dict_header - -CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded" -CONTENT_TYPE_MULTI_PART = "multipart/form-data" - - -def _basic_auth_str(username, password): - """Returns a Basic Auth string.""" - - # "I want us to put a big-ol' comment on top of it that - # says that this behaviour is dumb but we need to preserve - # it because people are relying on it." - # - Lukasa - # - # These are here solely to maintain backwards compatibility - # for things like ints. This will be removed in 3.0.0. - if not isinstance(username, basestring): - warnings.warn( - "Non-string usernames will no longer be supported in Requests " - "3.0.0. Please convert the object you've passed in ({!r}) to " - "a string or bytes object in the near future to avoid " - "problems.".format(username), - category=DeprecationWarning, - ) - username = str(username) - - if not isinstance(password, basestring): - warnings.warn( - "Non-string passwords will no longer be supported in Requests " - "3.0.0. Please convert the object you've passed in ({!r}) to " - "a string or bytes object in the near future to avoid " - "problems.".format(type(password)), - category=DeprecationWarning, - ) - password = str(password) - # -- End Removal -- - - if isinstance(username, str): - username = username.encode("latin1") - - if isinstance(password, str): - password = password.encode("latin1") - - authstr = "Basic " + to_native_string( - b64encode(b":".join((username, password))).strip() - ) - - return authstr - - -class AuthBase: - """Base class that all auth implementations derive from""" - - def __call__(self, r): - raise NotImplementedError("Auth hooks must be callable.") - - -class HTTPBasicAuth(AuthBase): - """Attaches HTTP Basic Authentication to the given Request object.""" - - def __init__(self, username, password): - self.username = username - self.password = password - - def __eq__(self, other): - return all( - [ - self.username == getattr(other, "username", None), - self.password == getattr(other, "password", None), - ] - ) - - def __ne__(self, other): - return not self == other - - def __call__(self, r): - r.headers["Authorization"] = _basic_auth_str(self.username, self.password) - return r - - -class HTTPProxyAuth(HTTPBasicAuth): - """Attaches HTTP Proxy Authentication to a given Request object.""" - - def __call__(self, r): - r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password) - return r - - -class HTTPDigestAuth(AuthBase): - """Attaches HTTP Digest Authentication to the given Request object.""" - - def __init__(self, username, password): - self.username = username - self.password = password - # Keep state in per-thread local storage - self._thread_local = threading.local() - - def init_per_thread_state(self): - # Ensure state is initialized just once per-thread - if not hasattr(self._thread_local, "init"): - self._thread_local.init = True - self._thread_local.last_nonce = "" - self._thread_local.nonce_count = 0 - self._thread_local.chal = {} - self._thread_local.pos = None - self._thread_local.num_401_calls = None - - def build_digest_header(self, method, url): - """ - :rtype: str - """ - - realm = self._thread_local.chal["realm"] - nonce = self._thread_local.chal["nonce"] - qop = self._thread_local.chal.get("qop") - algorithm = self._thread_local.chal.get("algorithm") - opaque = self._thread_local.chal.get("opaque") - hash_utf8 = None - - if algorithm is None: - _algorithm = "MD5" - else: - _algorithm = algorithm.upper() - # lambdas assume digest modules are imported at the top level - if _algorithm == "MD5" or _algorithm == "MD5-SESS": - - def md5_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.md5(x).hexdigest() - - hash_utf8 = md5_utf8 - elif _algorithm == "SHA": - - def sha_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.sha1(x).hexdigest() - - hash_utf8 = sha_utf8 - elif _algorithm == "SHA-256": - - def sha256_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.sha256(x).hexdigest() - - hash_utf8 = sha256_utf8 - elif _algorithm == "SHA-512": - - def sha512_utf8(x): - if isinstance(x, str): - x = x.encode("utf-8") - return hashlib.sha512(x).hexdigest() - - hash_utf8 = sha512_utf8 - - KD = lambda s, d: hash_utf8(f"{s}:{d}") # noqa:E731 - - if hash_utf8 is None: - return None - - # XXX not implemented yet - entdig = None - p_parsed = urlparse(url) - #: path is request-uri defined in RFC 2616 which should not be empty - path = p_parsed.path or "/" - if p_parsed.query: - path += f"?{p_parsed.query}" - - A1 = f"{self.username}:{realm}:{self.password}" - A2 = f"{method}:{path}" - - HA1 = hash_utf8(A1) - HA2 = hash_utf8(A2) - - if nonce == self._thread_local.last_nonce: - self._thread_local.nonce_count += 1 - else: - self._thread_local.nonce_count = 1 - ncvalue = f"{self._thread_local.nonce_count:08x}" - s = str(self._thread_local.nonce_count).encode("utf-8") - s += nonce.encode("utf-8") - s += time.ctime().encode("utf-8") - s += os.urandom(8) - - cnonce = hashlib.sha1(s).hexdigest()[:16] - if _algorithm == "MD5-SESS": - HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}") - - if not qop: - respdig = KD(HA1, f"{nonce}:{HA2}") - elif qop == "auth" or "auth" in qop.split(","): - noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}" - respdig = KD(HA1, noncebit) - else: - # XXX handle auth-int. - return None - - self._thread_local.last_nonce = nonce - - # XXX should the partial digests be encoded too? - base = ( - f'username="{self.username}", realm="{realm}", nonce="{nonce}", ' - f'uri="{path}", response="{respdig}"' - ) - if opaque: - base += f', opaque="{opaque}"' - if algorithm: - base += f', algorithm="{algorithm}"' - if entdig: - base += f', digest="{entdig}"' - if qop: - base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"' - - return f"Digest {base}" - - def handle_redirect(self, r, **kwargs): - """Reset num_401_calls counter on redirects.""" - if r.is_redirect: - self._thread_local.num_401_calls = 1 - - def handle_401(self, r, **kwargs): - """ - Takes the given response and tries digest-auth, if needed. - - :rtype: requests.Response - """ - - # If response is not 4xx, do not auth - # See https://github.com/psf/requests/issues/3772 - if not 400 <= r.status_code < 500: - self._thread_local.num_401_calls = 1 - return r - - if self._thread_local.pos is not None: - # Rewind the file position indicator of the body to where - # it was to resend the request. - r.request.body.seek(self._thread_local.pos) - s_auth = r.headers.get("www-authenticate", "") - - if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2: - self._thread_local.num_401_calls += 1 - pat = re.compile(r"digest ", flags=re.IGNORECASE) - self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1)) - - # Consume content and release the original connection - # to allow our new request to reuse the same one. - r.content - r.close() - prep = r.request.copy() - extract_cookies_to_jar(prep._cookies, r.request, r.raw) - prep.prepare_cookies(prep._cookies) - - prep.headers["Authorization"] = self.build_digest_header( - prep.method, prep.url - ) - _r = r.connection.send(prep, **kwargs) - _r.history.append(r) - _r.request = prep - - return _r - - self._thread_local.num_401_calls = 1 - return r - - def __call__(self, r): - # Initialize per-thread state, if needed - self.init_per_thread_state() - # If we have a saved nonce, skip the 401 - if self._thread_local.last_nonce: - r.headers["Authorization"] = self.build_digest_header(r.method, r.url) - try: - self._thread_local.pos = r.body.tell() - except AttributeError: - # In the case of HTTPDigestAuth being reused and the body of - # the previous request was a file-like object, pos has the - # file position of the previous body. Ensure it's set to - # None. - self._thread_local.pos = None - r.register_hook("response", self.handle_401) - r.register_hook("response", self.handle_redirect) - self._thread_local.num_401_calls = 1 - - return r - - def __eq__(self, other): - return all( - [ - self.username == getattr(other, "username", None), - self.password == getattr(other, "password", None), - ] - ) - - def __ne__(self, other): - return not self == other diff --git a/.venv/lib/python3.12/site-packages/requests/certs.py b/.venv/lib/python3.12/site-packages/requests/certs.py deleted file mode 100644 index be422c3..0000000 --- a/.venv/lib/python3.12/site-packages/requests/certs.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python - -""" -requests.certs -~~~~~~~~~~~~~~ - -This module returns the preferred default CA certificate bundle. There is -only one — the one from the certifi package. - -If you are packaging Requests, e.g., for a Linux distribution or a managed -environment, you can change the definition of where() to return a separately -packaged CA bundle. -""" -from certifi import where - -if __name__ == "__main__": - print(where()) diff --git a/.venv/lib/python3.12/site-packages/requests/compat.py b/.venv/lib/python3.12/site-packages/requests/compat.py deleted file mode 100644 index 095de1b..0000000 --- a/.venv/lib/python3.12/site-packages/requests/compat.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -requests.compat -~~~~~~~~~~~~~~~ - -This module previously handled import compatibility issues -between Python 2 and Python 3. It remains for backwards -compatibility until the next major version. -""" - -import importlib -import sys - -# ------------------- -# Character Detection -# ------------------- - - -def _resolve_char_detection(): - """Find supported character detection libraries.""" - chardet = None - for lib in ("chardet", "charset_normalizer"): - if chardet is None: - try: - chardet = importlib.import_module(lib) - except ImportError: - pass - return chardet - - -chardet = _resolve_char_detection() - -# ------- -# Pythons -# ------- - -# Syntax sugar. -_ver = sys.version_info - -#: Python 2.x? -is_py2 = _ver[0] == 2 - -#: Python 3.x? -is_py3 = _ver[0] == 3 - -# json/simplejson module import resolution -has_simplejson = False -try: - import simplejson as json - - has_simplejson = True -except ImportError: - import json - -if has_simplejson: - from simplejson import JSONDecodeError -else: - from json import JSONDecodeError - -# Keep OrderedDict for backwards compatibility. -from collections import OrderedDict -from collections.abc import Callable, Mapping, MutableMapping -from http import cookiejar as cookielib -from http.cookies import Morsel -from io import StringIO - -# -------------- -# Legacy Imports -# -------------- -from urllib.parse import ( - quote, - quote_plus, - unquote, - unquote_plus, - urldefrag, - urlencode, - urljoin, - urlparse, - urlsplit, - urlunparse, -) -from urllib.request import ( - getproxies, - getproxies_environment, - parse_http_list, - proxy_bypass, - proxy_bypass_environment, -) - -builtin_str = str -str = str -bytes = bytes -basestring = (str, bytes) -numeric_types = (int, float) -integer_types = (int,) diff --git a/.venv/lib/python3.12/site-packages/requests/cookies.py b/.venv/lib/python3.12/site-packages/requests/cookies.py deleted file mode 100644 index f69d0cd..0000000 --- a/.venv/lib/python3.12/site-packages/requests/cookies.py +++ /dev/null @@ -1,561 +0,0 @@ -""" -requests.cookies -~~~~~~~~~~~~~~~~ - -Compatibility code to be able to use `http.cookiejar.CookieJar` with requests. - -requests.utils imports from here, so be careful with imports. -""" - -import calendar -import copy -import time - -from ._internal_utils import to_native_string -from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse - -try: - import threading -except ImportError: - import dummy_threading as threading - - -class MockRequest: - """Wraps a `requests.Request` to mimic a `urllib2.Request`. - - The code in `http.cookiejar.CookieJar` expects this interface in order to correctly - manage cookie policies, i.e., determine whether a cookie can be set, given the - domains of the request and the cookie. - - The original request object is read-only. The client is responsible for collecting - the new headers via `get_new_headers()` and interpreting them appropriately. You - probably want `get_cookie_header`, defined below. - """ - - def __init__(self, request): - self._r = request - self._new_headers = {} - self.type = urlparse(self._r.url).scheme - - def get_type(self): - return self.type - - def get_host(self): - return urlparse(self._r.url).netloc - - def get_origin_req_host(self): - return self.get_host() - - def get_full_url(self): - # Only return the response's URL if the user hadn't set the Host - # header - if not self._r.headers.get("Host"): - return self._r.url - # If they did set it, retrieve it and reconstruct the expected domain - host = to_native_string(self._r.headers["Host"], encoding="utf-8") - parsed = urlparse(self._r.url) - # Reconstruct the URL as we expect it - return urlunparse( - [ - parsed.scheme, - host, - parsed.path, - parsed.params, - parsed.query, - parsed.fragment, - ] - ) - - def is_unverifiable(self): - return True - - def has_header(self, name): - return name in self._r.headers or name in self._new_headers - - def get_header(self, name, default=None): - return self._r.headers.get(name, self._new_headers.get(name, default)) - - def add_header(self, key, val): - """cookiejar has no legitimate use for this method; add it back if you find one.""" - raise NotImplementedError( - "Cookie headers should be added with add_unredirected_header()" - ) - - def add_unredirected_header(self, name, value): - self._new_headers[name] = value - - def get_new_headers(self): - return self._new_headers - - @property - def unverifiable(self): - return self.is_unverifiable() - - @property - def origin_req_host(self): - return self.get_origin_req_host() - - @property - def host(self): - return self.get_host() - - -class MockResponse: - """Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`. - - ...what? Basically, expose the parsed HTTP headers from the server response - the way `http.cookiejar` expects to see them. - """ - - def __init__(self, headers): - """Make a MockResponse for `cookiejar` to read. - - :param headers: a httplib.HTTPMessage or analogous carrying the headers - """ - self._headers = headers - - def info(self): - return self._headers - - def getheaders(self, name): - self._headers.getheaders(name) - - -def extract_cookies_to_jar(jar, request, response): - """Extract the cookies from the response into a CookieJar. - - :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar) - :param request: our own requests.Request object - :param response: urllib3.HTTPResponse object - """ - if not (hasattr(response, "_original_response") and response._original_response): - return - # the _original_response field is the wrapped httplib.HTTPResponse object, - req = MockRequest(request) - # pull out the HTTPMessage with the headers and put it in the mock: - res = MockResponse(response._original_response.msg) - jar.extract_cookies(res, req) - - -def get_cookie_header(jar, request): - """ - Produce an appropriate Cookie header string to be sent with `request`, or None. - - :rtype: str - """ - r = MockRequest(request) - jar.add_cookie_header(r) - return r.get_new_headers().get("Cookie") - - -def remove_cookie_by_name(cookiejar, name, domain=None, path=None): - """Unsets a cookie by name, by default over all domains and paths. - - Wraps CookieJar.clear(), is O(n). - """ - clearables = [] - for cookie in cookiejar: - if cookie.name != name: - continue - if domain is not None and domain != cookie.domain: - continue - if path is not None and path != cookie.path: - continue - clearables.append((cookie.domain, cookie.path, cookie.name)) - - for domain, path, name in clearables: - cookiejar.clear(domain, path, name) - - -class CookieConflictError(RuntimeError): - """There are two cookies that meet the criteria specified in the cookie jar. - Use .get and .set and include domain and path args in order to be more specific. - """ - - -class RequestsCookieJar(cookielib.CookieJar, MutableMapping): - """Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict - interface. - - This is the CookieJar we create by default for requests and sessions that - don't specify one, since some clients may expect response.cookies and - session.cookies to support dict operations. - - Requests does not use the dict interface internally; it's just for - compatibility with external client code. All requests code should work - out of the box with externally provided instances of ``CookieJar``, e.g. - ``LWPCookieJar`` and ``FileCookieJar``. - - Unlike a regular CookieJar, this class is pickleable. - - .. warning:: dictionary operations that are normally O(1) may be O(n). - """ - - def get(self, name, default=None, domain=None, path=None): - """Dict-like get() that also supports optional domain and path args in - order to resolve naming collisions from using one cookie jar over - multiple domains. - - .. warning:: operation is O(n), not O(1). - """ - try: - return self._find_no_duplicates(name, domain, path) - except KeyError: - return default - - def set(self, name, value, **kwargs): - """Dict-like set() that also supports optional domain and path args in - order to resolve naming collisions from using one cookie jar over - multiple domains. - """ - # support client code that unsets cookies by assignment of a None value: - if value is None: - remove_cookie_by_name( - self, name, domain=kwargs.get("domain"), path=kwargs.get("path") - ) - return - - if isinstance(value, Morsel): - c = morsel_to_cookie(value) - else: - c = create_cookie(name, value, **kwargs) - self.set_cookie(c) - return c - - def iterkeys(self): - """Dict-like iterkeys() that returns an iterator of names of cookies - from the jar. - - .. seealso:: itervalues() and iteritems(). - """ - for cookie in iter(self): - yield cookie.name - - def keys(self): - """Dict-like keys() that returns a list of names of cookies from the - jar. - - .. seealso:: values() and items(). - """ - return list(self.iterkeys()) - - def itervalues(self): - """Dict-like itervalues() that returns an iterator of values of cookies - from the jar. - - .. seealso:: iterkeys() and iteritems(). - """ - for cookie in iter(self): - yield cookie.value - - def values(self): - """Dict-like values() that returns a list of values of cookies from the - jar. - - .. seealso:: keys() and items(). - """ - return list(self.itervalues()) - - def iteritems(self): - """Dict-like iteritems() that returns an iterator of name-value tuples - from the jar. - - .. seealso:: iterkeys() and itervalues(). - """ - for cookie in iter(self): - yield cookie.name, cookie.value - - def items(self): - """Dict-like items() that returns a list of name-value tuples from the - jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a - vanilla python dict of key value pairs. - - .. seealso:: keys() and values(). - """ - return list(self.iteritems()) - - def list_domains(self): - """Utility method to list all the domains in the jar.""" - domains = [] - for cookie in iter(self): - if cookie.domain not in domains: - domains.append(cookie.domain) - return domains - - def list_paths(self): - """Utility method to list all the paths in the jar.""" - paths = [] - for cookie in iter(self): - if cookie.path not in paths: - paths.append(cookie.path) - return paths - - def multiple_domains(self): - """Returns True if there are multiple domains in the jar. - Returns False otherwise. - - :rtype: bool - """ - domains = [] - for cookie in iter(self): - if cookie.domain is not None and cookie.domain in domains: - return True - domains.append(cookie.domain) - return False # there is only one domain in jar - - def get_dict(self, domain=None, path=None): - """Takes as an argument an optional domain and path and returns a plain - old Python dict of name-value pairs of cookies that meet the - requirements. - - :rtype: dict - """ - dictionary = {} - for cookie in iter(self): - if (domain is None or cookie.domain == domain) and ( - path is None or cookie.path == path - ): - dictionary[cookie.name] = cookie.value - return dictionary - - def __contains__(self, name): - try: - return super().__contains__(name) - except CookieConflictError: - return True - - def __getitem__(self, name): - """Dict-like __getitem__() for compatibility with client code. Throws - exception if there are more than one cookie with name. In that case, - use the more explicit get() method instead. - - .. warning:: operation is O(n), not O(1). - """ - return self._find_no_duplicates(name) - - def __setitem__(self, name, value): - """Dict-like __setitem__ for compatibility with client code. Throws - exception if there is already a cookie of that name in the jar. In that - case, use the more explicit set() method instead. - """ - self.set(name, value) - - def __delitem__(self, name): - """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s - ``remove_cookie_by_name()``. - """ - remove_cookie_by_name(self, name) - - def set_cookie(self, cookie, *args, **kwargs): - if ( - hasattr(cookie.value, "startswith") - and cookie.value.startswith('"') - and cookie.value.endswith('"') - ): - cookie.value = cookie.value.replace('\\"', "") - return super().set_cookie(cookie, *args, **kwargs) - - def update(self, other): - """Updates this jar with cookies from another CookieJar or dict-like""" - if isinstance(other, cookielib.CookieJar): - for cookie in other: - self.set_cookie(copy.copy(cookie)) - else: - super().update(other) - - def _find(self, name, domain=None, path=None): - """Requests uses this method internally to get cookie values. - - If there are conflicting cookies, _find arbitrarily chooses one. - See _find_no_duplicates if you want an exception thrown if there are - conflicting cookies. - - :param name: a string containing name of cookie - :param domain: (optional) string containing domain of cookie - :param path: (optional) string containing path of cookie - :return: cookie.value - """ - for cookie in iter(self): - if cookie.name == name: - if domain is None or cookie.domain == domain: - if path is None or cookie.path == path: - return cookie.value - - raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") - - def _find_no_duplicates(self, name, domain=None, path=None): - """Both ``__get_item__`` and ``get`` call this function: it's never - used elsewhere in Requests. - - :param name: a string containing name of cookie - :param domain: (optional) string containing domain of cookie - :param path: (optional) string containing path of cookie - :raises KeyError: if cookie is not found - :raises CookieConflictError: if there are multiple cookies - that match name and optionally domain and path - :return: cookie.value - """ - toReturn = None - for cookie in iter(self): - if cookie.name == name: - if domain is None or cookie.domain == domain: - if path is None or cookie.path == path: - if toReturn is not None: - # if there are multiple cookies that meet passed in criteria - raise CookieConflictError( - f"There are multiple cookies with name, {name!r}" - ) - # we will eventually return this as long as no cookie conflict - toReturn = cookie.value - - if toReturn: - return toReturn - raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}") - - def __getstate__(self): - """Unlike a normal CookieJar, this class is pickleable.""" - state = self.__dict__.copy() - # remove the unpickleable RLock object - state.pop("_cookies_lock") - return state - - def __setstate__(self, state): - """Unlike a normal CookieJar, this class is pickleable.""" - self.__dict__.update(state) - if "_cookies_lock" not in self.__dict__: - self._cookies_lock = threading.RLock() - - def copy(self): - """Return a copy of this RequestsCookieJar.""" - new_cj = RequestsCookieJar() - new_cj.set_policy(self.get_policy()) - new_cj.update(self) - return new_cj - - def get_policy(self): - """Return the CookiePolicy instance used.""" - return self._policy - - -def _copy_cookie_jar(jar): - if jar is None: - return None - - if hasattr(jar, "copy"): - # We're dealing with an instance of RequestsCookieJar - return jar.copy() - # We're dealing with a generic CookieJar instance - new_jar = copy.copy(jar) - new_jar.clear() - for cookie in jar: - new_jar.set_cookie(copy.copy(cookie)) - return new_jar - - -def create_cookie(name, value, **kwargs): - """Make a cookie from underspecified parameters. - - By default, the pair of `name` and `value` will be set for the domain '' - and sent on every request (this is sometimes called a "supercookie"). - """ - result = { - "version": 0, - "name": name, - "value": value, - "port": None, - "domain": "", - "path": "/", - "secure": False, - "expires": None, - "discard": True, - "comment": None, - "comment_url": None, - "rest": {"HttpOnly": None}, - "rfc2109": False, - } - - badargs = set(kwargs) - set(result) - if badargs: - raise TypeError( - f"create_cookie() got unexpected keyword arguments: {list(badargs)}" - ) - - result.update(kwargs) - result["port_specified"] = bool(result["port"]) - result["domain_specified"] = bool(result["domain"]) - result["domain_initial_dot"] = result["domain"].startswith(".") - result["path_specified"] = bool(result["path"]) - - return cookielib.Cookie(**result) - - -def morsel_to_cookie(morsel): - """Convert a Morsel object into a Cookie containing the one k/v pair.""" - - expires = None - if morsel["max-age"]: - try: - expires = int(time.time() + int(morsel["max-age"])) - except ValueError: - raise TypeError(f"max-age: {morsel['max-age']} must be integer") - elif morsel["expires"]: - time_template = "%a, %d-%b-%Y %H:%M:%S GMT" - expires = calendar.timegm(time.strptime(morsel["expires"], time_template)) - return create_cookie( - comment=morsel["comment"], - comment_url=bool(morsel["comment"]), - discard=False, - domain=morsel["domain"], - expires=expires, - name=morsel.key, - path=morsel["path"], - port=None, - rest={"HttpOnly": morsel["httponly"]}, - rfc2109=False, - secure=bool(morsel["secure"]), - value=morsel.value, - version=morsel["version"] or 0, - ) - - -def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True): - """Returns a CookieJar from a key/value dictionary. - - :param cookie_dict: Dict of key/values to insert into CookieJar. - :param cookiejar: (optional) A cookiejar to add the cookies to. - :param overwrite: (optional) If False, will not replace cookies - already in the jar with new ones. - :rtype: CookieJar - """ - if cookiejar is None: - cookiejar = RequestsCookieJar() - - if cookie_dict is not None: - names_from_jar = [cookie.name for cookie in cookiejar] - for name in cookie_dict: - if overwrite or (name not in names_from_jar): - cookiejar.set_cookie(create_cookie(name, cookie_dict[name])) - - return cookiejar - - -def merge_cookies(cookiejar, cookies): - """Add cookies to cookiejar and returns a merged CookieJar. - - :param cookiejar: CookieJar object to add the cookies to. - :param cookies: Dictionary or CookieJar object to be added. - :rtype: CookieJar - """ - if not isinstance(cookiejar, cookielib.CookieJar): - raise ValueError("You can only merge into CookieJar") - - if isinstance(cookies, dict): - cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False) - elif isinstance(cookies, cookielib.CookieJar): - try: - cookiejar.update(cookies) - except AttributeError: - for cookie_in_jar in cookies: - cookiejar.set_cookie(cookie_in_jar) - - return cookiejar diff --git a/.venv/lib/python3.12/site-packages/requests/exceptions.py b/.venv/lib/python3.12/site-packages/requests/exceptions.py deleted file mode 100644 index 83986b4..0000000 --- a/.venv/lib/python3.12/site-packages/requests/exceptions.py +++ /dev/null @@ -1,151 +0,0 @@ -""" -requests.exceptions -~~~~~~~~~~~~~~~~~~~ - -This module contains the set of Requests' exceptions. -""" -from urllib3.exceptions import HTTPError as BaseHTTPError - -from .compat import JSONDecodeError as CompatJSONDecodeError - - -class RequestException(IOError): - """There was an ambiguous exception that occurred while handling your - request. - """ - - def __init__(self, *args, **kwargs): - """Initialize RequestException with `request` and `response` objects.""" - response = kwargs.pop("response", None) - self.response = response - self.request = kwargs.pop("request", None) - if response is not None and not self.request and hasattr(response, "request"): - self.request = self.response.request - super().__init__(*args, **kwargs) - - -class InvalidJSONError(RequestException): - """A JSON error occurred.""" - - -class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError): - """Couldn't decode the text into json""" - - def __init__(self, *args, **kwargs): - """ - Construct the JSONDecodeError instance first with all - args. Then use it's args to construct the IOError so that - the json specific args aren't used as IOError specific args - and the error message from JSONDecodeError is preserved. - """ - CompatJSONDecodeError.__init__(self, *args) - InvalidJSONError.__init__(self, *self.args, **kwargs) - - def __reduce__(self): - """ - The __reduce__ method called when pickling the object must - be the one from the JSONDecodeError (be it json/simplejson) - as it expects all the arguments for instantiation, not just - one like the IOError, and the MRO would by default call the - __reduce__ method from the IOError due to the inheritance order. - """ - return CompatJSONDecodeError.__reduce__(self) - - -class HTTPError(RequestException): - """An HTTP error occurred.""" - - -class ConnectionError(RequestException): - """A Connection error occurred.""" - - -class ProxyError(ConnectionError): - """A proxy error occurred.""" - - -class SSLError(ConnectionError): - """An SSL error occurred.""" - - -class Timeout(RequestException): - """The request timed out. - - Catching this error will catch both - :exc:`~requests.exceptions.ConnectTimeout` and - :exc:`~requests.exceptions.ReadTimeout` errors. - """ - - -class ConnectTimeout(ConnectionError, Timeout): - """The request timed out while trying to connect to the remote server. - - Requests that produced this error are safe to retry. - """ - - -class ReadTimeout(Timeout): - """The server did not send any data in the allotted amount of time.""" - - -class URLRequired(RequestException): - """A valid URL is required to make a request.""" - - -class TooManyRedirects(RequestException): - """Too many redirects.""" - - -class MissingSchema(RequestException, ValueError): - """The URL scheme (e.g. http or https) is missing.""" - - -class InvalidSchema(RequestException, ValueError): - """The URL scheme provided is either invalid or unsupported.""" - - -class InvalidURL(RequestException, ValueError): - """The URL provided was somehow invalid.""" - - -class InvalidHeader(RequestException, ValueError): - """The header value provided was somehow invalid.""" - - -class InvalidProxyURL(InvalidURL): - """The proxy URL provided is invalid.""" - - -class ChunkedEncodingError(RequestException): - """The server declared chunked encoding but sent an invalid chunk.""" - - -class ContentDecodingError(RequestException, BaseHTTPError): - """Failed to decode response content.""" - - -class StreamConsumedError(RequestException, TypeError): - """The content for this response was already consumed.""" - - -class RetryError(RequestException): - """Custom retries logic failed""" - - -class UnrewindableBodyError(RequestException): - """Requests encountered an error when trying to rewind a body.""" - - -# Warnings - - -class RequestsWarning(Warning): - """Base warning for Requests.""" - - -class FileModeWarning(RequestsWarning, DeprecationWarning): - """A file was opened in text mode, but Requests determined its binary length.""" - - -class RequestsDependencyWarning(RequestsWarning): - """An imported dependency doesn't match the expected version range.""" diff --git a/.venv/lib/python3.12/site-packages/requests/help.py b/.venv/lib/python3.12/site-packages/requests/help.py deleted file mode 100644 index 8fbcd65..0000000 --- a/.venv/lib/python3.12/site-packages/requests/help.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Module containing bug report helper(s).""" - -import json -import platform -import ssl -import sys - -import idna -import urllib3 - -from . import __version__ as requests_version - -try: - import charset_normalizer -except ImportError: - charset_normalizer = None - -try: - import chardet -except ImportError: - chardet = None - -try: - from urllib3.contrib import pyopenssl -except ImportError: - pyopenssl = None - OpenSSL = None - cryptography = None -else: - import cryptography - import OpenSSL - - -def _implementation(): - """Return a dict with the Python implementation and version. - - Provide both the name and the version of the Python implementation - currently running. For example, on CPython 3.10.3 it will return - {'name': 'CPython', 'version': '3.10.3'}. - - This function works best on CPython and PyPy: in particular, it probably - doesn't work for Jython or IronPython. Future investigation should be done - to work out the correct shape of the code for those platforms. - """ - implementation = platform.python_implementation() - - if implementation == "CPython": - implementation_version = platform.python_version() - elif implementation == "PyPy": - implementation_version = "{}.{}.{}".format( - sys.pypy_version_info.major, - sys.pypy_version_info.minor, - sys.pypy_version_info.micro, - ) - if sys.pypy_version_info.releaselevel != "final": - implementation_version = "".join( - [implementation_version, sys.pypy_version_info.releaselevel] - ) - elif implementation == "Jython": - implementation_version = platform.python_version() # Complete Guess - elif implementation == "IronPython": - implementation_version = platform.python_version() # Complete Guess - else: - implementation_version = "Unknown" - - return {"name": implementation, "version": implementation_version} - - -def info(): - """Generate information for a bug report.""" - try: - platform_info = { - "system": platform.system(), - "release": platform.release(), - } - except OSError: - platform_info = { - "system": "Unknown", - "release": "Unknown", - } - - implementation_info = _implementation() - urllib3_info = {"version": urllib3.__version__} - charset_normalizer_info = {"version": None} - chardet_info = {"version": None} - if charset_normalizer: - charset_normalizer_info = {"version": charset_normalizer.__version__} - if chardet: - chardet_info = {"version": chardet.__version__} - - pyopenssl_info = { - "version": None, - "openssl_version": "", - } - if OpenSSL: - pyopenssl_info = { - "version": OpenSSL.__version__, - "openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}", - } - cryptography_info = { - "version": getattr(cryptography, "__version__", ""), - } - idna_info = { - "version": getattr(idna, "__version__", ""), - } - - system_ssl = ssl.OPENSSL_VERSION_NUMBER - system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""} - - return { - "platform": platform_info, - "implementation": implementation_info, - "system_ssl": system_ssl_info, - "using_pyopenssl": pyopenssl is not None, - "using_charset_normalizer": chardet is None, - "pyOpenSSL": pyopenssl_info, - "urllib3": urllib3_info, - "chardet": chardet_info, - "charset_normalizer": charset_normalizer_info, - "cryptography": cryptography_info, - "idna": idna_info, - "requests": { - "version": requests_version, - }, - } - - -def main(): - """Pretty-print the bug information as JSON.""" - print(json.dumps(info(), sort_keys=True, indent=2)) - - -if __name__ == "__main__": - main() diff --git a/.venv/lib/python3.12/site-packages/requests/hooks.py b/.venv/lib/python3.12/site-packages/requests/hooks.py deleted file mode 100644 index d181ba2..0000000 --- a/.venv/lib/python3.12/site-packages/requests/hooks.py +++ /dev/null @@ -1,33 +0,0 @@ -""" -requests.hooks -~~~~~~~~~~~~~~ - -This module provides the capabilities for the Requests hooks system. - -Available hooks: - -``response``: - The response generated from a Request. -""" -HOOKS = ["response"] - - -def default_hooks(): - return {event: [] for event in HOOKS} - - -# TODO: response is the only one - - -def dispatch_hook(key, hooks, hook_data, **kwargs): - """Dispatches a hook dictionary on a given piece of data.""" - hooks = hooks or {} - hooks = hooks.get(key) - if hooks: - if hasattr(hooks, "__call__"): - hooks = [hooks] - for hook in hooks: - _hook_data = hook(hook_data, **kwargs) - if _hook_data is not None: - hook_data = _hook_data - return hook_data diff --git a/.venv/lib/python3.12/site-packages/requests/models.py b/.venv/lib/python3.12/site-packages/requests/models.py deleted file mode 100644 index 8f56ca7..0000000 --- a/.venv/lib/python3.12/site-packages/requests/models.py +++ /dev/null @@ -1,1037 +0,0 @@ -""" -requests.models -~~~~~~~~~~~~~~~ - -This module contains the primary objects that power Requests. -""" - -import datetime - -# Import encoding now, to avoid implicit import later. -# Implicit import within threads may cause LookupError when standard library is in a ZIP, -# such as in Embedded Python. See https://github.com/psf/requests/issues/3578. -import encodings.idna # noqa: F401 -from io import UnsupportedOperation - -from urllib3.exceptions import ( - DecodeError, - LocationParseError, - ProtocolError, - ReadTimeoutError, - SSLError, -) -from urllib3.fields import RequestField -from urllib3.filepost import encode_multipart_formdata -from urllib3.util import parse_url - -from ._internal_utils import to_native_string, unicode_is_ascii -from .auth import HTTPBasicAuth -from .compat import ( - Callable, - JSONDecodeError, - Mapping, - basestring, - builtin_str, - chardet, - cookielib, -) -from .compat import json as complexjson -from .compat import urlencode, urlsplit, urlunparse -from .cookies import _copy_cookie_jar, cookiejar_from_dict, get_cookie_header -from .exceptions import ( - ChunkedEncodingError, - ConnectionError, - ContentDecodingError, - HTTPError, - InvalidJSONError, - InvalidURL, -) -from .exceptions import JSONDecodeError as RequestsJSONDecodeError -from .exceptions import MissingSchema -from .exceptions import SSLError as RequestsSSLError -from .exceptions import StreamConsumedError -from .hooks import default_hooks -from .status_codes import codes -from .structures import CaseInsensitiveDict -from .utils import ( - check_header_validity, - get_auth_from_url, - guess_filename, - guess_json_utf, - iter_slices, - parse_header_links, - requote_uri, - stream_decode_response_unicode, - super_len, - to_key_val_list, -) - -#: The set of HTTP status codes that indicate an automatically -#: processable redirect. -REDIRECT_STATI = ( - codes.moved, # 301 - codes.found, # 302 - codes.other, # 303 - codes.temporary_redirect, # 307 - codes.permanent_redirect, # 308 -) - -DEFAULT_REDIRECT_LIMIT = 30 -CONTENT_CHUNK_SIZE = 10 * 1024 -ITER_CHUNK_SIZE = 512 - - -class RequestEncodingMixin: - @property - def path_url(self): - """Build the path URL to use.""" - - url = [] - - p = urlsplit(self.url) - - path = p.path - if not path: - path = "/" - - url.append(path) - - query = p.query - if query: - url.append("?") - url.append(query) - - return "".join(url) - - @staticmethod - def _encode_params(data): - """Encode parameters in a piece of data. - - Will successfully encode parameters when passed as a dict or a list of - 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary - if parameters are supplied as a dict. - """ - - if isinstance(data, (str, bytes)): - return data - elif hasattr(data, "read"): - return data - elif hasattr(data, "__iter__"): - result = [] - for k, vs in to_key_val_list(data): - if isinstance(vs, basestring) or not hasattr(vs, "__iter__"): - vs = [vs] - for v in vs: - if v is not None: - result.append( - ( - k.encode("utf-8") if isinstance(k, str) else k, - v.encode("utf-8") if isinstance(v, str) else v, - ) - ) - return urlencode(result, doseq=True) - else: - return data - - @staticmethod - def _encode_files(files, data): - """Build the body for a multipart/form-data request. - - Will successfully encode files when passed as a dict or a list of - tuples. Order is retained if data is a list of tuples but arbitrary - if parameters are supplied as a dict. - The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype) - or 4-tuples (filename, fileobj, contentype, custom_headers). - """ - if not files: - raise ValueError("Files must be provided.") - elif isinstance(data, basestring): - raise ValueError("Data must not be a string.") - - new_fields = [] - fields = to_key_val_list(data or {}) - files = to_key_val_list(files or {}) - - for field, val in fields: - if isinstance(val, basestring) or not hasattr(val, "__iter__"): - val = [val] - for v in val: - if v is not None: - # Don't call str() on bytestrings: in Py3 it all goes wrong. - if not isinstance(v, bytes): - v = str(v) - - new_fields.append( - ( - field.decode("utf-8") - if isinstance(field, bytes) - else field, - v.encode("utf-8") if isinstance(v, str) else v, - ) - ) - - for k, v in files: - # support for explicit filename - ft = None - fh = None - if isinstance(v, (tuple, list)): - if len(v) == 2: - fn, fp = v - elif len(v) == 3: - fn, fp, ft = v - else: - fn, fp, ft, fh = v - else: - fn = guess_filename(v) or k - fp = v - - if isinstance(fp, (str, bytes, bytearray)): - fdata = fp - elif hasattr(fp, "read"): - fdata = fp.read() - elif fp is None: - continue - else: - fdata = fp - - rf = RequestField(name=k, data=fdata, filename=fn, headers=fh) - rf.make_multipart(content_type=ft) - new_fields.append(rf) - - body, content_type = encode_multipart_formdata(new_fields) - - return body, content_type - - -class RequestHooksMixin: - def register_hook(self, event, hook): - """Properly register a hook.""" - - if event not in self.hooks: - raise ValueError(f'Unsupported event specified, with event name "{event}"') - - if isinstance(hook, Callable): - self.hooks[event].append(hook) - elif hasattr(hook, "__iter__"): - self.hooks[event].extend(h for h in hook if isinstance(h, Callable)) - - def deregister_hook(self, event, hook): - """Deregister a previously registered hook. - Returns True if the hook existed, False if not. - """ - - try: - self.hooks[event].remove(hook) - return True - except ValueError: - return False - - -class Request(RequestHooksMixin): - """A user-created :class:`Request ` object. - - Used to prepare a :class:`PreparedRequest `, which is sent to the server. - - :param method: HTTP method to use. - :param url: URL to send. - :param headers: dictionary of headers to send. - :param files: dictionary of {filename: fileobject} files to multipart upload. - :param data: the body to attach to the request. If a dictionary or - list of tuples ``[(key, value)]`` is provided, form-encoding will - take place. - :param json: json for the body to attach to the request (if files or data is not specified). - :param params: URL parameters to append to the URL. If a dictionary or - list of tuples ``[(key, value)]`` is provided, form-encoding will - take place. - :param auth: Auth handler or (user, pass) tuple. - :param cookies: dictionary or CookieJar of cookies to attach to this request. - :param hooks: dictionary of callback hooks, for internal usage. - - Usage:: - - >>> import requests - >>> req = requests.Request('GET', 'https://httpbin.org/get') - >>> req.prepare() - - """ - - def __init__( - self, - method=None, - url=None, - headers=None, - files=None, - data=None, - params=None, - auth=None, - cookies=None, - hooks=None, - json=None, - ): - # Default empty dicts for dict params. - data = [] if data is None else data - files = [] if files is None else files - headers = {} if headers is None else headers - params = {} if params is None else params - hooks = {} if hooks is None else hooks - - self.hooks = default_hooks() - for k, v in list(hooks.items()): - self.register_hook(event=k, hook=v) - - self.method = method - self.url = url - self.headers = headers - self.files = files - self.data = data - self.json = json - self.params = params - self.auth = auth - self.cookies = cookies - - def __repr__(self): - return f"" - - def prepare(self): - """Constructs a :class:`PreparedRequest ` for transmission and returns it.""" - p = PreparedRequest() - p.prepare( - method=self.method, - url=self.url, - headers=self.headers, - files=self.files, - data=self.data, - json=self.json, - params=self.params, - auth=self.auth, - cookies=self.cookies, - hooks=self.hooks, - ) - return p - - -class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): - """The fully mutable :class:`PreparedRequest ` object, - containing the exact bytes that will be sent to the server. - - Instances are generated from a :class:`Request ` object, and - should not be instantiated manually; doing so may produce undesirable - effects. - - Usage:: - - >>> import requests - >>> req = requests.Request('GET', 'https://httpbin.org/get') - >>> r = req.prepare() - >>> r - - - >>> s = requests.Session() - >>> s.send(r) - - """ - - def __init__(self): - #: HTTP verb to send to the server. - self.method = None - #: HTTP URL to send the request to. - self.url = None - #: dictionary of HTTP headers. - self.headers = None - # The `CookieJar` used to create the Cookie header will be stored here - # after prepare_cookies is called - self._cookies = None - #: request body to send to the server. - self.body = None - #: dictionary of callback hooks, for internal usage. - self.hooks = default_hooks() - #: integer denoting starting position of a readable file-like body. - self._body_position = None - - def prepare( - self, - method=None, - url=None, - headers=None, - files=None, - data=None, - params=None, - auth=None, - cookies=None, - hooks=None, - json=None, - ): - """Prepares the entire request with the given parameters.""" - - self.prepare_method(method) - self.prepare_url(url, params) - self.prepare_headers(headers) - self.prepare_cookies(cookies) - self.prepare_body(data, files, json) - self.prepare_auth(auth, url) - - # Note that prepare_auth must be last to enable authentication schemes - # such as OAuth to work on a fully prepared request. - - # This MUST go after prepare_auth. Authenticators could add a hook - self.prepare_hooks(hooks) - - def __repr__(self): - return f"" - - def copy(self): - p = PreparedRequest() - p.method = self.method - p.url = self.url - p.headers = self.headers.copy() if self.headers is not None else None - p._cookies = _copy_cookie_jar(self._cookies) - p.body = self.body - p.hooks = self.hooks - p._body_position = self._body_position - return p - - def prepare_method(self, method): - """Prepares the given HTTP method.""" - self.method = method - if self.method is not None: - self.method = to_native_string(self.method.upper()) - - @staticmethod - def _get_idna_encoded_host(host): - import idna - - try: - host = idna.encode(host, uts46=True).decode("utf-8") - except idna.IDNAError: - raise UnicodeError - return host - - def prepare_url(self, url, params): - """Prepares the given HTTP URL.""" - #: Accept objects that have string representations. - #: We're unable to blindly call unicode/str functions - #: as this will include the bytestring indicator (b'') - #: on python 3.x. - #: https://github.com/psf/requests/pull/2238 - if isinstance(url, bytes): - url = url.decode("utf8") - else: - url = str(url) - - # Remove leading whitespaces from url - url = url.lstrip() - - # Don't do any URL preparation for non-HTTP schemes like `mailto`, - # `data` etc to work around exceptions from `url_parse`, which - # handles RFC 3986 only. - if ":" in url and not url.lower().startswith("http"): - self.url = url - return - - # Support for unicode domain names and paths. - try: - scheme, auth, host, port, path, query, fragment = parse_url(url) - except LocationParseError as e: - raise InvalidURL(*e.args) - - if not scheme: - raise MissingSchema( - f"Invalid URL {url!r}: No scheme supplied. " - f"Perhaps you meant https://{url}?" - ) - - if not host: - raise InvalidURL(f"Invalid URL {url!r}: No host supplied") - - # In general, we want to try IDNA encoding the hostname if the string contains - # non-ASCII characters. This allows users to automatically get the correct IDNA - # behaviour. For strings containing only ASCII characters, we need to also verify - # it doesn't start with a wildcard (*), before allowing the unencoded hostname. - if not unicode_is_ascii(host): - try: - host = self._get_idna_encoded_host(host) - except UnicodeError: - raise InvalidURL("URL has an invalid label.") - elif host.startswith(("*", ".")): - raise InvalidURL("URL has an invalid label.") - - # Carefully reconstruct the network location - netloc = auth or "" - if netloc: - netloc += "@" - netloc += host - if port: - netloc += f":{port}" - - # Bare domains aren't valid URLs. - if not path: - path = "/" - - if isinstance(params, (str, bytes)): - params = to_native_string(params) - - enc_params = self._encode_params(params) - if enc_params: - if query: - query = f"{query}&{enc_params}" - else: - query = enc_params - - url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment])) - self.url = url - - def prepare_headers(self, headers): - """Prepares the given HTTP headers.""" - - self.headers = CaseInsensitiveDict() - if headers: - for header in headers.items(): - # Raise exception on invalid header value. - check_header_validity(header) - name, value = header - self.headers[to_native_string(name)] = value - - def prepare_body(self, data, files, json=None): - """Prepares the given HTTP body data.""" - - # Check if file, fo, generator, iterator. - # If not, run through normal process. - - # Nottin' on you. - body = None - content_type = None - - if not data and json is not None: - # urllib3 requires a bytes-like body. Python 2's json.dumps - # provides this natively, but Python 3 gives a Unicode string. - content_type = "application/json" - - try: - body = complexjson.dumps(json, allow_nan=False) - except ValueError as ve: - raise InvalidJSONError(ve, request=self) - - if not isinstance(body, bytes): - body = body.encode("utf-8") - - is_stream = all( - [ - hasattr(data, "__iter__"), - not isinstance(data, (basestring, list, tuple, Mapping)), - ] - ) - - if is_stream: - try: - length = super_len(data) - except (TypeError, AttributeError, UnsupportedOperation): - length = None - - body = data - - if getattr(body, "tell", None) is not None: - # Record the current file position before reading. - # This will allow us to rewind a file in the event - # of a redirect. - try: - self._body_position = body.tell() - except OSError: - # This differentiates from None, allowing us to catch - # a failed `tell()` later when trying to rewind the body - self._body_position = object() - - if files: - raise NotImplementedError( - "Streamed bodies and files are mutually exclusive." - ) - - if length: - self.headers["Content-Length"] = builtin_str(length) - else: - self.headers["Transfer-Encoding"] = "chunked" - else: - # Multi-part file uploads. - if files: - (body, content_type) = self._encode_files(files, data) - else: - if data: - body = self._encode_params(data) - if isinstance(data, basestring) or hasattr(data, "read"): - content_type = None - else: - content_type = "application/x-www-form-urlencoded" - - self.prepare_content_length(body) - - # Add content-type if it wasn't explicitly provided. - if content_type and ("content-type" not in self.headers): - self.headers["Content-Type"] = content_type - - self.body = body - - def prepare_content_length(self, body): - """Prepare Content-Length header based on request method and body""" - if body is not None: - length = super_len(body) - if length: - # If length exists, set it. Otherwise, we fallback - # to Transfer-Encoding: chunked. - self.headers["Content-Length"] = builtin_str(length) - elif ( - self.method not in ("GET", "HEAD") - and self.headers.get("Content-Length") is None - ): - # Set Content-Length to 0 for methods that can have a body - # but don't provide one. (i.e. not GET or HEAD) - self.headers["Content-Length"] = "0" - - def prepare_auth(self, auth, url=""): - """Prepares the given HTTP auth data.""" - - # If no Auth is explicitly provided, extract it from the URL first. - if auth is None: - url_auth = get_auth_from_url(self.url) - auth = url_auth if any(url_auth) else None - - if auth: - if isinstance(auth, tuple) and len(auth) == 2: - # special-case basic HTTP auth - auth = HTTPBasicAuth(*auth) - - # Allow auth to make its changes. - r = auth(self) - - # Update self to reflect the auth changes. - self.__dict__.update(r.__dict__) - - # Recompute Content-Length - self.prepare_content_length(self.body) - - def prepare_cookies(self, cookies): - """Prepares the given HTTP cookie data. - - This function eventually generates a ``Cookie`` header from the - given cookies using cookielib. Due to cookielib's design, the header - will not be regenerated if it already exists, meaning this function - can only be called once for the life of the - :class:`PreparedRequest ` object. Any subsequent calls - to ``prepare_cookies`` will have no actual effect, unless the "Cookie" - header is removed beforehand. - """ - if isinstance(cookies, cookielib.CookieJar): - self._cookies = cookies - else: - self._cookies = cookiejar_from_dict(cookies) - - cookie_header = get_cookie_header(self._cookies, self) - if cookie_header is not None: - self.headers["Cookie"] = cookie_header - - def prepare_hooks(self, hooks): - """Prepares the given hooks.""" - # hooks can be passed as None to the prepare method and to this - # method. To prevent iterating over None, simply use an empty list - # if hooks is False-y - hooks = hooks or [] - for event in hooks: - self.register_hook(event, hooks[event]) - - -class Response: - """The :class:`Response ` object, which contains a - server's response to an HTTP request. - """ - - __attrs__ = [ - "_content", - "status_code", - "headers", - "url", - "history", - "encoding", - "reason", - "cookies", - "elapsed", - "request", - ] - - def __init__(self): - self._content = False - self._content_consumed = False - self._next = None - - #: Integer Code of responded HTTP Status, e.g. 404 or 200. - self.status_code = None - - #: Case-insensitive Dictionary of Response Headers. - #: For example, ``headers['content-encoding']`` will return the - #: value of a ``'Content-Encoding'`` response header. - self.headers = CaseInsensitiveDict() - - #: File-like object representation of response (for advanced usage). - #: Use of ``raw`` requires that ``stream=True`` be set on the request. - #: This requirement does not apply for use internally to Requests. - self.raw = None - - #: Final URL location of Response. - self.url = None - - #: Encoding to decode with when accessing r.text. - self.encoding = None - - #: A list of :class:`Response ` objects from - #: the history of the Request. Any redirect responses will end - #: up here. The list is sorted from the oldest to the most recent request. - self.history = [] - - #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK". - self.reason = None - - #: A CookieJar of Cookies the server sent back. - self.cookies = cookiejar_from_dict({}) - - #: The amount of time elapsed between sending the request - #: and the arrival of the response (as a timedelta). - #: This property specifically measures the time taken between sending - #: the first byte of the request and finishing parsing the headers. It - #: is therefore unaffected by consuming the response content or the - #: value of the ``stream`` keyword argument. - self.elapsed = datetime.timedelta(0) - - #: The :class:`PreparedRequest ` object to which this - #: is a response. - self.request = None - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def __getstate__(self): - # Consume everything; accessing the content attribute makes - # sure the content has been fully read. - if not self._content_consumed: - self.content - - return {attr: getattr(self, attr, None) for attr in self.__attrs__} - - def __setstate__(self, state): - for name, value in state.items(): - setattr(self, name, value) - - # pickled objects do not have .raw - setattr(self, "_content_consumed", True) - setattr(self, "raw", None) - - def __repr__(self): - return f"" - - def __bool__(self): - """Returns True if :attr:`status_code` is less than 400. - - This attribute checks if the status code of the response is between - 400 and 600 to see if there was a client error or a server error. If - the status code, is between 200 and 400, this will return True. This - is **not** a check to see if the response code is ``200 OK``. - """ - return self.ok - - def __nonzero__(self): - """Returns True if :attr:`status_code` is less than 400. - - This attribute checks if the status code of the response is between - 400 and 600 to see if there was a client error or a server error. If - the status code, is between 200 and 400, this will return True. This - is **not** a check to see if the response code is ``200 OK``. - """ - return self.ok - - def __iter__(self): - """Allows you to use a response as an iterator.""" - return self.iter_content(128) - - @property - def ok(self): - """Returns True if :attr:`status_code` is less than 400, False if not. - - This attribute checks if the status code of the response is between - 400 and 600 to see if there was a client error or a server error. If - the status code is between 200 and 400, this will return True. This - is **not** a check to see if the response code is ``200 OK``. - """ - try: - self.raise_for_status() - except HTTPError: - return False - return True - - @property - def is_redirect(self): - """True if this Response is a well-formed HTTP redirect that could have - been processed automatically (by :meth:`Session.resolve_redirects`). - """ - return "location" in self.headers and self.status_code in REDIRECT_STATI - - @property - def is_permanent_redirect(self): - """True if this Response one of the permanent versions of redirect.""" - return "location" in self.headers and self.status_code in ( - codes.moved_permanently, - codes.permanent_redirect, - ) - - @property - def next(self): - """Returns a PreparedRequest for the next request in a redirect chain, if there is one.""" - return self._next - - @property - def apparent_encoding(self): - """The apparent encoding, provided by the charset_normalizer or chardet libraries.""" - if chardet is not None: - return chardet.detect(self.content)["encoding"] - else: - # If no character detection library is available, we'll fall back - # to a standard Python utf-8 str. - return "utf-8" - - def iter_content(self, chunk_size=1, decode_unicode=False): - """Iterates over the response data. When stream=True is set on the - request, this avoids reading the content at once into memory for - large responses. The chunk size is the number of bytes it should - read into memory. This is not necessarily the length of each item - returned as decoding can take place. - - chunk_size must be of type int or None. A value of None will - function differently depending on the value of `stream`. - stream=True will read data as it arrives in whatever size the - chunks are received. If stream=False, data is returned as - a single chunk. - - If decode_unicode is True, content will be decoded using the best - available encoding based on the response. - """ - - def generate(): - # Special case for urllib3. - if hasattr(self.raw, "stream"): - try: - yield from self.raw.stream(chunk_size, decode_content=True) - except ProtocolError as e: - raise ChunkedEncodingError(e) - except DecodeError as e: - raise ContentDecodingError(e) - except ReadTimeoutError as e: - raise ConnectionError(e) - except SSLError as e: - raise RequestsSSLError(e) - else: - # Standard file-like object. - while True: - chunk = self.raw.read(chunk_size) - if not chunk: - break - yield chunk - - self._content_consumed = True - - if self._content_consumed and isinstance(self._content, bool): - raise StreamConsumedError() - elif chunk_size is not None and not isinstance(chunk_size, int): - raise TypeError( - f"chunk_size must be an int, it is instead a {type(chunk_size)}." - ) - # simulate reading small chunks of the content - reused_chunks = iter_slices(self._content, chunk_size) - - stream_chunks = generate() - - chunks = reused_chunks if self._content_consumed else stream_chunks - - if decode_unicode: - chunks = stream_decode_response_unicode(chunks, self) - - return chunks - - def iter_lines( - self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None - ): - """Iterates over the response data, one line at a time. When - stream=True is set on the request, this avoids reading the - content at once into memory for large responses. - - .. note:: This method is not reentrant safe. - """ - - pending = None - - for chunk in self.iter_content( - chunk_size=chunk_size, decode_unicode=decode_unicode - ): - if pending is not None: - chunk = pending + chunk - - if delimiter: - lines = chunk.split(delimiter) - else: - lines = chunk.splitlines() - - if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]: - pending = lines.pop() - else: - pending = None - - yield from lines - - if pending is not None: - yield pending - - @property - def content(self): - """Content of the response, in bytes.""" - - if self._content is False: - # Read the contents. - if self._content_consumed: - raise RuntimeError("The content for this response was already consumed") - - if self.status_code == 0 or self.raw is None: - self._content = None - else: - self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" - - self._content_consumed = True - # don't need to release the connection; that's been handled by urllib3 - # since we exhausted the data. - return self._content - - @property - def text(self): - """Content of the response, in unicode. - - If Response.encoding is None, encoding will be guessed using - ``charset_normalizer`` or ``chardet``. - - The encoding of the response content is determined based solely on HTTP - headers, following RFC 2616 to the letter. If you can take advantage of - non-HTTP knowledge to make a better guess at the encoding, you should - set ``r.encoding`` appropriately before accessing this property. - """ - - # Try charset from content-type - content = None - encoding = self.encoding - - if not self.content: - return "" - - # Fallback to auto-detected encoding. - if self.encoding is None: - encoding = self.apparent_encoding - - # Decode unicode from given encoding. - try: - content = str(self.content, encoding, errors="replace") - except (LookupError, TypeError): - # A LookupError is raised if the encoding was not found which could - # indicate a misspelling or similar mistake. - # - # A TypeError can be raised if encoding is None - # - # So we try blindly encoding. - content = str(self.content, errors="replace") - - return content - - def json(self, **kwargs): - r"""Returns the json-encoded content of a response, if any. - - :param \*\*kwargs: Optional arguments that ``json.loads`` takes. - :raises requests.exceptions.JSONDecodeError: If the response body does not - contain valid json. - """ - - if not self.encoding and self.content and len(self.content) > 3: - # No encoding set. JSON RFC 4627 section 3 states we should expect - # UTF-8, -16 or -32. Detect which one to use; If the detection or - # decoding fails, fall back to `self.text` (using charset_normalizer to make - # a best guess). - encoding = guess_json_utf(self.content) - if encoding is not None: - try: - return complexjson.loads(self.content.decode(encoding), **kwargs) - except UnicodeDecodeError: - # Wrong UTF codec detected; usually because it's not UTF-8 - # but some other 8-bit codec. This is an RFC violation, - # and the server didn't bother to tell us what codec *was* - # used. - pass - except JSONDecodeError as e: - raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) - - try: - return complexjson.loads(self.text, **kwargs) - except JSONDecodeError as e: - # Catch JSON-related errors and raise as requests.JSONDecodeError - # This aliases json.JSONDecodeError and simplejson.JSONDecodeError - raise RequestsJSONDecodeError(e.msg, e.doc, e.pos) - - @property - def links(self): - """Returns the parsed header links of the response, if any.""" - - header = self.headers.get("link") - - resolved_links = {} - - if header: - links = parse_header_links(header) - - for link in links: - key = link.get("rel") or link.get("url") - resolved_links[key] = link - - return resolved_links - - def raise_for_status(self): - """Raises :class:`HTTPError`, if one occurred.""" - - http_error_msg = "" - if isinstance(self.reason, bytes): - # We attempt to decode utf-8 first because some servers - # choose to localize their reason strings. If the string - # isn't utf-8, we fall back to iso-8859-1 for all other - # encodings. (See PR #3538) - try: - reason = self.reason.decode("utf-8") - except UnicodeDecodeError: - reason = self.reason.decode("iso-8859-1") - else: - reason = self.reason - - if 400 <= self.status_code < 500: - http_error_msg = ( - f"{self.status_code} Client Error: {reason} for url: {self.url}" - ) - - elif 500 <= self.status_code < 600: - http_error_msg = ( - f"{self.status_code} Server Error: {reason} for url: {self.url}" - ) - - if http_error_msg: - raise HTTPError(http_error_msg, response=self) - - def close(self): - """Releases the connection back to the pool. Once this method has been - called the underlying ``raw`` object must not be accessed again. - - *Note: Should not normally need to be called explicitly.* - """ - if not self._content_consumed: - self.raw.close() - - release_conn = getattr(self.raw, "release_conn", None) - if release_conn is not None: - release_conn() diff --git a/.venv/lib/python3.12/site-packages/requests/packages.py b/.venv/lib/python3.12/site-packages/requests/packages.py deleted file mode 100644 index 5ab3d8e..0000000 --- a/.venv/lib/python3.12/site-packages/requests/packages.py +++ /dev/null @@ -1,23 +0,0 @@ -import sys - -from .compat import chardet - -# This code exists for backwards compatibility reasons. -# I don't like it either. Just look the other way. :) - -for package in ("urllib3", "idna"): - locals()[package] = __import__(package) - # This traversal is apparently necessary such that the identities are - # preserved (requests.packages.urllib3.* is urllib3.*) - for mod in list(sys.modules): - if mod == package or mod.startswith(f"{package}."): - sys.modules[f"requests.packages.{mod}"] = sys.modules[mod] - -if chardet is not None: - target = chardet.__name__ - for mod in list(sys.modules): - if mod == target or mod.startswith(f"{target}."): - imported_mod = sys.modules[mod] - sys.modules[f"requests.packages.{mod}"] = imported_mod - mod = mod.replace(target, "chardet") - sys.modules[f"requests.packages.{mod}"] = imported_mod diff --git a/.venv/lib/python3.12/site-packages/requests/sessions.py b/.venv/lib/python3.12/site-packages/requests/sessions.py deleted file mode 100644 index b387bc3..0000000 --- a/.venv/lib/python3.12/site-packages/requests/sessions.py +++ /dev/null @@ -1,831 +0,0 @@ -""" -requests.sessions -~~~~~~~~~~~~~~~~~ - -This module provides a Session object to manage and persist settings across -requests (cookies, auth, proxies). -""" -import os -import sys -import time -from collections import OrderedDict -from datetime import timedelta - -from ._internal_utils import to_native_string -from .adapters import HTTPAdapter -from .auth import _basic_auth_str -from .compat import Mapping, cookielib, urljoin, urlparse -from .cookies import ( - RequestsCookieJar, - cookiejar_from_dict, - extract_cookies_to_jar, - merge_cookies, -) -from .exceptions import ( - ChunkedEncodingError, - ContentDecodingError, - InvalidSchema, - TooManyRedirects, -) -from .hooks import default_hooks, dispatch_hook - -# formerly defined here, reexposed here for backward compatibility -from .models import ( # noqa: F401 - DEFAULT_REDIRECT_LIMIT, - REDIRECT_STATI, - PreparedRequest, - Request, -) -from .status_codes import codes -from .structures import CaseInsensitiveDict -from .utils import ( # noqa: F401 - DEFAULT_PORTS, - default_headers, - get_auth_from_url, - get_environ_proxies, - get_netrc_auth, - requote_uri, - resolve_proxies, - rewind_body, - should_bypass_proxies, - to_key_val_list, -) - -# Preferred clock, based on which one is more accurate on a given system. -if sys.platform == "win32": - preferred_clock = time.perf_counter -else: - preferred_clock = time.time - - -def merge_setting(request_setting, session_setting, dict_class=OrderedDict): - """Determines appropriate setting for a given request, taking into account - the explicit setting on that request, and the setting in the session. If a - setting is a dictionary, they will be merged together using `dict_class` - """ - - if session_setting is None: - return request_setting - - if request_setting is None: - return session_setting - - # Bypass if not a dictionary (e.g. verify) - if not ( - isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping) - ): - return request_setting - - merged_setting = dict_class(to_key_val_list(session_setting)) - merged_setting.update(to_key_val_list(request_setting)) - - # Remove keys that are set to None. Extract keys first to avoid altering - # the dictionary during iteration. - none_keys = [k for (k, v) in merged_setting.items() if v is None] - for key in none_keys: - del merged_setting[key] - - return merged_setting - - -def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): - """Properly merges both requests and session hooks. - - This is necessary because when request_hooks == {'response': []}, the - merge breaks Session hooks entirely. - """ - if session_hooks is None or session_hooks.get("response") == []: - return request_hooks - - if request_hooks is None or request_hooks.get("response") == []: - return session_hooks - - return merge_setting(request_hooks, session_hooks, dict_class) - - -class SessionRedirectMixin: - def get_redirect_target(self, resp): - """Receives a Response. Returns a redirect URI or ``None``""" - # Due to the nature of how requests processes redirects this method will - # be called at least once upon the original response and at least twice - # on each subsequent redirect response (if any). - # If a custom mixin is used to handle this logic, it may be advantageous - # to cache the redirect location onto the response object as a private - # attribute. - if resp.is_redirect: - location = resp.headers["location"] - # Currently the underlying http module on py3 decode headers - # in latin1, but empirical evidence suggests that latin1 is very - # rarely used with non-ASCII characters in HTTP headers. - # It is more likely to get UTF8 header rather than latin1. - # This causes incorrect handling of UTF8 encoded location headers. - # To solve this, we re-encode the location in latin1. - location = location.encode("latin1") - return to_native_string(location, "utf8") - return None - - def should_strip_auth(self, old_url, new_url): - """Decide whether Authorization header should be removed when redirecting""" - old_parsed = urlparse(old_url) - new_parsed = urlparse(new_url) - if old_parsed.hostname != new_parsed.hostname: - return True - # Special case: allow http -> https redirect when using the standard - # ports. This isn't specified by RFC 7235, but is kept to avoid - # breaking backwards compatibility with older versions of requests - # that allowed any redirects on the same host. - if ( - old_parsed.scheme == "http" - and old_parsed.port in (80, None) - and new_parsed.scheme == "https" - and new_parsed.port in (443, None) - ): - return False - - # Handle default port usage corresponding to scheme. - changed_port = old_parsed.port != new_parsed.port - changed_scheme = old_parsed.scheme != new_parsed.scheme - default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) - if ( - not changed_scheme - and old_parsed.port in default_port - and new_parsed.port in default_port - ): - return False - - # Standard case: root URI must match - return changed_port or changed_scheme - - def resolve_redirects( - self, - resp, - req, - stream=False, - timeout=None, - verify=True, - cert=None, - proxies=None, - yield_requests=False, - **adapter_kwargs, - ): - """Receives a Response. Returns a generator of Responses or Requests.""" - - hist = [] # keep track of history - - url = self.get_redirect_target(resp) - previous_fragment = urlparse(req.url).fragment - while url: - prepared_request = req.copy() - - # Update history and keep track of redirects. - # resp.history must ignore the original request in this loop - hist.append(resp) - resp.history = hist[1:] - - try: - resp.content # Consume socket so it can be released - except (ChunkedEncodingError, ContentDecodingError, RuntimeError): - resp.raw.read(decode_content=False) - - if len(resp.history) >= self.max_redirects: - raise TooManyRedirects( - f"Exceeded {self.max_redirects} redirects.", response=resp - ) - - # Release the connection back into the pool. - resp.close() - - # Handle redirection without scheme (see: RFC 1808 Section 4) - if url.startswith("//"): - parsed_rurl = urlparse(resp.url) - url = ":".join([to_native_string(parsed_rurl.scheme), url]) - - # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) - parsed = urlparse(url) - if parsed.fragment == "" and previous_fragment: - parsed = parsed._replace(fragment=previous_fragment) - elif parsed.fragment: - previous_fragment = parsed.fragment - url = parsed.geturl() - - # Facilitate relative 'location' headers, as allowed by RFC 7231. - # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') - # Compliant with RFC3986, we percent encode the url. - if not parsed.netloc: - url = urljoin(resp.url, requote_uri(url)) - else: - url = requote_uri(url) - - prepared_request.url = to_native_string(url) - - self.rebuild_method(prepared_request, resp) - - # https://github.com/psf/requests/issues/1084 - if resp.status_code not in ( - codes.temporary_redirect, - codes.permanent_redirect, - ): - # https://github.com/psf/requests/issues/3490 - purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding") - for header in purged_headers: - prepared_request.headers.pop(header, None) - prepared_request.body = None - - headers = prepared_request.headers - headers.pop("Cookie", None) - - # Extract any cookies sent on the response to the cookiejar - # in the new request. Because we've mutated our copied prepared - # request, use the old one that we haven't yet touched. - extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) - merge_cookies(prepared_request._cookies, self.cookies) - prepared_request.prepare_cookies(prepared_request._cookies) - - # Rebuild auth and proxy information. - proxies = self.rebuild_proxies(prepared_request, proxies) - self.rebuild_auth(prepared_request, resp) - - # A failed tell() sets `_body_position` to `object()`. This non-None - # value ensures `rewindable` will be True, allowing us to raise an - # UnrewindableBodyError, instead of hanging the connection. - rewindable = prepared_request._body_position is not None and ( - "Content-Length" in headers or "Transfer-Encoding" in headers - ) - - # Attempt to rewind consumed file-like object. - if rewindable: - rewind_body(prepared_request) - - # Override the original request. - req = prepared_request - - if yield_requests: - yield req - else: - resp = self.send( - req, - stream=stream, - timeout=timeout, - verify=verify, - cert=cert, - proxies=proxies, - allow_redirects=False, - **adapter_kwargs, - ) - - extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) - - # extract redirect url, if any, for the next loop - url = self.get_redirect_target(resp) - yield resp - - def rebuild_auth(self, prepared_request, response): - """When being redirected we may want to strip authentication from the - request to avoid leaking credentials. This method intelligently removes - and reapplies authentication where possible to avoid credential loss. - """ - headers = prepared_request.headers - url = prepared_request.url - - if "Authorization" in headers and self.should_strip_auth( - response.request.url, url - ): - # If we get redirected to a new host, we should strip out any - # authentication headers. - del headers["Authorization"] - - # .netrc might have more auth for us on our new host. - new_auth = get_netrc_auth(url) if self.trust_env else None - if new_auth is not None: - prepared_request.prepare_auth(new_auth) - - def rebuild_proxies(self, prepared_request, proxies): - """This method re-evaluates the proxy configuration by considering the - environment variables. If we are redirected to a URL covered by - NO_PROXY, we strip the proxy configuration. Otherwise, we set missing - proxy keys for this URL (in case they were stripped by a previous - redirect). - - This method also replaces the Proxy-Authorization header where - necessary. - - :rtype: dict - """ - headers = prepared_request.headers - scheme = urlparse(prepared_request.url).scheme - new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env) - - if "Proxy-Authorization" in headers: - del headers["Proxy-Authorization"] - - try: - username, password = get_auth_from_url(new_proxies[scheme]) - except KeyError: - username, password = None, None - - # urllib3 handles proxy authorization for us in the standard adapter. - # Avoid appending this to TLS tunneled requests where it may be leaked. - if not scheme.startswith("https") and username and password: - headers["Proxy-Authorization"] = _basic_auth_str(username, password) - - return new_proxies - - def rebuild_method(self, prepared_request, response): - """When being redirected we may want to change the method of the request - based on certain specs or browser behavior. - """ - method = prepared_request.method - - # https://tools.ietf.org/html/rfc7231#section-6.4.4 - if response.status_code == codes.see_other and method != "HEAD": - method = "GET" - - # Do what the browsers do, despite standards... - # First, turn 302s into GETs. - if response.status_code == codes.found and method != "HEAD": - method = "GET" - - # Second, if a POST is responded to with a 301, turn it into a GET. - # This bizarre behaviour is explained in Issue 1704. - if response.status_code == codes.moved and method == "POST": - method = "GET" - - prepared_request.method = method - - -class Session(SessionRedirectMixin): - """A Requests session. - - Provides cookie persistence, connection-pooling, and configuration. - - Basic Usage:: - - >>> import requests - >>> s = requests.Session() - >>> s.get('https://httpbin.org/get') - - - Or as a context manager:: - - >>> with requests.Session() as s: - ... s.get('https://httpbin.org/get') - - """ - - __attrs__ = [ - "headers", - "cookies", - "auth", - "proxies", - "hooks", - "params", - "verify", - "cert", - "adapters", - "stream", - "trust_env", - "max_redirects", - ] - - def __init__(self): - #: A case-insensitive dictionary of headers to be sent on each - #: :class:`Request ` sent from this - #: :class:`Session `. - self.headers = default_headers() - - #: Default Authentication tuple or object to attach to - #: :class:`Request `. - self.auth = None - - #: Dictionary mapping protocol or protocol and host to the URL of the proxy - #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to - #: be used on each :class:`Request `. - self.proxies = {} - - #: Event-handling hooks. - self.hooks = default_hooks() - - #: Dictionary of querystring data to attach to each - #: :class:`Request `. The dictionary values may be lists for - #: representing multivalued query parameters. - self.params = {} - - #: Stream response content default. - self.stream = False - - #: SSL Verification default. - #: Defaults to `True`, requiring requests to verify the TLS certificate at the - #: remote end. - #: If verify is set to `False`, requests will accept any TLS certificate - #: presented by the server, and will ignore hostname mismatches and/or - #: expired certificates, which will make your application vulnerable to - #: man-in-the-middle (MitM) attacks. - #: Only set this to `False` for testing. - self.verify = True - - #: SSL client certificate default, if String, path to ssl client - #: cert file (.pem). If Tuple, ('cert', 'key') pair. - self.cert = None - - #: Maximum number of redirects allowed. If the request exceeds this - #: limit, a :class:`TooManyRedirects` exception is raised. - #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is - #: 30. - self.max_redirects = DEFAULT_REDIRECT_LIMIT - - #: Trust environment settings for proxy configuration, default - #: authentication and similar. - self.trust_env = True - - #: A CookieJar containing all currently outstanding cookies set on this - #: session. By default it is a - #: :class:`RequestsCookieJar `, but - #: may be any other ``cookielib.CookieJar`` compatible object. - self.cookies = cookiejar_from_dict({}) - - # Default connection adapters. - self.adapters = OrderedDict() - self.mount("https://", HTTPAdapter()) - self.mount("http://", HTTPAdapter()) - - def __enter__(self): - return self - - def __exit__(self, *args): - self.close() - - def prepare_request(self, request): - """Constructs a :class:`PreparedRequest ` for - transmission and returns it. The :class:`PreparedRequest` has settings - merged from the :class:`Request ` instance and those of the - :class:`Session`. - - :param request: :class:`Request` instance to prepare with this - session's settings. - :rtype: requests.PreparedRequest - """ - cookies = request.cookies or {} - - # Bootstrap CookieJar. - if not isinstance(cookies, cookielib.CookieJar): - cookies = cookiejar_from_dict(cookies) - - # Merge with session cookies - merged_cookies = merge_cookies( - merge_cookies(RequestsCookieJar(), self.cookies), cookies - ) - - # Set environment's basic authentication if not explicitly set. - auth = request.auth - if self.trust_env and not auth and not self.auth: - auth = get_netrc_auth(request.url) - - p = PreparedRequest() - p.prepare( - method=request.method.upper(), - url=request.url, - files=request.files, - data=request.data, - json=request.json, - headers=merge_setting( - request.headers, self.headers, dict_class=CaseInsensitiveDict - ), - params=merge_setting(request.params, self.params), - auth=merge_setting(auth, self.auth), - cookies=merged_cookies, - hooks=merge_hooks(request.hooks, self.hooks), - ) - return p - - def request( - self, - method, - url, - params=None, - data=None, - headers=None, - cookies=None, - files=None, - auth=None, - timeout=None, - allow_redirects=True, - proxies=None, - hooks=None, - stream=None, - verify=None, - cert=None, - json=None, - ): - """Constructs a :class:`Request `, prepares it and sends it. - Returns :class:`Response ` object. - - :param method: method for the new :class:`Request` object. - :param url: URL for the new :class:`Request` object. - :param params: (optional) Dictionary or bytes to be sent in the query - string for the :class:`Request`. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json to send in the body of the - :class:`Request`. - :param headers: (optional) Dictionary of HTTP Headers to send with the - :class:`Request`. - :param cookies: (optional) Dict or CookieJar object to send with the - :class:`Request`. - :param files: (optional) Dictionary of ``'filename': file-like-objects`` - for multipart encoding upload. - :param auth: (optional) Auth tuple or callable to enable - Basic/Digest/Custom HTTP Auth. - :param timeout: (optional) How long to wait for the server to send - data before giving up, as a float, or a :ref:`(connect timeout, - read timeout) ` tuple. - :type timeout: float or tuple - :param allow_redirects: (optional) Set to True by default. - :type allow_redirects: bool - :param proxies: (optional) Dictionary mapping protocol or protocol and - hostname to the URL of the proxy. - :param hooks: (optional) Dictionary mapping hook name to one event or - list of events, event must be callable. - :param stream: (optional) whether to immediately download the response - content. Defaults to ``False``. - :param verify: (optional) Either a boolean, in which case it controls whether we verify - the server's TLS certificate, or a string, in which case it must be a path - to a CA bundle to use. Defaults to ``True``. When set to - ``False``, requests will accept any TLS certificate presented by - the server, and will ignore hostname mismatches and/or expired - certificates, which will make your application vulnerable to - man-in-the-middle (MitM) attacks. Setting verify to ``False`` - may be useful during local development or testing. - :param cert: (optional) if String, path to ssl client cert file (.pem). - If Tuple, ('cert', 'key') pair. - :rtype: requests.Response - """ - # Create the Request. - req = Request( - method=method.upper(), - url=url, - headers=headers, - files=files, - data=data or {}, - json=json, - params=params or {}, - auth=auth, - cookies=cookies, - hooks=hooks, - ) - prep = self.prepare_request(req) - - proxies = proxies or {} - - settings = self.merge_environment_settings( - prep.url, proxies, stream, verify, cert - ) - - # Send the request. - send_kwargs = { - "timeout": timeout, - "allow_redirects": allow_redirects, - } - send_kwargs.update(settings) - resp = self.send(prep, **send_kwargs) - - return resp - - def get(self, url, **kwargs): - r"""Sends a GET request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", True) - return self.request("GET", url, **kwargs) - - def options(self, url, **kwargs): - r"""Sends a OPTIONS request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", True) - return self.request("OPTIONS", url, **kwargs) - - def head(self, url, **kwargs): - r"""Sends a HEAD request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - kwargs.setdefault("allow_redirects", False) - return self.request("HEAD", url, **kwargs) - - def post(self, url, data=None, json=None, **kwargs): - r"""Sends a POST request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param json: (optional) json to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("POST", url, data=data, json=json, **kwargs) - - def put(self, url, data=None, **kwargs): - r"""Sends a PUT request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("PUT", url, data=data, **kwargs) - - def patch(self, url, data=None, **kwargs): - r"""Sends a PATCH request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param data: (optional) Dictionary, list of tuples, bytes, or file-like - object to send in the body of the :class:`Request`. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("PATCH", url, data=data, **kwargs) - - def delete(self, url, **kwargs): - r"""Sends a DELETE request. Returns :class:`Response` object. - - :param url: URL for the new :class:`Request` object. - :param \*\*kwargs: Optional arguments that ``request`` takes. - :rtype: requests.Response - """ - - return self.request("DELETE", url, **kwargs) - - def send(self, request, **kwargs): - """Send a given PreparedRequest. - - :rtype: requests.Response - """ - # Set defaults that the hooks can utilize to ensure they always have - # the correct parameters to reproduce the previous request. - kwargs.setdefault("stream", self.stream) - kwargs.setdefault("verify", self.verify) - kwargs.setdefault("cert", self.cert) - if "proxies" not in kwargs: - kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env) - - # It's possible that users might accidentally send a Request object. - # Guard against that specific failure case. - if isinstance(request, Request): - raise ValueError("You can only send PreparedRequests.") - - # Set up variables needed for resolve_redirects and dispatching of hooks - allow_redirects = kwargs.pop("allow_redirects", True) - stream = kwargs.get("stream") - hooks = request.hooks - - # Get the appropriate adapter to use - adapter = self.get_adapter(url=request.url) - - # Start time (approximately) of the request - start = preferred_clock() - - # Send the request - r = adapter.send(request, **kwargs) - - # Total elapsed time of the request (approximately) - elapsed = preferred_clock() - start - r.elapsed = timedelta(seconds=elapsed) - - # Response manipulation hooks - r = dispatch_hook("response", hooks, r, **kwargs) - - # Persist cookies - if r.history: - # If the hooks create history then we want those cookies too - for resp in r.history: - extract_cookies_to_jar(self.cookies, resp.request, resp.raw) - - extract_cookies_to_jar(self.cookies, request, r.raw) - - # Resolve redirects if allowed. - if allow_redirects: - # Redirect resolving generator. - gen = self.resolve_redirects(r, request, **kwargs) - history = [resp for resp in gen] - else: - history = [] - - # Shuffle things around if there's history. - if history: - # Insert the first (original) request at the start - history.insert(0, r) - # Get the last request made - r = history.pop() - r.history = history - - # If redirects aren't being followed, store the response on the Request for Response.next(). - if not allow_redirects: - try: - r._next = next( - self.resolve_redirects(r, request, yield_requests=True, **kwargs) - ) - except StopIteration: - pass - - if not stream: - r.content - - return r - - def merge_environment_settings(self, url, proxies, stream, verify, cert): - """ - Check the environment and merge it with some settings. - - :rtype: dict - """ - # Gather clues from the surrounding environment. - if self.trust_env: - # Set environment's proxies. - no_proxy = proxies.get("no_proxy") if proxies is not None else None - env_proxies = get_environ_proxies(url, no_proxy=no_proxy) - for k, v in env_proxies.items(): - proxies.setdefault(k, v) - - # Look for requests environment configuration - # and be compatible with cURL. - if verify is True or verify is None: - verify = ( - os.environ.get("REQUESTS_CA_BUNDLE") - or os.environ.get("CURL_CA_BUNDLE") - or verify - ) - - # Merge all the kwargs. - proxies = merge_setting(proxies, self.proxies) - stream = merge_setting(stream, self.stream) - verify = merge_setting(verify, self.verify) - cert = merge_setting(cert, self.cert) - - return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert} - - def get_adapter(self, url): - """ - Returns the appropriate connection adapter for the given URL. - - :rtype: requests.adapters.BaseAdapter - """ - for prefix, adapter in self.adapters.items(): - if url.lower().startswith(prefix.lower()): - return adapter - - # Nothing matches :-/ - raise InvalidSchema(f"No connection adapters were found for {url!r}") - - def close(self): - """Closes all adapters and as such the session""" - for v in self.adapters.values(): - v.close() - - def mount(self, prefix, adapter): - """Registers a connection adapter to a prefix. - - Adapters are sorted in descending order by prefix length. - """ - self.adapters[prefix] = adapter - keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] - - for key in keys_to_move: - self.adapters[key] = self.adapters.pop(key) - - def __getstate__(self): - state = {attr: getattr(self, attr, None) for attr in self.__attrs__} - return state - - def __setstate__(self, state): - for attr, value in state.items(): - setattr(self, attr, value) - - -def session(): - """ - Returns a :class:`Session` for context-management. - - .. deprecated:: 1.0.0 - - This method has been deprecated since version 1.0.0 and is only kept for - backwards compatibility. New code should use :class:`~requests.sessions.Session` - to create a session. This may be removed at a future date. - - :rtype: Session - """ - return Session() diff --git a/.venv/lib/python3.12/site-packages/requests/status_codes.py b/.venv/lib/python3.12/site-packages/requests/status_codes.py deleted file mode 100644 index c7945a2..0000000 --- a/.venv/lib/python3.12/site-packages/requests/status_codes.py +++ /dev/null @@ -1,128 +0,0 @@ -r""" -The ``codes`` object defines a mapping from common names for HTTP statuses -to their numerical codes, accessible either as attributes or as dictionary -items. - -Example:: - - >>> import requests - >>> requests.codes['temporary_redirect'] - 307 - >>> requests.codes.teapot - 418 - >>> requests.codes['\o/'] - 200 - -Some codes have multiple names, and both upper- and lower-case versions of -the names are allowed. For example, ``codes.ok``, ``codes.OK``, and -``codes.okay`` all correspond to the HTTP status code 200. -""" - -from .structures import LookupDict - -_codes = { - # Informational. - 100: ("continue",), - 101: ("switching_protocols",), - 102: ("processing", "early-hints"), - 103: ("checkpoint",), - 122: ("uri_too_long", "request_uri_too_long"), - 200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"), - 201: ("created",), - 202: ("accepted",), - 203: ("non_authoritative_info", "non_authoritative_information"), - 204: ("no_content",), - 205: ("reset_content", "reset"), - 206: ("partial_content", "partial"), - 207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"), - 208: ("already_reported",), - 226: ("im_used",), - # Redirection. - 300: ("multiple_choices",), - 301: ("moved_permanently", "moved", "\\o-"), - 302: ("found",), - 303: ("see_other", "other"), - 304: ("not_modified",), - 305: ("use_proxy",), - 306: ("switch_proxy",), - 307: ("temporary_redirect", "temporary_moved", "temporary"), - 308: ( - "permanent_redirect", - "resume_incomplete", - "resume", - ), # "resume" and "resume_incomplete" to be removed in 3.0 - # Client Error. - 400: ("bad_request", "bad"), - 401: ("unauthorized",), - 402: ("payment_required", "payment"), - 403: ("forbidden",), - 404: ("not_found", "-o-"), - 405: ("method_not_allowed", "not_allowed"), - 406: ("not_acceptable",), - 407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"), - 408: ("request_timeout", "timeout"), - 409: ("conflict",), - 410: ("gone",), - 411: ("length_required",), - 412: ("precondition_failed", "precondition"), - 413: ("request_entity_too_large", "content_too_large"), - 414: ("request_uri_too_large", "uri_too_long"), - 415: ("unsupported_media_type", "unsupported_media", "media_type"), - 416: ( - "requested_range_not_satisfiable", - "requested_range", - "range_not_satisfiable", - ), - 417: ("expectation_failed",), - 418: ("im_a_teapot", "teapot", "i_am_a_teapot"), - 421: ("misdirected_request",), - 422: ("unprocessable_entity", "unprocessable", "unprocessable_content"), - 423: ("locked",), - 424: ("failed_dependency", "dependency"), - 425: ("unordered_collection", "unordered", "too_early"), - 426: ("upgrade_required", "upgrade"), - 428: ("precondition_required", "precondition"), - 429: ("too_many_requests", "too_many"), - 431: ("header_fields_too_large", "fields_too_large"), - 444: ("no_response", "none"), - 449: ("retry_with", "retry"), - 450: ("blocked_by_windows_parental_controls", "parental_controls"), - 451: ("unavailable_for_legal_reasons", "legal_reasons"), - 499: ("client_closed_request",), - # Server Error. - 500: ("internal_server_error", "server_error", "/o\\", "✗"), - 501: ("not_implemented",), - 502: ("bad_gateway",), - 503: ("service_unavailable", "unavailable"), - 504: ("gateway_timeout",), - 505: ("http_version_not_supported", "http_version"), - 506: ("variant_also_negotiates",), - 507: ("insufficient_storage",), - 509: ("bandwidth_limit_exceeded", "bandwidth"), - 510: ("not_extended",), - 511: ("network_authentication_required", "network_auth", "network_authentication"), -} - -codes = LookupDict(name="status_codes") - - -def _init(): - for code, titles in _codes.items(): - for title in titles: - setattr(codes, title, code) - if not title.startswith(("\\", "/")): - setattr(codes, title.upper(), code) - - def doc(code): - names = ", ".join(f"``{n}``" for n in _codes[code]) - return "* %d: %s" % (code, names) - - global __doc__ - __doc__ = ( - __doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes)) - if __doc__ is not None - else None - ) - - -_init() diff --git a/.venv/lib/python3.12/site-packages/requests/structures.py b/.venv/lib/python3.12/site-packages/requests/structures.py deleted file mode 100644 index 188e13e..0000000 --- a/.venv/lib/python3.12/site-packages/requests/structures.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -requests.structures -~~~~~~~~~~~~~~~~~~~ - -Data structures that power Requests. -""" - -from collections import OrderedDict - -from .compat import Mapping, MutableMapping - - -class CaseInsensitiveDict(MutableMapping): - """A case-insensitive ``dict``-like object. - - Implements all methods and operations of - ``MutableMapping`` as well as dict's ``copy``. Also - provides ``lower_items``. - - All keys are expected to be strings. The structure remembers the - case of the last key to be set, and ``iter(instance)``, - ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()`` - will contain case-sensitive keys. However, querying and contains - testing is case insensitive:: - - cid = CaseInsensitiveDict() - cid['Accept'] = 'application/json' - cid['aCCEPT'] == 'application/json' # True - list(cid) == ['Accept'] # True - - For example, ``headers['content-encoding']`` will return the - value of a ``'Content-Encoding'`` response header, regardless - of how the header name was originally stored. - - If the constructor, ``.update``, or equality comparison - operations are given keys that have equal ``.lower()``s, the - behavior is undefined. - """ - - def __init__(self, data=None, **kwargs): - self._store = OrderedDict() - if data is None: - data = {} - self.update(data, **kwargs) - - def __setitem__(self, key, value): - # Use the lowercased key for lookups, but store the actual - # key alongside the value. - self._store[key.lower()] = (key, value) - - def __getitem__(self, key): - return self._store[key.lower()][1] - - def __delitem__(self, key): - del self._store[key.lower()] - - def __iter__(self): - return (casedkey for casedkey, mappedvalue in self._store.values()) - - def __len__(self): - return len(self._store) - - def lower_items(self): - """Like iteritems(), but with all lowercase keys.""" - return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()) - - def __eq__(self, other): - if isinstance(other, Mapping): - other = CaseInsensitiveDict(other) - else: - return NotImplemented - # Compare insensitively - return dict(self.lower_items()) == dict(other.lower_items()) - - # Copy is required - def copy(self): - return CaseInsensitiveDict(self._store.values()) - - def __repr__(self): - return str(dict(self.items())) - - -class LookupDict(dict): - """Dictionary lookup object.""" - - def __init__(self, name=None): - self.name = name - super().__init__() - - def __repr__(self): - return f"" - - def __getitem__(self, key): - # We allow fall-through here, so values default to None - - return self.__dict__.get(key, None) - - def get(self, key, default=None): - return self.__dict__.get(key, default) diff --git a/.venv/lib/python3.12/site-packages/requests/utils.py b/.venv/lib/python3.12/site-packages/requests/utils.py deleted file mode 100644 index ae6c42f..0000000 --- a/.venv/lib/python3.12/site-packages/requests/utils.py +++ /dev/null @@ -1,1096 +0,0 @@ -""" -requests.utils -~~~~~~~~~~~~~~ - -This module provides utility functions that are used within Requests -that are also useful for external consumption. -""" - -import codecs -import contextlib -import io -import os -import re -import socket -import struct -import sys -import tempfile -import warnings -import zipfile -from collections import OrderedDict - -from urllib3.util import make_headers, parse_url - -from . import certs -from .__version__ import __version__ - -# to_native_string is unused here, but imported here for backwards compatibility -from ._internal_utils import ( # noqa: F401 - _HEADER_VALIDATORS_BYTE, - _HEADER_VALIDATORS_STR, - HEADER_VALIDATORS, - to_native_string, -) -from .compat import ( - Mapping, - basestring, - bytes, - getproxies, - getproxies_environment, - integer_types, -) -from .compat import parse_http_list as _parse_list_header -from .compat import ( - proxy_bypass, - proxy_bypass_environment, - quote, - str, - unquote, - urlparse, - urlunparse, -) -from .cookies import cookiejar_from_dict -from .exceptions import ( - FileModeWarning, - InvalidHeader, - InvalidURL, - UnrewindableBodyError, -) -from .structures import CaseInsensitiveDict - -NETRC_FILES = (".netrc", "_netrc") - -DEFAULT_CA_BUNDLE_PATH = certs.where() - -DEFAULT_PORTS = {"http": 80, "https": 443} - -# Ensure that ', ' is used to preserve previous delimiter behavior. -DEFAULT_ACCEPT_ENCODING = ", ".join( - re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"]) -) - - -if sys.platform == "win32": - # provide a proxy_bypass version on Windows without DNS lookups - - def proxy_bypass_registry(host): - try: - import winreg - except ImportError: - return False - - try: - internetSettings = winreg.OpenKey( - winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Internet Settings", - ) - # ProxyEnable could be REG_SZ or REG_DWORD, normalizing it - proxyEnable = int(winreg.QueryValueEx(internetSettings, "ProxyEnable")[0]) - # ProxyOverride is almost always a string - proxyOverride = winreg.QueryValueEx(internetSettings, "ProxyOverride")[0] - except (OSError, ValueError): - return False - if not proxyEnable or not proxyOverride: - return False - - # make a check value list from the registry entry: replace the - # '' string by the localhost entry and the corresponding - # canonical entry. - proxyOverride = proxyOverride.split(";") - # filter out empty strings to avoid re.match return true in the following code. - proxyOverride = filter(None, proxyOverride) - # now check if we match one of the registry values. - for test in proxyOverride: - if test == "": - if "." not in host: - return True - test = test.replace(".", r"\.") # mask dots - test = test.replace("*", r".*") # change glob sequence - test = test.replace("?", r".") # change glob char - if re.match(test, host, re.I): - return True - return False - - def proxy_bypass(host): # noqa - """Return True, if the host should be bypassed. - - Checks proxy settings gathered from the environment, if specified, - or the registry. - """ - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_registry(host) - - -def dict_to_sequence(d): - """Returns an internal sequence dictionary update.""" - - if hasattr(d, "items"): - d = d.items() - - return d - - -def super_len(o): - total_length = None - current_position = 0 - - if isinstance(o, str): - o = o.encode("utf-8") - - if hasattr(o, "__len__"): - total_length = len(o) - - elif hasattr(o, "len"): - total_length = o.len - - elif hasattr(o, "fileno"): - try: - fileno = o.fileno() - except (io.UnsupportedOperation, AttributeError): - # AttributeError is a surprising exception, seeing as how we've just checked - # that `hasattr(o, 'fileno')`. It happens for objects obtained via - # `Tarfile.extractfile()`, per issue 5229. - pass - else: - total_length = os.fstat(fileno).st_size - - # Having used fstat to determine the file length, we need to - # confirm that this file was opened up in binary mode. - if "b" not in o.mode: - warnings.warn( - ( - "Requests has determined the content-length for this " - "request using the binary size of the file: however, the " - "file has been opened in text mode (i.e. without the 'b' " - "flag in the mode). This may lead to an incorrect " - "content-length. In Requests 3.0, support will be removed " - "for files in text mode." - ), - FileModeWarning, - ) - - if hasattr(o, "tell"): - try: - current_position = o.tell() - except OSError: - # This can happen in some weird situations, such as when the file - # is actually a special file descriptor like stdin. In this - # instance, we don't know what the length is, so set it to zero and - # let requests chunk it instead. - if total_length is not None: - current_position = total_length - else: - if hasattr(o, "seek") and total_length is None: - # StringIO and BytesIO have seek but no usable fileno - try: - # seek to end of file - o.seek(0, 2) - total_length = o.tell() - - # seek back to current position to support - # partially read file-like objects - o.seek(current_position or 0) - except OSError: - total_length = 0 - - if total_length is None: - total_length = 0 - - return max(0, total_length - current_position) - - -def get_netrc_auth(url, raise_errors=False): - """Returns the Requests tuple auth for a given url from netrc.""" - - netrc_file = os.environ.get("NETRC") - if netrc_file is not None: - netrc_locations = (netrc_file,) - else: - netrc_locations = (f"~/{f}" for f in NETRC_FILES) - - try: - from netrc import NetrcParseError, netrc - - netrc_path = None - - for f in netrc_locations: - try: - loc = os.path.expanduser(f) - except KeyError: - # os.path.expanduser can fail when $HOME is undefined and - # getpwuid fails. See https://bugs.python.org/issue20164 & - # https://github.com/psf/requests/issues/1846 - return - - if os.path.exists(loc): - netrc_path = loc - break - - # Abort early if there isn't one. - if netrc_path is None: - return - - ri = urlparse(url) - - # Strip port numbers from netloc. This weird `if...encode`` dance is - # used for Python 3.2, which doesn't support unicode literals. - splitstr = b":" - if isinstance(url, str): - splitstr = splitstr.decode("ascii") - host = ri.netloc.split(splitstr)[0] - - try: - _netrc = netrc(netrc_path).authenticators(host) - if _netrc: - # Return with login / password - login_i = 0 if _netrc[0] else 1 - return (_netrc[login_i], _netrc[2]) - except (NetrcParseError, OSError): - # If there was a parsing error or a permissions issue reading the file, - # we'll just skip netrc auth unless explicitly asked to raise errors. - if raise_errors: - raise - - # App Engine hackiness. - except (ImportError, AttributeError): - pass - - -def guess_filename(obj): - """Tries to guess the filename of the given object.""" - name = getattr(obj, "name", None) - if name and isinstance(name, basestring) and name[0] != "<" and name[-1] != ">": - return os.path.basename(name) - - -def extract_zipped_paths(path): - """Replace nonexistent paths that look like they refer to a member of a zip - archive with the location of an extracted copy of the target, or else - just return the provided path unchanged. - """ - if os.path.exists(path): - # this is already a valid path, no need to do anything further - return path - - # find the first valid part of the provided path and treat that as a zip archive - # assume the rest of the path is the name of a member in the archive - archive, member = os.path.split(path) - while archive and not os.path.exists(archive): - archive, prefix = os.path.split(archive) - if not prefix: - # If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split), - # we _can_ end up in an infinite loop on a rare corner case affecting a small number of users - break - member = "/".join([prefix, member]) - - if not zipfile.is_zipfile(archive): - return path - - zip_file = zipfile.ZipFile(archive) - if member not in zip_file.namelist(): - return path - - # we have a valid zip archive and a valid member of that archive - tmp = tempfile.gettempdir() - extracted_path = os.path.join(tmp, member.split("/")[-1]) - if not os.path.exists(extracted_path): - # use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition - with atomic_open(extracted_path) as file_handler: - file_handler.write(zip_file.read(member)) - return extracted_path - - -@contextlib.contextmanager -def atomic_open(filename): - """Write a file to the disk in an atomic fashion""" - tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename)) - try: - with os.fdopen(tmp_descriptor, "wb") as tmp_handler: - yield tmp_handler - os.replace(tmp_name, filename) - except BaseException: - os.remove(tmp_name) - raise - - -def from_key_val_list(value): - """Take an object and test to see if it can be represented as a - dictionary. Unless it can not be represented as such, return an - OrderedDict, e.g., - - :: - - >>> from_key_val_list([('key', 'val')]) - OrderedDict([('key', 'val')]) - >>> from_key_val_list('string') - Traceback (most recent call last): - ... - ValueError: cannot encode objects that are not 2-tuples - >>> from_key_val_list({'key': 'val'}) - OrderedDict([('key', 'val')]) - - :rtype: OrderedDict - """ - if value is None: - return None - - if isinstance(value, (str, bytes, bool, int)): - raise ValueError("cannot encode objects that are not 2-tuples") - - return OrderedDict(value) - - -def to_key_val_list(value): - """Take an object and test to see if it can be represented as a - dictionary. If it can be, return a list of tuples, e.g., - - :: - - >>> to_key_val_list([('key', 'val')]) - [('key', 'val')] - >>> to_key_val_list({'key': 'val'}) - [('key', 'val')] - >>> to_key_val_list('string') - Traceback (most recent call last): - ... - ValueError: cannot encode objects that are not 2-tuples - - :rtype: list - """ - if value is None: - return None - - if isinstance(value, (str, bytes, bool, int)): - raise ValueError("cannot encode objects that are not 2-tuples") - - if isinstance(value, Mapping): - value = value.items() - - return list(value) - - -# From mitsuhiko/werkzeug (used with permission). -def parse_list_header(value): - """Parse lists as described by RFC 2068 Section 2. - - In particular, parse comma-separated lists where the elements of - the list may include quoted-strings. A quoted-string could - contain a comma. A non-quoted string could have quotes in the - middle. Quotes are removed automatically after parsing. - - It basically works like :func:`parse_set_header` just that items - may appear multiple times and case sensitivity is preserved. - - The return value is a standard :class:`list`: - - >>> parse_list_header('token, "quoted value"') - ['token', 'quoted value'] - - To create a header from the :class:`list` again, use the - :func:`dump_header` function. - - :param value: a string with a list header. - :return: :class:`list` - :rtype: list - """ - result = [] - for item in _parse_list_header(value): - if item[:1] == item[-1:] == '"': - item = unquote_header_value(item[1:-1]) - result.append(item) - return result - - -# From mitsuhiko/werkzeug (used with permission). -def parse_dict_header(value): - """Parse lists of key, value pairs as described by RFC 2068 Section 2 and - convert them into a python dict: - - >>> d = parse_dict_header('foo="is a fish", bar="as well"') - >>> type(d) is dict - True - >>> sorted(d.items()) - [('bar', 'as well'), ('foo', 'is a fish')] - - If there is no value for a key it will be `None`: - - >>> parse_dict_header('key_without_value') - {'key_without_value': None} - - To create a header from the :class:`dict` again, use the - :func:`dump_header` function. - - :param value: a string with a dict header. - :return: :class:`dict` - :rtype: dict - """ - result = {} - for item in _parse_list_header(value): - if "=" not in item: - result[item] = None - continue - name, value = item.split("=", 1) - if value[:1] == value[-1:] == '"': - value = unquote_header_value(value[1:-1]) - result[name] = value - return result - - -# From mitsuhiko/werkzeug (used with permission). -def unquote_header_value(value, is_filename=False): - r"""Unquotes a header value. (Reversal of :func:`quote_header_value`). - This does not use the real unquoting but what browsers are actually - using for quoting. - - :param value: the header value to unquote. - :rtype: str - """ - if value and value[0] == value[-1] == '"': - # this is not the real unquoting, but fixing this so that the - # RFC is met will result in bugs with internet explorer and - # probably some other browsers as well. IE for example is - # uploading files with "C:\foo\bar.txt" as filename - value = value[1:-1] - - # if this is a filename and the starting characters look like - # a UNC path, then just return the value without quotes. Using the - # replace sequence below on a UNC path has the effect of turning - # the leading double slash into a single slash and then - # _fix_ie_filename() doesn't work correctly. See #458. - if not is_filename or value[:2] != "\\\\": - return value.replace("\\\\", "\\").replace('\\"', '"') - return value - - -def dict_from_cookiejar(cj): - """Returns a key/value dictionary from a CookieJar. - - :param cj: CookieJar object to extract cookies from. - :rtype: dict - """ - - cookie_dict = {cookie.name: cookie.value for cookie in cj} - return cookie_dict - - -def add_dict_to_cookiejar(cj, cookie_dict): - """Returns a CookieJar from a key/value dictionary. - - :param cj: CookieJar to insert cookies into. - :param cookie_dict: Dict of key/values to insert into CookieJar. - :rtype: CookieJar - """ - - return cookiejar_from_dict(cookie_dict, cj) - - -def get_encodings_from_content(content): - """Returns encodings from given content string. - - :param content: bytestring to extract encodings from. - """ - warnings.warn( - ( - "In requests 3.0, get_encodings_from_content will be removed. For " - "more information, please see the discussion on issue #2266. (This" - " warning should only appear once.)" - ), - DeprecationWarning, - ) - - charset_re = re.compile(r']', flags=re.I) - pragma_re = re.compile(r']', flags=re.I) - xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]') - - return ( - charset_re.findall(content) - + pragma_re.findall(content) - + xml_re.findall(content) - ) - - -def _parse_content_type_header(header): - """Returns content type and parameters from given header - - :param header: string - :return: tuple containing content type and dictionary of - parameters - """ - - tokens = header.split(";") - content_type, params = tokens[0].strip(), tokens[1:] - params_dict = {} - items_to_strip = "\"' " - - for param in params: - param = param.strip() - if param: - key, value = param, True - index_of_equals = param.find("=") - if index_of_equals != -1: - key = param[:index_of_equals].strip(items_to_strip) - value = param[index_of_equals + 1 :].strip(items_to_strip) - params_dict[key.lower()] = value - return content_type, params_dict - - -def get_encoding_from_headers(headers): - """Returns encodings from given HTTP Header Dict. - - :param headers: dictionary to extract encoding from. - :rtype: str - """ - - content_type = headers.get("content-type") - - if not content_type: - return None - - content_type, params = _parse_content_type_header(content_type) - - if "charset" in params: - return params["charset"].strip("'\"") - - if "text" in content_type: - return "ISO-8859-1" - - if "application/json" in content_type: - # Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset - return "utf-8" - - -def stream_decode_response_unicode(iterator, r): - """Stream decodes an iterator.""" - - if r.encoding is None: - yield from iterator - return - - decoder = codecs.getincrementaldecoder(r.encoding)(errors="replace") - for chunk in iterator: - rv = decoder.decode(chunk) - if rv: - yield rv - rv = decoder.decode(b"", final=True) - if rv: - yield rv - - -def iter_slices(string, slice_length): - """Iterate over slices of a string.""" - pos = 0 - if slice_length is None or slice_length <= 0: - slice_length = len(string) - while pos < len(string): - yield string[pos : pos + slice_length] - pos += slice_length - - -def get_unicode_from_response(r): - """Returns the requested content back in unicode. - - :param r: Response object to get unicode content from. - - Tried: - - 1. charset from content-type - 2. fall back and replace all unicode characters - - :rtype: str - """ - warnings.warn( - ( - "In requests 3.0, get_unicode_from_response will be removed. For " - "more information, please see the discussion on issue #2266. (This" - " warning should only appear once.)" - ), - DeprecationWarning, - ) - - tried_encodings = [] - - # Try charset from content-type - encoding = get_encoding_from_headers(r.headers) - - if encoding: - try: - return str(r.content, encoding) - except UnicodeError: - tried_encodings.append(encoding) - - # Fall back: - try: - return str(r.content, encoding, errors="replace") - except TypeError: - return r.content - - -# The unreserved URI characters (RFC 3986) -UNRESERVED_SET = frozenset( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~" -) - - -def unquote_unreserved(uri): - """Un-escape any percent-escape sequences in a URI that are unreserved - characters. This leaves all reserved, illegal and non-ASCII bytes encoded. - - :rtype: str - """ - parts = uri.split("%") - for i in range(1, len(parts)): - h = parts[i][0:2] - if len(h) == 2 and h.isalnum(): - try: - c = chr(int(h, 16)) - except ValueError: - raise InvalidURL(f"Invalid percent-escape sequence: '{h}'") - - if c in UNRESERVED_SET: - parts[i] = c + parts[i][2:] - else: - parts[i] = f"%{parts[i]}" - else: - parts[i] = f"%{parts[i]}" - return "".join(parts) - - -def requote_uri(uri): - """Re-quote the given URI. - - This function passes the given URI through an unquote/quote cycle to - ensure that it is fully and consistently quoted. - - :rtype: str - """ - safe_with_percent = "!#$%&'()*+,/:;=?@[]~" - safe_without_percent = "!#$&'()*+,/:;=?@[]~" - try: - # Unquote only the unreserved characters - # Then quote only illegal characters (do not quote reserved, - # unreserved, or '%') - return quote(unquote_unreserved(uri), safe=safe_with_percent) - except InvalidURL: - # We couldn't unquote the given URI, so let's try quoting it, but - # there may be unquoted '%'s in the URI. We need to make sure they're - # properly quoted so they do not cause issues elsewhere. - return quote(uri, safe=safe_without_percent) - - -def address_in_network(ip, net): - """This function allows you to check if an IP belongs to a network subnet - - Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24 - returns False if ip = 192.168.1.1 and net = 192.168.100.0/24 - - :rtype: bool - """ - ipaddr = struct.unpack("=L", socket.inet_aton(ip))[0] - netaddr, bits = net.split("/") - netmask = struct.unpack("=L", socket.inet_aton(dotted_netmask(int(bits))))[0] - network = struct.unpack("=L", socket.inet_aton(netaddr))[0] & netmask - return (ipaddr & netmask) == (network & netmask) - - -def dotted_netmask(mask): - """Converts mask from /xx format to xxx.xxx.xxx.xxx - - Example: if mask is 24 function returns 255.255.255.0 - - :rtype: str - """ - bits = 0xFFFFFFFF ^ (1 << 32 - mask) - 1 - return socket.inet_ntoa(struct.pack(">I", bits)) - - -def is_ipv4_address(string_ip): - """ - :rtype: bool - """ - try: - socket.inet_aton(string_ip) - except OSError: - return False - return True - - -def is_valid_cidr(string_network): - """ - Very simple check of the cidr format in no_proxy variable. - - :rtype: bool - """ - if string_network.count("/") == 1: - try: - mask = int(string_network.split("/")[1]) - except ValueError: - return False - - if mask < 1 or mask > 32: - return False - - try: - socket.inet_aton(string_network.split("/")[0]) - except OSError: - return False - else: - return False - return True - - -@contextlib.contextmanager -def set_environ(env_name, value): - """Set the environment variable 'env_name' to 'value' - - Save previous value, yield, and then restore the previous value stored in - the environment variable 'env_name'. - - If 'value' is None, do nothing""" - value_changed = value is not None - if value_changed: - old_value = os.environ.get(env_name) - os.environ[env_name] = value - try: - yield - finally: - if value_changed: - if old_value is None: - del os.environ[env_name] - else: - os.environ[env_name] = old_value - - -def should_bypass_proxies(url, no_proxy): - """ - Returns whether we should bypass proxies or not. - - :rtype: bool - """ - - # Prioritize lowercase environment variables over uppercase - # to keep a consistent behaviour with other http projects (curl, wget). - def get_proxy(key): - return os.environ.get(key) or os.environ.get(key.upper()) - - # First check whether no_proxy is defined. If it is, check that the URL - # we're getting isn't in the no_proxy list. - no_proxy_arg = no_proxy - if no_proxy is None: - no_proxy = get_proxy("no_proxy") - parsed = urlparse(url) - - if parsed.hostname is None: - # URLs don't always have hostnames, e.g. file:/// urls. - return True - - if no_proxy: - # We need to check whether we match here. We need to see if we match - # the end of the hostname, both with and without the port. - no_proxy = (host for host in no_proxy.replace(" ", "").split(",") if host) - - if is_ipv4_address(parsed.hostname): - for proxy_ip in no_proxy: - if is_valid_cidr(proxy_ip): - if address_in_network(parsed.hostname, proxy_ip): - return True - elif parsed.hostname == proxy_ip: - # If no_proxy ip was defined in plain IP notation instead of cidr notation & - # matches the IP of the index - return True - else: - host_with_port = parsed.hostname - if parsed.port: - host_with_port += f":{parsed.port}" - - for host in no_proxy: - if parsed.hostname.endswith(host) or host_with_port.endswith(host): - # The URL does match something in no_proxy, so we don't want - # to apply the proxies on this URL. - return True - - with set_environ("no_proxy", no_proxy_arg): - # parsed.hostname can be `None` in cases such as a file URI. - try: - bypass = proxy_bypass(parsed.hostname) - except (TypeError, socket.gaierror): - bypass = False - - if bypass: - return True - - return False - - -def get_environ_proxies(url, no_proxy=None): - """ - Return a dict of environment proxies. - - :rtype: dict - """ - if should_bypass_proxies(url, no_proxy=no_proxy): - return {} - else: - return getproxies() - - -def select_proxy(url, proxies): - """Select a proxy for the url, if applicable. - - :param url: The url being for the request - :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs - """ - proxies = proxies or {} - urlparts = urlparse(url) - if urlparts.hostname is None: - return proxies.get(urlparts.scheme, proxies.get("all")) - - proxy_keys = [ - urlparts.scheme + "://" + urlparts.hostname, - urlparts.scheme, - "all://" + urlparts.hostname, - "all", - ] - proxy = None - for proxy_key in proxy_keys: - if proxy_key in proxies: - proxy = proxies[proxy_key] - break - - return proxy - - -def resolve_proxies(request, proxies, trust_env=True): - """This method takes proxy information from a request and configuration - input to resolve a mapping of target proxies. This will consider settings - such as NO_PROXY to strip proxy configurations. - - :param request: Request or PreparedRequest - :param proxies: A dictionary of schemes or schemes and hosts to proxy URLs - :param trust_env: Boolean declaring whether to trust environment configs - - :rtype: dict - """ - proxies = proxies if proxies is not None else {} - url = request.url - scheme = urlparse(url).scheme - no_proxy = proxies.get("no_proxy") - new_proxies = proxies.copy() - - if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy): - environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) - - proxy = environ_proxies.get(scheme, environ_proxies.get("all")) - - if proxy: - new_proxies.setdefault(scheme, proxy) - return new_proxies - - -def default_user_agent(name="python-requests"): - """ - Return a string representing the default user agent. - - :rtype: str - """ - return f"{name}/{__version__}" - - -def default_headers(): - """ - :rtype: requests.structures.CaseInsensitiveDict - """ - return CaseInsensitiveDict( - { - "User-Agent": default_user_agent(), - "Accept-Encoding": DEFAULT_ACCEPT_ENCODING, - "Accept": "*/*", - "Connection": "keep-alive", - } - ) - - -def parse_header_links(value): - """Return a list of parsed link headers proxies. - - i.e. Link: ; rel=front; type="image/jpeg",; rel=back;type="image/jpeg" - - :rtype: list - """ - - links = [] - - replace_chars = " '\"" - - value = value.strip(replace_chars) - if not value: - return links - - for val in re.split(", *<", value): - try: - url, params = val.split(";", 1) - except ValueError: - url, params = val, "" - - link = {"url": url.strip("<> '\"")} - - for param in params.split(";"): - try: - key, value = param.split("=") - except ValueError: - break - - link[key.strip(replace_chars)] = value.strip(replace_chars) - - links.append(link) - - return links - - -# Null bytes; no need to recreate these on each call to guess_json_utf -_null = "\x00".encode("ascii") # encoding to ASCII for Python 3 -_null2 = _null * 2 -_null3 = _null * 3 - - -def guess_json_utf(data): - """ - :rtype: str - """ - # JSON always starts with two ASCII characters, so detection is as - # easy as counting the nulls and from their location and count - # determine the encoding. Also detect a BOM, if present. - sample = data[:4] - if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): - return "utf-32" # BOM included - if sample[:3] == codecs.BOM_UTF8: - return "utf-8-sig" # BOM included, MS style (discouraged) - if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): - return "utf-16" # BOM included - nullcount = sample.count(_null) - if nullcount == 0: - return "utf-8" - if nullcount == 2: - if sample[::2] == _null2: # 1st and 3rd are null - return "utf-16-be" - if sample[1::2] == _null2: # 2nd and 4th are null - return "utf-16-le" - # Did not detect 2 valid UTF-16 ascii-range characters - if nullcount == 3: - if sample[:3] == _null3: - return "utf-32-be" - if sample[1:] == _null3: - return "utf-32-le" - # Did not detect a valid UTF-32 ascii-range character - return None - - -def prepend_scheme_if_needed(url, new_scheme): - """Given a URL that may or may not have a scheme, prepend the given scheme. - Does not replace a present scheme with the one provided as an argument. - - :rtype: str - """ - parsed = parse_url(url) - scheme, auth, host, port, path, query, fragment = parsed - - # A defect in urlparse determines that there isn't a netloc present in some - # urls. We previously assumed parsing was overly cautious, and swapped the - # netloc and path. Due to a lack of tests on the original defect, this is - # maintained with parse_url for backwards compatibility. - netloc = parsed.netloc - if not netloc: - netloc, path = path, netloc - - if auth: - # parse_url doesn't provide the netloc with auth - # so we'll add it ourselves. - netloc = "@".join([auth, netloc]) - if scheme is None: - scheme = new_scheme - if path is None: - path = "" - - return urlunparse((scheme, netloc, path, "", query, fragment)) - - -def get_auth_from_url(url): - """Given a url with authentication components, extract them into a tuple of - username,password. - - :rtype: (str,str) - """ - parsed = urlparse(url) - - try: - auth = (unquote(parsed.username), unquote(parsed.password)) - except (AttributeError, TypeError): - auth = ("", "") - - return auth - - -def check_header_validity(header): - """Verifies that header parts don't contain leading whitespace - reserved characters, or return characters. - - :param header: tuple, in the format (name, value). - """ - name, value = header - _validate_header_part(header, name, 0) - _validate_header_part(header, value, 1) - - -def _validate_header_part(header, header_part, header_validator_index): - if isinstance(header_part, str): - validator = _HEADER_VALIDATORS_STR[header_validator_index] - elif isinstance(header_part, bytes): - validator = _HEADER_VALIDATORS_BYTE[header_validator_index] - else: - raise InvalidHeader( - f"Header part ({header_part!r}) from {header} " - f"must be of type str or bytes, not {type(header_part)}" - ) - - if not validator.match(header_part): - header_kind = "name" if header_validator_index == 0 else "value" - raise InvalidHeader( - f"Invalid leading whitespace, reserved character(s), or return " - f"character(s) in header {header_kind}: {header_part!r}" - ) - - -def urldefragauth(url): - """ - Given a url remove the fragment and the authentication part. - - :rtype: str - """ - scheme, netloc, path, params, query, fragment = urlparse(url) - - # see func:`prepend_scheme_if_needed` - if not netloc: - netloc, path = path, netloc - - netloc = netloc.rsplit("@", 1)[-1] - - return urlunparse((scheme, netloc, path, params, query, "")) - - -def rewind_body(prepared_request): - """Move file pointer back to its recorded starting position - so it can be read again on redirect. - """ - body_seek = getattr(prepared_request.body, "seek", None) - if body_seek is not None and isinstance( - prepared_request._body_position, integer_types - ): - try: - body_seek(prepared_request._body_position) - except OSError: - raise UnrewindableBodyError( - "An error occurred when rewinding request body for redirect." - ) - else: - raise UnrewindableBodyError("Unable to rewind request body for redirect.") diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/METADATA deleted file mode 100644 index baeb1dd..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/METADATA +++ /dev/null @@ -1,154 +0,0 @@ -Metadata-Version: 2.3 -Name: urllib3 -Version: 2.2.2 -Summary: HTTP library with thread-safe connection pooling, file post, and more. -Project-URL: Changelog, https://github.com/urllib3/urllib3/blob/main/CHANGES.rst -Project-URL: Documentation, https://urllib3.readthedocs.io -Project-URL: Code, https://github.com/urllib3/urllib3 -Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues -Author-email: Andrey Petrov -Maintainer-email: Seth Michael Larson , Quentin Pradet , Illia Volochii -License-File: LICENSE.txt -Keywords: filepost,http,httplib,https,pooling,ssl,threadsafe,urllib -Classifier: Environment :: Web Environment -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.12 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Internet :: WWW/HTTP -Classifier: Topic :: Software Development :: Libraries -Requires-Python: >=3.8 -Provides-Extra: brotli -Requires-Dist: brotli>=1.0.9; (platform_python_implementation == 'CPython') and extra == 'brotli' -Requires-Dist: brotlicffi>=0.8.0; (platform_python_implementation != 'CPython') and extra == 'brotli' -Provides-Extra: h2 -Requires-Dist: h2<5,>=4; extra == 'h2' -Provides-Extra: socks -Requires-Dist: pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks' -Provides-Extra: zstd -Requires-Dist: zstandard>=0.18.0; extra == 'zstd' -Description-Content-Type: text/markdown - -

- -![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg) - -

- -

- PyPI Version - Python Versions - Join our Discord - Coverage Status - Build Status on GitHub - Documentation Status
- OpenSSF Scorecard - SLSA 3 - CII Best Practices -

- -urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the -Python ecosystem already uses urllib3 and you should too. -urllib3 brings many critical features that are missing from the Python -standard libraries: - -- Thread safety. -- Connection pooling. -- Client-side SSL/TLS verification. -- File uploads with multipart encoding. -- Helpers for retrying requests and dealing with HTTP redirects. -- Support for gzip, deflate, brotli, and zstd encoding. -- Proxy support for HTTP and SOCKS. -- 100% test coverage. - -urllib3 is powerful and easy to use: - -```python3 ->>> import urllib3 ->>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt") ->>> resp.status -200 ->>> resp.data -b"User-agent: *\nDisallow: /deny\n" -``` - -## Installing - -urllib3 can be installed with [pip](https://pip.pypa.io): - -```bash -$ python -m pip install urllib3 -``` - -Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3): - -```bash -$ git clone https://github.com/urllib3/urllib3.git -$ cd urllib3 -$ pip install . -``` - - -## Documentation - -urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io). - - -## Community - -urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and -collaborating with other contributors. Drop by and say hello 👋 - - -## Contributing - -urllib3 happily accepts contributions. Please see our -[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html) -for some tips on getting started. - - -## Security Disclosures - -To report a security vulnerability, please use the -[Tidelift security contact](https://tidelift.com/security). -Tidelift will coordinate the fix and disclosure with maintainers. - - -## Maintainers - -- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson) -- [@pquentin](https://github.com/pquentin) (Quentin Pradet) -- [@illia-v](https://github.com/illia-v) (Illia Volochii) -- [@theacodes](https://github.com/theacodes) (Thea Flowers) -- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro) -- [@lukasa](https://github.com/lukasa) (Cory Benfield) -- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco) -- [@shazow](https://github.com/shazow) (Andrey Petrov) - -👋 - - -## Sponsorship - -If your company benefits from this library, please consider [sponsoring its -development](https://urllib3.readthedocs.io/en/latest/sponsors.html). - - -## For Enterprise - -Professional support for urllib3 is available as part of the [Tidelift -Subscription][1]. Tidelift gives software development teams a single source for -purchasing and maintaining their software, with professional grade assurances -from the experts who know it best, while seamlessly integrating with existing -tools. - -[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/RECORD deleted file mode 100644 index 7c9ac85..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/RECORD +++ /dev/null @@ -1,75 +0,0 @@ -urllib3-2.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -urllib3-2.2.2.dist-info/METADATA,sha256=1lFmEu2KSrvTuzjDf_UQxhN3hm5dHoUoUe8iX0XpK20,6434 -urllib3-2.2.2.dist-info/RECORD,, -urllib3-2.2.2.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87 -urllib3-2.2.2.dist-info/licenses/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093 -urllib3/__init__.py,sha256=JMo1tg1nIV1AeJ2vENC_Txfl0e5h6Gzl9DGVk1rWRbo,6979 -urllib3/__pycache__/__init__.cpython-312.pyc,, -urllib3/__pycache__/_base_connection.cpython-312.pyc,, -urllib3/__pycache__/_collections.cpython-312.pyc,, -urllib3/__pycache__/_request_methods.cpython-312.pyc,, -urllib3/__pycache__/_version.cpython-312.pyc,, -urllib3/__pycache__/connection.cpython-312.pyc,, -urllib3/__pycache__/connectionpool.cpython-312.pyc,, -urllib3/__pycache__/exceptions.cpython-312.pyc,, -urllib3/__pycache__/fields.cpython-312.pyc,, -urllib3/__pycache__/filepost.cpython-312.pyc,, -urllib3/__pycache__/http2.cpython-312.pyc,, -urllib3/__pycache__/poolmanager.cpython-312.pyc,, -urllib3/__pycache__/response.cpython-312.pyc,, -urllib3/_base_connection.py,sha256=tH0ZlOxWKika-S9NW-MuIlI_PLFQUUmSnoE_9MeywkM,5652 -urllib3/_collections.py,sha256=aGhh9zCYce3o-5FW9DPSUay6O9LjHx8z6T7wDtdhrkY,17370 -urllib3/_request_methods.py,sha256=ucEpHQyQf06b9o1RxKLkCpzGH0ct-v7X2xGpU6rmmlo,9984 -urllib3/_version.py,sha256=NcS90vVEriRXqG3ygXh7bjsUTZrTaLODQB3cjBtxkAw,98 -urllib3/connection.py,sha256=jSJmWOwEfzghV-P9F5dU4opSt6TFG6g6Y23JAaN2Bxg,34762 -urllib3/connectionpool.py,sha256=5fPIHypPwlbKBASMs6bESTEJVEGlsj9FOY9_GGU2GpM,43393 -urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -urllib3/contrib/__pycache__/__init__.cpython-312.pyc,, -urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc,, -urllib3/contrib/__pycache__/socks.cpython-312.pyc,, -urllib3/contrib/emscripten/__init__.py,sha256=u6KNgzjlFZbuAAXa_ybCR7gQ71VJESnF-IIdDA73brw,733 -urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc,, -urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc,, -urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc,, -urllib3/contrib/emscripten/__pycache__/request.cpython-312.pyc,, -urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc,, -urllib3/contrib/emscripten/connection.py,sha256=kaBe2tWt7Yy9vNUFRBV7CSyDnfhCYILGxju9KTZj8Sw,8755 -urllib3/contrib/emscripten/emscripten_fetch_worker.js,sha256=CDfYF_9CDobtx2lGidyJ1zjDEvwNT5F-dchmVWXDh0E,3655 -urllib3/contrib/emscripten/fetch.py,sha256=ymwJlHBBuw6WTpKgPHpdmmrNBxlsr75HqoD4Rn27YXk,14131 -urllib3/contrib/emscripten/request.py,sha256=mL28szy1KvE3NJhWor5jNmarp8gwplDU-7gwGZY5g0Q,566 -urllib3/contrib/emscripten/response.py,sha256=wEYWPHCL-JsgCtpCpfnWGYA1-DcjDGpFGqWCXZLwbHY,10017 -urllib3/contrib/pyopenssl.py,sha256=X31eCYGwB09EkAHX8RhDKC0X0Ki7d0cCVWoMJZUM5bQ,19161 -urllib3/contrib/socks.py,sha256=-iardc61GypsJzD6W6yuRS7KVCyfowcQrl_719H7lIM,7549 -urllib3/exceptions.py,sha256=RDaiudtR7rqbVKTKpLSgZBBtwaIqV7eZtervZV_mZag,9393 -urllib3/fields.py,sha256=8vi0PeRo_pE5chPmJA07LZtMkVls4UrBS1k2xM506jM,10843 -urllib3/filepost.py,sha256=-9qJT11cNGjO9dqnI20-oErZuTvNaM18xZZPCjZSbOE,2395 -urllib3/http2.py,sha256=ZfXCqoeKqMoC2JOn9ajMFwNgdyqJozj-6oBZNAUcqd0,7517 -urllib3/poolmanager.py,sha256=2_L2AjVDgoQ0qBmYbX9u9QqyU1u5J37zQbtv_-ueZQA,22913 -urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93 -urllib3/response.py,sha256=NS0rqwRmtwWtC_6XDqgDJN_uo-jEmBVzx0V6KCsHlwg,44801 -urllib3/util/__init__.py,sha256=-qeS0QceivazvBEKDNFCAI-6ACcdDOE4TMvo7SLNlAQ,1001 -urllib3/util/__pycache__/__init__.cpython-312.pyc,, -urllib3/util/__pycache__/connection.cpython-312.pyc,, -urllib3/util/__pycache__/proxy.cpython-312.pyc,, -urllib3/util/__pycache__/request.cpython-312.pyc,, -urllib3/util/__pycache__/response.cpython-312.pyc,, -urllib3/util/__pycache__/retry.cpython-312.pyc,, -urllib3/util/__pycache__/ssl_.cpython-312.pyc,, -urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc,, -urllib3/util/__pycache__/ssltransport.cpython-312.pyc,, -urllib3/util/__pycache__/timeout.cpython-312.pyc,, -urllib3/util/__pycache__/url.cpython-312.pyc,, -urllib3/util/__pycache__/util.cpython-312.pyc,, -urllib3/util/__pycache__/wait.cpython-312.pyc,, -urllib3/util/connection.py,sha256=QeUUEuNmhznpuKNPL-B0IVOkMdMCu8oJX62OC0Vpzug,4462 -urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148 -urllib3/util/request.py,sha256=QIZWYzQ6Y4MpJsD_kssVEIrWNu69ioUMuE6fjd4qhPM,8069 -urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374 -urllib3/util/retry.py,sha256=bj-2YUqblxLlv8THg5fxww-DM54XCbjgZXIQ71XioCY,18459 -urllib3/util/ssl_.py,sha256=DevjBk-8GNMgZjHzgCHHaxmWlXb-dKjcgymNd3ZT-ew,19107 -urllib3/util/ssl_match_hostname.py,sha256=gaWqixoYtQ_GKO8fcRGFj3VXeMoqyxQQuUTPgWeiL_M,5812 -urllib3/util/ssltransport.py,sha256=HHvjU0i-PjPThvNYQ8R1SEsseY0x07tPfVntiXg72n0,8990 -urllib3/util/timeout.py,sha256=4eT1FVeZZU7h7mYD1Jq2OXNe4fxekdNvhoWUkZusRpA,10346 -urllib3/util/url.py,sha256=wHORhp80RAXyTlAIkTqLFzSrkU7J34ZDxX-tN65MBZk,15213 -urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146 -urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423 diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/WHEEL deleted file mode 100644 index 516596c..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: hatchling 1.24.2 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/licenses/LICENSE.txt b/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/licenses/LICENSE.txt deleted file mode 100644 index e6183d0..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3-2.2.2.dist-info/licenses/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2008-2020 Andrey Petrov and contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.venv/lib/python3.12/site-packages/urllib3/__init__.py b/.venv/lib/python3.12/site-packages/urllib3/__init__.py deleted file mode 100644 index 3fe782c..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/__init__.py +++ /dev/null @@ -1,211 +0,0 @@ -""" -Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more -""" - -from __future__ import annotations - -# Set default logging handler to avoid "No handler found" warnings. -import logging -import sys -import typing -import warnings -from logging import NullHandler - -from . import exceptions -from ._base_connection import _TYPE_BODY -from ._collections import HTTPHeaderDict -from ._version import __version__ -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url -from .filepost import _TYPE_FIELDS, encode_multipart_formdata -from .poolmanager import PoolManager, ProxyManager, proxy_from_url -from .response import BaseHTTPResponse, HTTPResponse -from .util.request import make_headers -from .util.retry import Retry -from .util.timeout import Timeout - -# Ensure that Python is compiled with OpenSSL 1.1.1+ -# If the 'ssl' module isn't available at all that's -# fine, we only care if the module is available. -try: - import ssl -except ImportError: - pass -else: - if not ssl.OPENSSL_VERSION.startswith("OpenSSL "): # Defensive: - warnings.warn( - "urllib3 v2 only supports OpenSSL 1.1.1+, currently " - f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " - "See: https://github.com/urllib3/urllib3/issues/3020", - exceptions.NotOpenSSLWarning, - ) - elif ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive: - raise ImportError( - "urllib3 v2 only supports OpenSSL 1.1.1+, currently " - f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " - "See: https://github.com/urllib3/urllib3/issues/2168" - ) - -__author__ = "Andrey Petrov (andrey.petrov@shazow.net)" -__license__ = "MIT" -__version__ = __version__ - -__all__ = ( - "HTTPConnectionPool", - "HTTPHeaderDict", - "HTTPSConnectionPool", - "PoolManager", - "ProxyManager", - "HTTPResponse", - "Retry", - "Timeout", - "add_stderr_logger", - "connection_from_url", - "disable_warnings", - "encode_multipart_formdata", - "make_headers", - "proxy_from_url", - "request", - "BaseHTTPResponse", -) - -logging.getLogger(__name__).addHandler(NullHandler()) - - -def add_stderr_logger( - level: int = logging.DEBUG, -) -> logging.StreamHandler[typing.TextIO]: - """ - Helper for quickly adding a StreamHandler to the logger. Useful for - debugging. - - Returns the handler after adding it. - """ - # This method needs to be in this __init__.py to get the __name__ correct - # even if urllib3 is vendored within another package. - logger = logging.getLogger(__name__) - handler = logging.StreamHandler() - handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) - logger.addHandler(handler) - logger.setLevel(level) - logger.debug("Added a stderr logging handler to logger: %s", __name__) - return handler - - -# ... Clean up. -del NullHandler - - -# All warning filters *must* be appended unless you're really certain that they -# shouldn't be: otherwise, it's very hard for users to use most Python -# mechanisms to silence them. -# SecurityWarning's always go off by default. -warnings.simplefilter("always", exceptions.SecurityWarning, append=True) -# InsecurePlatformWarning's don't vary between requests, so we keep it default. -warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) - - -def disable_warnings(category: type[Warning] = exceptions.HTTPWarning) -> None: - """ - Helper for quickly disabling all urllib3 warnings. - """ - warnings.simplefilter("ignore", category) - - -_DEFAULT_POOL = PoolManager() - - -def request( - method: str, - url: str, - *, - body: _TYPE_BODY | None = None, - fields: _TYPE_FIELDS | None = None, - headers: typing.Mapping[str, str] | None = None, - preload_content: bool | None = True, - decode_content: bool | None = True, - redirect: bool | None = True, - retries: Retry | bool | int | None = None, - timeout: Timeout | float | int | None = 3, - json: typing.Any | None = None, -) -> BaseHTTPResponse: - """ - A convenience, top-level request method. It uses a module-global ``PoolManager`` instance. - Therefore, its side effects could be shared across dependencies relying on it. - To avoid side effects create a new ``PoolManager`` instance and use it instead. - The method does not accept low-level ``**urlopen_kw`` keyword arguments. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param body: - Data to send in the request body, either :class:`str`, :class:`bytes`, - an iterable of :class:`str`/:class:`bytes`, or a file-like object. - - :param fields: - Data to encode and send in the request body. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. - - :param bool preload_content: - If True, the response's body will be preloaded into memory. - - :param bool decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param redirect: - If True, automatically handle redirects (status codes 301, 302, - 303, 307, 308). Each redirect counts as a retry. Disabling retries - will disable redirect, too. - - :param retries: - Configure the number of retries to allow before raising a - :class:`~urllib3.exceptions.MaxRetryError` exception. - - If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a - :class:`~urllib3.util.retry.Retry` object for fine-grained control - over different types of retries. - Pass an integer number to retry connection errors that many times, - but no other types of errors. Pass zero to never retry. - - If ``False``, then retries are disabled and any exception is raised - immediately. Also, instead of raising a MaxRetryError on redirects, - the redirect response will be returned. - - :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. - - :param timeout: - If specified, overrides the default timeout for this one - request. It may be a float (in seconds) or an instance of - :class:`urllib3.util.Timeout`. - - :param json: - Data to encode and send as JSON with UTF-encoded in the request body. - The ``"Content-Type"`` header will be set to ``"application/json"`` - unless specified otherwise. - """ - - return _DEFAULT_POOL.request( - method, - url, - body=body, - fields=fields, - headers=headers, - preload_content=preload_content, - decode_content=decode_content, - redirect=redirect, - retries=retries, - timeout=timeout, - json=json, - ) - - -if sys.platform == "emscripten": - from .contrib.emscripten import inject_into_urllib3 # noqa: 401 - - inject_into_urllib3() diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 12a1fb8e12c2d61d77ae38037c95209771b25b34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7303 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 KiT)E)(Ebmrq3c`# diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/_base_connection.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/_base_connection.cpython-312.pyc deleted file mode 100644 index 348e82c2189da4fd010fbb71045a4a196199643f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6850 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 MtfrR;X>OqZ0#(sup#T5? diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/_collections.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/_collections.cpython-312.pyc deleted file mode 100644 index 0bd2cd5a5a4e832d6414691d0e37e0788a534de6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22702 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 UGb1D8QwF66e4>rqMeINk0O`d=?f?J) diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/connection.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/connection.cpython-312.pyc deleted file mode 100644 index a3a697692581b55080a366989cdb456a6e79fa0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32253 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/connectionpool.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/connectionpool.cpython-312.pyc deleted file mode 100644 index a943d5e707fe3d7f950fccbf31125b9e78354efd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39736 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/fields.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/fields.cpython-312.pyc deleted file mode 100644 index 4b2f3af35f47aca2e3685d9e75e6afd415cfe517..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12065 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/filepost.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/filepost.cpython-312.pyc deleted file mode 100644 index ff85f1f89e55e6033b1b01e011aa7cf96923ba11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3506 zcma)8U2Gf25#BrAk;fm2mMF=VB`X)(bxlhoYsq$E#dhIDG9pD*68{0EV^AmF(mMHg z$L=lVQlJaD2pS0rDh2^+8F65vPcDoCdFVr*i=r=mAt414E)F0d23o(#ae%gY>gwJ=Ts3+-hsE`yKDkeps9^ISuC4C?(sG=@q{Yif|kPL8$N7ysn;cn8Ps`nA>!r zggzO<;`CmJ@j?gO?iU!&2XK3K;O@fy49~RTK$SY0TYf+X;~)GnEma-Kh#FL-4~1l# z8dCj0+xu>N$h%M3nWMbD zJ9X(7?+vHUjSPui>YbqRy;jrnift?nt4N2NFnYhdb$gg zM@3tj)C|Q+Ynttw!oSkBOu^<&%l74oVyI@;_Nq8-s`!uSIWy+__Y+fQ7AHs!Q)13& z>8S*pO`O-PbRs^3jhTe5O(t>$I%OII@uU3-OQSfJQ_?q->)1-oN3W` zu3(3^*+(L<5zAfc5Oj0sI|+r_)`DG?VAs7j?~mRceLy}NUkUcE1^X+({*~advT%&F zfM~iN5cpv3Kfr?ZcNn~bxEBIeM6@2VX#oVbwMVsC(o#gzzHO86=?3N&XYfUIiz{+N z=vpsAxA;a<6nQGSE9?5iBL92t4qWb!_t1L4ryY23WvjHv{|?n8V*)C0fAZYqAPIi! z#BPLQqEIZGGnmL3ma|qo;LKdi=W-^YmaJR6P0NmQpA!Wq_V1|snzEU znUDoi&eFlO4kCQN1F6x5q}I}*dx!4F@5Wbs(e+5@ol~Ej`t;0#v=Ql?mo{2DzeR#LFfTq1wwLg| z4_1Rm)?0TkzVoEDXRY-}rS-_e;c{#LN~r&tAH@D^v8(!SMBt0uT!6A8IIYg-8aPlyk7@}D$?{i77Eh7iDY;Zgr_TWUKlki}3984{;!+OL zpu2C8cey)hE_$Y$T*(G?ZFN_22;`a@^X}-DK-n#GT2Y`ZOirsOcaXNbyrST0+qSCB zm8h@kdTHzHIyq#t?CiV7zHo}2y}L!r$A1%kNU9nG@#SNndRf#y=WoFkpWf}BvO!%h z7sU(@cyDQ|Iny$-}SiX)82PM_2`|4i!Kf7v`Xj>HmgV^b2`B-e0Zwk!%ba+=7 zv7;8l1^5^j*V&7ii&qQsW2V>-jU9rY6SW`o*YE9tf{X{rUno^0d*n+c^&gThN71;C6Q zRAYy6!L_rxd#Y}HilvXU&;h`9qflh=`Tu@2%CK{5aB4qMx3eAKJnSwU&-8j;)1y zDxscb`eo?gMrha4=)!1;u7>4npaKVKdz2M?Ep!_H#@o{L_BX`UG5 zC*X>xP<44|%M4lluebwR@AF0)|t6MAPwI~P0(pgLTK$`rNEeSw4<)S=pLW(*% zTAcEDb&|bT`(&8C7Ts+JQmITHEWoK0VU>jhp|iaVEz%ZYNKV)@$>ue)nr1912&+}q z!t2#d^F}^LUS&ekMfSvH#ag^<01DXRCH5w84rjM-Jyq)B@cWmC$KM(z492QfvnQF8 zz=S0TVQ)vrOHQ%=UOJUE)x3_+kmE4Hik$Ul=r%nZ$9;|Ve}lqbp{B2pANsFR$3M}@ z4S&ac$I`xqeWjZ#{#PDw761M@@5Y{<`{BFcbs_N4*zK`W$DQs^x|h@C1B2zB!Ai>; zfBB%?GExynpLzIBzKq&8y=Yg*QgNZUE_AXrfh>Na=_EhP?xI;I@j_*X^JE!^|19dOV diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/http2.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/http2.cpython-312.pyc deleted file mode 100644 index 4b26a70b2307733f8556b4b4497c6c2a2530d72d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11028 zcmdTqTW}lKb$5YX01E;jNP-k!AXg$O5t2xn)XP>S+ma|svSrGZl%%Gk!Vq>zf&u}0 zcPR-7*eDszn3~El)g&fer(`XuGOh4MB(@AIAPKkgvkhMI+soM|#K_)%& z=qEkr?gJo5JCA<#VsP%=`#AS?UiaMlZ(gsPKnR_^H+msR$fx*W28W(tRs#$n^F$;f z6DO0*2*Y677PpPqXlx&`)0iD$Y0Ql{XzVm%ml5+LZkk_3V|&~)=^gO`E>`5?zRBv5 zYMOS${gX8#H8kyv*G|@r)Gxy@pYSbd!xX@S@eacjyJs{ITRg&3_W zl1L=gh#E^KlwtVxAB`x|$@Ax*KPxFykSBeHLxch!W}cKHq9hN-qAKK6W6rr_$wWem zVtdG{tH^pjnT%`9`KUf>9oqDWS$!N5^F$&ej7VTZ7}yMlXoCT=qEoa(%<*LXPjrba zblOQ}A?i)`|N`3N_q~i8O$NQp5QR;gxrbPRCFH4Ebeeu}EzNwTto=oiN z?H}k;x2hF;u;6RY)^-041Nmn=DKRq91u!8GxjTAkE_m(F4 zp{s>XZ8&6;>!5p9mljM9DaE^5;sbm z;joyDhQo3_w3M3>2mtO9Iu*JmauwzxXoSCV2*7pnz~ zyU0OYbdcKZMJJ|Qq-Ix<$CR76eQQ;a(h(!NDEzHp&;zsjI%K_4Ce(S*XB8s6YL<*L zB6Hmf;@)G3doRtiU-#%DU-YeSgI{7bh017a8GG$+J7ty3$yAng`) zd_3TT-mWquo^vErr|YYMlD>+TvmFN`hj}rpKZ3-M8Oo~VjBL#Ho)Lopvr0u^(UNc* z!!HZ7$Yku7iNcCDjO8j7>52x?e$T-0eMZFHGp6Dk#%aQ%M;mIFbK{cG=aqQdK)QnyqSG^u4z|D?EO`wrgqo>0Iq|OP=Tc+vUCC z%(@yE0t;%^)w_5(=jzpEm$hxs2@VamO5gN(qQa*hHq&GiSi2&K%sV!78)q2RX%?4Z za+!Ihc*5H5QeK96$07(5BTy|nd>+iIs%Sh0dk|K@1c6V7zVji6W*Z-np8zC4+qjxQ z4%0HV;%{Sd@ z%Gd17*6e&wU8>oct2s35xL@7+le2GKc;iAocpw`*@WJJ!;DKClC|7-A$#r7mCZMrS zH{l~lAm^19?}iPy0hZn5y021(`^XHFVJ58Q2Si3#*Yfw?iV0M$6~V-0ZVo+4z;WUp zDnJOxqaCClwC)ufLtcPy%(-fRfm~s9Rqd1qpq}P5Hbq7&U#mllBNkC5%9h4nu?iYK zP6e#JNIb52#E2RRtI05CfW}AVt~m|EtQ*SbUTRE&jJ7ryN_IQ zqk7rZwBoIK?ZS-vf)w47s5@X>Al066fgEFsuPE(4oh60R9; zGrlu7qe9e@iA}Z`#O_uQznhl5Nklu8(32#LuPj_;6xc?QeUtnRJL5>Se`R~HWq(I| z`-}tXc*>9)$%*QXc_Jq|fRxdUs|ea1Yw9>(;h&mqPbtdqH zW$EQO`Ti(vZb`Y9X}MaPmdFh^-1}#a) znQpG+u2R{v&`$Xe0HDEq#8<2Hm7n(kUkM$vgUg;^p{*<5b~M{|G}ksbJ9zWVN_Fj= zI3K$e%T~7++S(V{xB1)r!gua$yL0~C7w*3B>z>0ObS&+Da;f#HTl`9U$6Kj4QVW;v z)ZE$o?h|*P`1P(o`M|!k^UzZ3;W>WASPASb)VD7-ydAt9{M+`W zK!2fr+v2Hw=iY4R-dyMYrNDtgeQ-f}EB!|L&CF6@cai66YS-*eukT?UjQAk|@BqOh z1?l(IqX&=q?f>lOjx{;g9SL!*;Loi76cWJPwn1_M)?o&j3vw#MW(OW1`f_n$G!Yq- zOutULXY60!cGObR47vFni`JBFIL%F~qdkwqDMP0r^cf^X8Mja4;>jzLY#u-yx<#Zp z;OGHVs&RVELd_C&@IV22y>!zi#Gx07&_p>20M1zTEuRxR@0&di61IHL3N>5igSUbU zS8_Ew@-;o#nx0%u-+T4%xBjg4gRxxy(31be7j~Q1_rMOVzeb%Fp_{Lfv#2mpYg7c- zFq~IKfLz0;R$f4@9|H%dW@f@NZcDXHsdbf^vB@ofXA@bwnYzuCZJCH(yWdOEhU{Q3 z*@4%oISb+f)TCj}x?yt-F#_^|>Ru1(Q`6kl1q+gbd-OK*Iht+`eAp!6Jfx)&%8jkF? z|6(_HWZ!zW#l3?+vx>)7pcO`8Ql#Y1DKX#o8DtpY9LxtqrGWMbIL6kWHc}Ee1?7Ig z1ZQY!qOe%6+`L2|PC`rvD)|F*(dskK=D(?~D|l+}`?fA^8_fBRFS(9yBy-E0_;;89 zx_H6-woH4I=w?vu8}=EXw*r_Mc9EH3Gwd9r%xq5bo5>r|B1#}-dp77ZEq1bUV$f{E9f2XnJ4ZJXwHZp<}% zf^irBfH9+&tsfkqe&8Rok#XoX_4BB*Gi(NGfQ?>O!mqdm9b ztaS4!{j3yn4iCkC`&$qOx?Kr(6Vsq_BT6(DI}dJRheC7Y7Xi0cRc1HJFF{VIS>rFJ zR4F3Mkrb8kRFG?|EJZJC9)MEpvJ^(aq`6~CECJXOQAs|C*c=i#ja5bSn|B{)_3EOG z%1`gn4o-N@AxdaxYW9dIYMwzrX4S+&XiebROC+zvC2>pw)0Xxr>YX->f@y(gJ#bma zp+Yz=CC0$>J%a73&QZN}9G(2S7D5H2anX2Eku)zAo95VLG$S+)C#!@!`f*-_EV>N3 zvidgG@UH7$Mm?l)bfR=Z@;9)}w-BJ(qh=l&HD=IF-zqT4W#d99-BKyO%L-G7<1G-x z)O}OifZl+{zx>WDYbm`JelIA!rncEv{V2&`Rg7x1YWJY@xgN zUGH7*dt2H8})wTTr4_Maxq^TW9EcRbs7d^tE& zXcZQZzx~YZXR@um3xkE$j(qFkZ0q5I(3KYsWraf@NdK7pc@nS$+tys`5DaGf20scO zFNAvj`k7)EOsLpRI(Owe4rDtHiVVXtxK-00Kw@}sB0<&TB!YiKC}BZ4Rh)bTh@5a?=BLKcRxJF zV0!}Kk%9{PjpJ1(n(hCxeJ{XYHFGCAoa^o`EXT|s?5E)pg;tdToi99Ee`rlqgmTfb znTc+eTh`$wR_3M()>6b9-7p}IzQ%n69s_HP)NzYah6d`mwfqMvE)Wej;1KgF2x4=r zx}!(6?AT5s+tJ~3^Y|6R-BalXy7^4G8>mV*KK+!b%e+1nW~atmw+k`~ujEplFBq(Zec4K>$2bz*HeWV2(D}=MEor+J5QekJfR&^xGigm$eM0 z>+G0r;EuLCbsw9Hx&|~m8~~_)AngWuZC&~KhZN9mSI!siI^EWzae5e}Xq;PV8B@f^WP@0eWRt> zP~qJMw;+1A@vcW?L>q`BE@Ovl07E39od)6xYFH5olH)ZR zWCR@6i7~^)R_ai1{}Lj31py@_oQQ5n7_ta)rW@ATG1k@3&!J2?3;=`?=w04NFK~3F zwi0M4v~;Z0Z7H;b3XL7bI>I-t5jXD!x@u?!+2Sq;TW8r1UBQhfMWklB19(CDRSpsJ zI-QYcp_Ys%qO$9A%7y9L_2Whx@;|X29^|f(6}Tf$-$*aHnl=srD(FK%Qk)0(6BSnS z4*06%G^0a=TZ;g29ue#xa;3|?Y&9J(G)>ozs8N_!eqtozE4Bny0sD)&7TWK6VhEX(!ZdGZ#ZZg3|w0@-at}~%%v2n<* zH3*>0!lIMaYRm#8tX@i2elyEFgUT%S?E&QlXK4EPP|*dBwNjd|WVubD`9+s>9-BGw z(akr(Lx8$jbDogk-A*j3zm%brkK89CUCOw06^s1Hbx41X^Q4UCGinzFr5RYS4D$)u z_9^jyOgtYGFHHnKCjL)J)5oOo6VmfPr27-H;}g=ivSs@c39PWS*M?ph{@(E1o*cX7 z8nfnTWLoEj=7(<$FZRR1Z;inB+WsRprg1L0MlfDG$qI~jDbTq_AYL6}IHq^a&oX-{ GC;TrK&>wOD diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/poolmanager.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/poolmanager.cpython-312.pyc deleted file mode 100644 index 16051e4e892c87a3574abd3f2b0457a29123f17c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24067 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

P+@C!S@5@H}-dy|M2gl!U z%C{fY+zB5C>Q}C>e0lZSJtY@7@c@3D6Cc0e+H9ihw?BC%* zGvJavU;Z&AY%10X`WYn*`k0_2l>ARh3Y7dklBFK9-ow5QNdRei)B69S()%c3p>mc* zm`vmHPbm3oO0FXTEoW0bNEud7h|euG>*$~HF~}cMX_cyb%G#F~={;i~>=V<+k$+0b zVXl7UrRZ+|hsz-mr7#usZCT+8(iYiNu0WLj=u zxs7C7Zerk@e2KNU%IRg+UfgwSFF~5MmmtmBOAy(Ne_2UT4|jq;1H)~=*~|>LbnkwJ zJ#TutpTNG|TPM{#f0Qw|wsIaAR3vg-Vn@q@9lA*2DfTv~dv9BrT z8@kuK?i*UG1+?xM(l$VaOdNfw5^G8N9MKVkYkpz7yJQ!NY*~rarfe--)}1l>9Yihe zARLy7QR640=$Fq(cUE2S7GHsJN3GCSt^7?Y;G||1nW(ku2SGptNr=VsX+Cj341n=C zPU*r#sCmY<-?pe`VR|t2>8^1_CiPbJT2c$s3e+kqgEV+e2O+JC=^LsSKe(Vd(iCk z%Od@tiO>@n#&0~+uNxmfbMpABXU9+D2UwN3TBFSqe*6JP-szWu73K}Z?`nl1R^XRE zJTPmW!=&O=);MAM0VTgr$yLJX9fx5_oTQ&i)m&giV^tSsmtyL*V^-dQ4{u{1u)9%@@H$AjB{nSzQ zcaEL^!~5d03%l0UynJP&qA6F=wCc`RbT8YBj>>A=vcD*Bjcw~(?bC`rzB4UzrIp*YU(|yN*5!@L z&Rk{Zde?#XUHQsae&Mv&+t#^iP>J6C>qjo-S|--H#-|mho&4e3p{E?(pH6zu+xV0G S?H8YN40t;2;Q3)TXa5g6U4&i$ diff --git a/.venv/lib/python3.12/site-packages/urllib3/__pycache__/response.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/__pycache__/response.cpython-312.pyc deleted file mode 100644 index 15c72eae0a2a4f5520a94b54daae276a09a45dcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50453 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/_base_connection.py b/.venv/lib/python3.12/site-packages/urllib3/_base_connection.py deleted file mode 100644 index 29ca334..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/_base_connection.py +++ /dev/null @@ -1,172 +0,0 @@ -from __future__ import annotations - -import typing - -from .util.connection import _TYPE_SOCKET_OPTIONS -from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT -from .util.url import Url - -_TYPE_BODY = typing.Union[bytes, typing.IO[typing.Any], typing.Iterable[bytes], str] - - -class ProxyConfig(typing.NamedTuple): - ssl_context: ssl.SSLContext | None - use_forwarding_for_https: bool - assert_hostname: None | str | typing.Literal[False] - assert_fingerprint: str | None - - -class _ResponseOptions(typing.NamedTuple): - # TODO: Remove this in favor of a better - # HTTP request/response lifecycle tracking. - request_method: str - request_url: str - preload_content: bool - decode_content: bool - enforce_content_length: bool - - -if typing.TYPE_CHECKING: - import ssl - from typing import Protocol - - from .response import BaseHTTPResponse - - class BaseHTTPConnection(Protocol): - default_port: typing.ClassVar[int] - default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] - - host: str - port: int - timeout: None | ( - float - ) # Instance doesn't store _DEFAULT_TIMEOUT, must be resolved. - blocksize: int - source_address: tuple[str, int] | None - socket_options: _TYPE_SOCKET_OPTIONS | None - - proxy: Url | None - proxy_config: ProxyConfig | None - - is_verified: bool - proxy_is_verified: bool | None - - def __init__( - self, - host: str, - port: int | None = None, - *, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - blocksize: int = 8192, - socket_options: _TYPE_SOCKET_OPTIONS | None = ..., - proxy: Url | None = None, - proxy_config: ProxyConfig | None = None, - ) -> None: - ... - - def set_tunnel( - self, - host: str, - port: int | None = None, - headers: typing.Mapping[str, str] | None = None, - scheme: str = "http", - ) -> None: - ... - - def connect(self) -> None: - ... - - def request( - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - # We know *at least* botocore is depending on the order of the - # first 3 parameters so to be safe we only mark the later ones - # as keyword-only to ensure we have space to extend. - *, - chunked: bool = False, - preload_content: bool = True, - decode_content: bool = True, - enforce_content_length: bool = True, - ) -> None: - ... - - def getresponse(self) -> BaseHTTPResponse: - ... - - def close(self) -> None: - ... - - @property - def is_closed(self) -> bool: - """Whether the connection either is brand new or has been previously closed. - If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` - properties must be False. - """ - - @property - def is_connected(self) -> bool: - """Whether the connection is actively connected to any origin (proxy or target)""" - - @property - def has_connected_to_proxy(self) -> bool: - """Whether the connection has successfully connected to its proxy. - This returns False if no proxy is in use. Used to determine whether - errors are coming from the proxy layer or from tunnelling to the target origin. - """ - - class BaseHTTPSConnection(BaseHTTPConnection, Protocol): - default_port: typing.ClassVar[int] - default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] - - # Certificate verification methods - cert_reqs: int | str | None - assert_hostname: None | str | typing.Literal[False] - assert_fingerprint: str | None - ssl_context: ssl.SSLContext | None - - # Trusted CAs - ca_certs: str | None - ca_cert_dir: str | None - ca_cert_data: None | str | bytes - - # TLS version - ssl_minimum_version: int | None - ssl_maximum_version: int | None - ssl_version: int | str | None # Deprecated - - # Client certificates - cert_file: str | None - key_file: str | None - key_password: str | None - - def __init__( - self, - host: str, - port: int | None = None, - *, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - blocksize: int = 16384, - socket_options: _TYPE_SOCKET_OPTIONS | None = ..., - proxy: Url | None = None, - proxy_config: ProxyConfig | None = None, - cert_reqs: int | str | None = None, - assert_hostname: None | str | typing.Literal[False] = None, - assert_fingerprint: str | None = None, - server_hostname: str | None = None, - ssl_context: ssl.SSLContext | None = None, - ca_certs: str | None = None, - ca_cert_dir: str | None = None, - ca_cert_data: None | str | bytes = None, - ssl_minimum_version: int | None = None, - ssl_maximum_version: int | None = None, - ssl_version: int | str | None = None, # Deprecated - cert_file: str | None = None, - key_file: str | None = None, - key_password: str | None = None, - ) -> None: - ... diff --git a/.venv/lib/python3.12/site-packages/urllib3/_collections.py b/.venv/lib/python3.12/site-packages/urllib3/_collections.py deleted file mode 100644 index 8a4409a..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/_collections.py +++ /dev/null @@ -1,483 +0,0 @@ -from __future__ import annotations - -import typing -from collections import OrderedDict -from enum import Enum, auto -from threading import RLock - -if typing.TYPE_CHECKING: - # We can only import Protocol if TYPE_CHECKING because it's a development - # dependency, and is not available at runtime. - from typing import Protocol - - from typing_extensions import Self - - class HasGettableStringKeys(Protocol): - def keys(self) -> typing.Iterator[str]: - ... - - def __getitem__(self, key: str) -> str: - ... - - -__all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"] - - -# Key type -_KT = typing.TypeVar("_KT") -# Value type -_VT = typing.TypeVar("_VT") -# Default type -_DT = typing.TypeVar("_DT") - -ValidHTTPHeaderSource = typing.Union[ - "HTTPHeaderDict", - typing.Mapping[str, str], - typing.Iterable[typing.Tuple[str, str]], - "HasGettableStringKeys", -] - - -class _Sentinel(Enum): - not_passed = auto() - - -def ensure_can_construct_http_header_dict( - potential: object, -) -> ValidHTTPHeaderSource | None: - if isinstance(potential, HTTPHeaderDict): - return potential - elif isinstance(potential, typing.Mapping): - # Full runtime checking of the contents of a Mapping is expensive, so for the - # purposes of typechecking, we assume that any Mapping is the right shape. - return typing.cast(typing.Mapping[str, str], potential) - elif isinstance(potential, typing.Iterable): - # Similarly to Mapping, full runtime checking of the contents of an Iterable is - # expensive, so for the purposes of typechecking, we assume that any Iterable - # is the right shape. - return typing.cast(typing.Iterable[typing.Tuple[str, str]], potential) - elif hasattr(potential, "keys") and hasattr(potential, "__getitem__"): - return typing.cast("HasGettableStringKeys", potential) - else: - return None - - -class RecentlyUsedContainer(typing.Generic[_KT, _VT], typing.MutableMapping[_KT, _VT]): - """ - Provides a thread-safe dict-like container which maintains up to - ``maxsize`` keys while throwing away the least-recently-used keys beyond - ``maxsize``. - - :param maxsize: - Maximum number of recent elements to retain. - - :param dispose_func: - Every time an item is evicted from the container, - ``dispose_func(value)`` is called. Callback which will get called - """ - - _container: typing.OrderedDict[_KT, _VT] - _maxsize: int - dispose_func: typing.Callable[[_VT], None] | None - lock: RLock - - def __init__( - self, - maxsize: int = 10, - dispose_func: typing.Callable[[_VT], None] | None = None, - ) -> None: - super().__init__() - self._maxsize = maxsize - self.dispose_func = dispose_func - self._container = OrderedDict() - self.lock = RLock() - - def __getitem__(self, key: _KT) -> _VT: - # Re-insert the item, moving it to the end of the eviction line. - with self.lock: - item = self._container.pop(key) - self._container[key] = item - return item - - def __setitem__(self, key: _KT, value: _VT) -> None: - evicted_item = None - with self.lock: - # Possibly evict the existing value of 'key' - try: - # If the key exists, we'll overwrite it, which won't change the - # size of the pool. Because accessing a key should move it to - # the end of the eviction line, we pop it out first. - evicted_item = key, self._container.pop(key) - self._container[key] = value - except KeyError: - # When the key does not exist, we insert the value first so that - # evicting works in all cases, including when self._maxsize is 0 - self._container[key] = value - if len(self._container) > self._maxsize: - # If we didn't evict an existing value, and we've hit our maximum - # size, then we have to evict the least recently used item from - # the beginning of the container. - evicted_item = self._container.popitem(last=False) - - # After releasing the lock on the pool, dispose of any evicted value. - if evicted_item is not None and self.dispose_func: - _, evicted_value = evicted_item - self.dispose_func(evicted_value) - - def __delitem__(self, key: _KT) -> None: - with self.lock: - value = self._container.pop(key) - - if self.dispose_func: - self.dispose_func(value) - - def __len__(self) -> int: - with self.lock: - return len(self._container) - - def __iter__(self) -> typing.NoReturn: - raise NotImplementedError( - "Iteration over this class is unlikely to be threadsafe." - ) - - def clear(self) -> None: - with self.lock: - # Copy pointers to all values, then wipe the mapping - values = list(self._container.values()) - self._container.clear() - - if self.dispose_func: - for value in values: - self.dispose_func(value) - - def keys(self) -> set[_KT]: # type: ignore[override] - with self.lock: - return set(self._container.keys()) - - -class HTTPHeaderDictItemView(typing.Set[typing.Tuple[str, str]]): - """ - HTTPHeaderDict is unusual for a Mapping[str, str] in that it has two modes of - address. - - If we directly try to get an item with a particular name, we will get a string - back that is the concatenated version of all the values: - - >>> d['X-Header-Name'] - 'Value1, Value2, Value3' - - However, if we iterate over an HTTPHeaderDict's items, we will optionally combine - these values based on whether combine=True was called when building up the dictionary - - >>> d = HTTPHeaderDict({"A": "1", "B": "foo"}) - >>> d.add("A", "2", combine=True) - >>> d.add("B", "bar") - >>> list(d.items()) - [ - ('A', '1, 2'), - ('B', 'foo'), - ('B', 'bar'), - ] - - This class conforms to the interface required by the MutableMapping ABC while - also giving us the nonstandard iteration behavior we want; items with duplicate - keys, ordered by time of first insertion. - """ - - _headers: HTTPHeaderDict - - def __init__(self, headers: HTTPHeaderDict) -> None: - self._headers = headers - - def __len__(self) -> int: - return len(list(self._headers.iteritems())) - - def __iter__(self) -> typing.Iterator[tuple[str, str]]: - return self._headers.iteritems() - - def __contains__(self, item: object) -> bool: - if isinstance(item, tuple) and len(item) == 2: - passed_key, passed_val = item - if isinstance(passed_key, str) and isinstance(passed_val, str): - return self._headers._has_value_for_header(passed_key, passed_val) - return False - - -class HTTPHeaderDict(typing.MutableMapping[str, str]): - """ - :param headers: - An iterable of field-value pairs. Must not contain multiple field names - when compared case-insensitively. - - :param kwargs: - Additional field-value pairs to pass in to ``dict.update``. - - A ``dict`` like container for storing HTTP Headers. - - Field names are stored and compared case-insensitively in compliance with - RFC 7230. Iteration provides the first case-sensitive key seen for each - case-insensitive pair. - - Using ``__setitem__`` syntax overwrites fields that compare equal - case-insensitively in order to maintain ``dict``'s api. For fields that - compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` - in a loop. - - If multiple fields that are equal case-insensitively are passed to the - constructor or ``.update``, the behavior is undefined and some will be - lost. - - >>> headers = HTTPHeaderDict() - >>> headers.add('Set-Cookie', 'foo=bar') - >>> headers.add('set-cookie', 'baz=quxx') - >>> headers['content-length'] = '7' - >>> headers['SET-cookie'] - 'foo=bar, baz=quxx' - >>> headers['Content-Length'] - '7' - """ - - _container: typing.MutableMapping[str, list[str]] - - def __init__(self, headers: ValidHTTPHeaderSource | None = None, **kwargs: str): - super().__init__() - self._container = {} # 'dict' is insert-ordered - if headers is not None: - if isinstance(headers, HTTPHeaderDict): - self._copy_from(headers) - else: - self.extend(headers) - if kwargs: - self.extend(kwargs) - - def __setitem__(self, key: str, val: str) -> None: - # avoid a bytes/str comparison by decoding before httplib - if isinstance(key, bytes): - key = key.decode("latin-1") - self._container[key.lower()] = [key, val] - - def __getitem__(self, key: str) -> str: - val = self._container[key.lower()] - return ", ".join(val[1:]) - - def __delitem__(self, key: str) -> None: - del self._container[key.lower()] - - def __contains__(self, key: object) -> bool: - if isinstance(key, str): - return key.lower() in self._container - return False - - def setdefault(self, key: str, default: str = "") -> str: - return super().setdefault(key, default) - - def __eq__(self, other: object) -> bool: - maybe_constructable = ensure_can_construct_http_header_dict(other) - if maybe_constructable is None: - return False - else: - other_as_http_header_dict = type(self)(maybe_constructable) - - return {k.lower(): v for k, v in self.itermerged()} == { - k.lower(): v for k, v in other_as_http_header_dict.itermerged() - } - - def __ne__(self, other: object) -> bool: - return not self.__eq__(other) - - def __len__(self) -> int: - return len(self._container) - - def __iter__(self) -> typing.Iterator[str]: - # Only provide the originally cased names - for vals in self._container.values(): - yield vals[0] - - def discard(self, key: str) -> None: - try: - del self[key] - except KeyError: - pass - - def add(self, key: str, val: str, *, combine: bool = False) -> None: - """Adds a (name, value) pair, doesn't overwrite the value if it already - exists. - - If this is called with combine=True, instead of adding a new header value - as a distinct item during iteration, this will instead append the value to - any existing header value with a comma. If no existing header value exists - for the key, then the value will simply be added, ignoring the combine parameter. - - >>> headers = HTTPHeaderDict(foo='bar') - >>> headers.add('Foo', 'baz') - >>> headers['foo'] - 'bar, baz' - >>> list(headers.items()) - [('foo', 'bar'), ('foo', 'baz')] - >>> headers.add('foo', 'quz', combine=True) - >>> list(headers.items()) - [('foo', 'bar, baz, quz')] - """ - # avoid a bytes/str comparison by decoding before httplib - if isinstance(key, bytes): - key = key.decode("latin-1") - key_lower = key.lower() - new_vals = [key, val] - # Keep the common case aka no item present as fast as possible - vals = self._container.setdefault(key_lower, new_vals) - if new_vals is not vals: - # if there are values here, then there is at least the initial - # key/value pair - assert len(vals) >= 2 - if combine: - vals[-1] = vals[-1] + ", " + val - else: - vals.append(val) - - def extend(self, *args: ValidHTTPHeaderSource, **kwargs: str) -> None: - """Generic import function for any type of header-like object. - Adapted version of MutableMapping.update in order to insert items - with self.add instead of self.__setitem__ - """ - if len(args) > 1: - raise TypeError( - f"extend() takes at most 1 positional arguments ({len(args)} given)" - ) - other = args[0] if len(args) >= 1 else () - - if isinstance(other, HTTPHeaderDict): - for key, val in other.iteritems(): - self.add(key, val) - elif isinstance(other, typing.Mapping): - for key, val in other.items(): - self.add(key, val) - elif isinstance(other, typing.Iterable): - other = typing.cast(typing.Iterable[typing.Tuple[str, str]], other) - for key, value in other: - self.add(key, value) - elif hasattr(other, "keys") and hasattr(other, "__getitem__"): - # THIS IS NOT A TYPESAFE BRANCH - # In this branch, the object has a `keys` attr but is not a Mapping or any of - # the other types indicated in the method signature. We do some stuff with - # it as though it partially implements the Mapping interface, but we're not - # doing that stuff safely AT ALL. - for key in other.keys(): - self.add(key, other[key]) - - for key, value in kwargs.items(): - self.add(key, value) - - @typing.overload - def getlist(self, key: str) -> list[str]: - ... - - @typing.overload - def getlist(self, key: str, default: _DT) -> list[str] | _DT: - ... - - def getlist( - self, key: str, default: _Sentinel | _DT = _Sentinel.not_passed - ) -> list[str] | _DT: - """Returns a list of all the values for the named field. Returns an - empty list if the key doesn't exist.""" - try: - vals = self._container[key.lower()] - except KeyError: - if default is _Sentinel.not_passed: - # _DT is unbound; empty list is instance of List[str] - return [] - # _DT is bound; default is instance of _DT - return default - else: - # _DT may or may not be bound; vals[1:] is instance of List[str], which - # meets our external interface requirement of `Union[List[str], _DT]`. - return vals[1:] - - def _prepare_for_method_change(self) -> Self: - """ - Remove content-specific header fields before changing the request - method to GET or HEAD according to RFC 9110, Section 15.4. - """ - content_specific_headers = [ - "Content-Encoding", - "Content-Language", - "Content-Location", - "Content-Type", - "Content-Length", - "Digest", - "Last-Modified", - ] - for header in content_specific_headers: - self.discard(header) - return self - - # Backwards compatibility for httplib - getheaders = getlist - getallmatchingheaders = getlist - iget = getlist - - # Backwards compatibility for http.cookiejar - get_all = getlist - - def __repr__(self) -> str: - return f"{type(self).__name__}({dict(self.itermerged())})" - - def _copy_from(self, other: HTTPHeaderDict) -> None: - for key in other: - val = other.getlist(key) - self._container[key.lower()] = [key, *val] - - def copy(self) -> Self: - clone = type(self)() - clone._copy_from(self) - return clone - - def iteritems(self) -> typing.Iterator[tuple[str, str]]: - """Iterate over all header lines, including duplicate ones.""" - for key in self: - vals = self._container[key.lower()] - for val in vals[1:]: - yield vals[0], val - - def itermerged(self) -> typing.Iterator[tuple[str, str]]: - """Iterate over all headers, merging duplicate ones together.""" - for key in self: - val = self._container[key.lower()] - yield val[0], ", ".join(val[1:]) - - def items(self) -> HTTPHeaderDictItemView: # type: ignore[override] - return HTTPHeaderDictItemView(self) - - def _has_value_for_header(self, header_name: str, potential_value: str) -> bool: - if header_name in self: - return potential_value in self._container[header_name.lower()][1:] - return False - - def __ior__(self, other: object) -> HTTPHeaderDict: - # Supports extending a header dict in-place using operator |= - # combining items with add instead of __setitem__ - maybe_constructable = ensure_can_construct_http_header_dict(other) - if maybe_constructable is None: - return NotImplemented - self.extend(maybe_constructable) - return self - - def __or__(self, other: object) -> Self: - # Supports merging header dicts using operator | - # combining items with add instead of __setitem__ - maybe_constructable = ensure_can_construct_http_header_dict(other) - if maybe_constructable is None: - return NotImplemented - result = self.copy() - result.extend(maybe_constructable) - return result - - def __ror__(self, other: object) -> Self: - # Supports merging header dicts using operator | when other is on left side - # combining items with add instead of __setitem__ - maybe_constructable = ensure_can_construct_http_header_dict(other) - if maybe_constructable is None: - return NotImplemented - result = type(self)(maybe_constructable) - result.extend(self) - return result diff --git a/.venv/lib/python3.12/site-packages/urllib3/_request_methods.py b/.venv/lib/python3.12/site-packages/urllib3/_request_methods.py deleted file mode 100644 index 632042f..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/_request_methods.py +++ /dev/null @@ -1,279 +0,0 @@ -from __future__ import annotations - -import json as _json -import typing -from urllib.parse import urlencode - -from ._base_connection import _TYPE_BODY -from ._collections import HTTPHeaderDict -from .filepost import _TYPE_FIELDS, encode_multipart_formdata -from .response import BaseHTTPResponse - -__all__ = ["RequestMethods"] - -_TYPE_ENCODE_URL_FIELDS = typing.Union[ - typing.Sequence[typing.Tuple[str, typing.Union[str, bytes]]], - typing.Mapping[str, typing.Union[str, bytes]], -] - - -class RequestMethods: - """ - Convenience mixin for classes who implement a :meth:`urlopen` method, such - as :class:`urllib3.HTTPConnectionPool` and - :class:`urllib3.PoolManager`. - - Provides behavior for making common types of HTTP request methods and - decides which type of request field encoding to use. - - Specifically, - - :meth:`.request_encode_url` is for sending requests whose fields are - encoded in the URL (such as GET, HEAD, DELETE). - - :meth:`.request_encode_body` is for sending requests whose fields are - encoded in the *body* of the request using multipart or www-form-urlencoded - (such as for POST, PUT, PATCH). - - :meth:`.request` is for making any kind of request, it will look up the - appropriate encoding format and use one of the above two methods to make - the request. - - Initializer parameters: - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - """ - - _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} - - def __init__(self, headers: typing.Mapping[str, str] | None = None) -> None: - self.headers = headers or {} - - def urlopen( - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - encode_multipart: bool = True, - multipart_boundary: str | None = None, - **kw: typing.Any, - ) -> BaseHTTPResponse: # Abstract - raise NotImplementedError( - "Classes extending RequestMethods must implement " - "their own ``urlopen`` method." - ) - - def request( - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - fields: _TYPE_FIELDS | None = None, - headers: typing.Mapping[str, str] | None = None, - json: typing.Any | None = None, - **urlopen_kw: typing.Any, - ) -> BaseHTTPResponse: - """ - Make a request using :meth:`urlopen` with the appropriate encoding of - ``fields`` based on the ``method`` used. - - This is a convenience method that requires the least amount of manual - effort. It can be used in most situations, while still having the - option to drop down to more specific methods when necessary, such as - :meth:`request_encode_url`, :meth:`request_encode_body`, - or even the lowest level :meth:`urlopen`. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param body: - Data to send in the request body, either :class:`str`, :class:`bytes`, - an iterable of :class:`str`/:class:`bytes`, or a file-like object. - - :param fields: - Data to encode and send in the request body. Values are processed - by :func:`urllib.parse.urlencode`. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - - :param json: - Data to encode and send as JSON with UTF-encoded in the request body. - The ``"Content-Type"`` header will be set to ``"application/json"`` - unless specified otherwise. - """ - method = method.upper() - - if json is not None and body is not None: - raise TypeError( - "request got values for both 'body' and 'json' parameters which are mutually exclusive" - ) - - if json is not None: - if headers is None: - headers = self.headers - - if not ("content-type" in map(str.lower, headers.keys())): - headers = HTTPHeaderDict(headers) - headers["Content-Type"] = "application/json" - - body = _json.dumps(json, separators=(",", ":"), ensure_ascii=False).encode( - "utf-8" - ) - - if body is not None: - urlopen_kw["body"] = body - - if method in self._encode_url_methods: - return self.request_encode_url( - method, - url, - fields=fields, # type: ignore[arg-type] - headers=headers, - **urlopen_kw, - ) - else: - return self.request_encode_body( - method, url, fields=fields, headers=headers, **urlopen_kw - ) - - def request_encode_url( - self, - method: str, - url: str, - fields: _TYPE_ENCODE_URL_FIELDS | None = None, - headers: typing.Mapping[str, str] | None = None, - **urlopen_kw: str, - ) -> BaseHTTPResponse: - """ - Make a request using :meth:`urlopen` with the ``fields`` encoded in - the url. This is useful for request methods like GET, HEAD, DELETE, etc. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param fields: - Data to encode and send in the request body. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - """ - if headers is None: - headers = self.headers - - extra_kw: dict[str, typing.Any] = {"headers": headers} - extra_kw.update(urlopen_kw) - - if fields: - url += "?" + urlencode(fields) - - return self.urlopen(method, url, **extra_kw) - - def request_encode_body( - self, - method: str, - url: str, - fields: _TYPE_FIELDS | None = None, - headers: typing.Mapping[str, str] | None = None, - encode_multipart: bool = True, - multipart_boundary: str | None = None, - **urlopen_kw: str, - ) -> BaseHTTPResponse: - """ - Make a request using :meth:`urlopen` with the ``fields`` encoded in - the body. This is useful for request methods like POST, PUT, PATCH, etc. - - When ``encode_multipart=True`` (default), then - :func:`urllib3.encode_multipart_formdata` is used to encode - the payload with the appropriate content type. Otherwise - :func:`urllib.parse.urlencode` is used with the - 'application/x-www-form-urlencoded' content type. - - Multipart encoding must be used when posting files, and it's reasonably - safe to use it in other times too. However, it may break request - signing, such as with OAuth. - - Supports an optional ``fields`` parameter of key/value strings AND - key/filetuple. A filetuple is a (filename, data, MIME type) tuple where - the MIME type is optional. For example:: - - fields = { - 'foo': 'bar', - 'fakefile': ('foofile.txt', 'contents of foofile'), - 'realfile': ('barfile.txt', open('realfile').read()), - 'typedfile': ('bazfile.bin', open('bazfile').read(), - 'image/jpeg'), - 'nonamefile': 'contents of nonamefile field', - } - - When uploading a file, providing a filename (the first parameter of the - tuple) is optional but recommended to best mimic behavior of browsers. - - Note that if ``headers`` are supplied, the 'Content-Type' header will - be overwritten because it depends on the dynamic random boundary string - which is used to compose the body of the request. The random boundary - string can be explicitly set with the ``multipart_boundary`` parameter. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param fields: - Data to encode and send in the request body. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - - :param encode_multipart: - If True, encode the ``fields`` using the multipart/form-data MIME - format. - - :param multipart_boundary: - If not specified, then a random boundary will be generated using - :func:`urllib3.filepost.choose_boundary`. - """ - if headers is None: - headers = self.headers - - extra_kw: dict[str, typing.Any] = {"headers": HTTPHeaderDict(headers)} - body: bytes | str - - if fields: - if "body" in urlopen_kw: - raise TypeError( - "request got values for both 'fields' and 'body', can only specify one." - ) - - if encode_multipart: - body, content_type = encode_multipart_formdata( - fields, boundary=multipart_boundary - ) - else: - body, content_type = ( - urlencode(fields), # type: ignore[arg-type] - "application/x-www-form-urlencoded", - ) - - extra_kw["body"] = body - extra_kw["headers"].setdefault("Content-Type", content_type) - - extra_kw.update(urlopen_kw) - - return self.urlopen(method, url, **extra_kw) diff --git a/.venv/lib/python3.12/site-packages/urllib3/_version.py b/.venv/lib/python3.12/site-packages/urllib3/_version.py deleted file mode 100644 index 7442f2b..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/_version.py +++ /dev/null @@ -1,4 +0,0 @@ -# This file is protected via CODEOWNERS -from __future__ import annotations - -__version__ = "2.2.2" diff --git a/.venv/lib/python3.12/site-packages/urllib3/connection.py b/.venv/lib/python3.12/site-packages/urllib3/connection.py deleted file mode 100644 index 1b16279..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/connection.py +++ /dev/null @@ -1,929 +0,0 @@ -from __future__ import annotations - -import datetime -import logging -import os -import re -import socket -import sys -import typing -import warnings -from http.client import HTTPConnection as _HTTPConnection -from http.client import HTTPException as HTTPException # noqa: F401 -from http.client import ResponseNotReady -from socket import timeout as SocketTimeout - -if typing.TYPE_CHECKING: - from .response import HTTPResponse - from .util.ssl_ import _TYPE_PEER_CERT_RET_DICT - from .util.ssltransport import SSLTransport - -from ._collections import HTTPHeaderDict -from .util.response import assert_header_parsing -from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT, Timeout -from .util.util import to_str -from .util.wait import wait_for_read - -try: # Compiled with SSL? - import ssl - - BaseSSLError = ssl.SSLError -except (ImportError, AttributeError): - ssl = None # type: ignore[assignment] - - class BaseSSLError(BaseException): # type: ignore[no-redef] - pass - - -from ._base_connection import _TYPE_BODY -from ._base_connection import ProxyConfig as ProxyConfig -from ._base_connection import _ResponseOptions as _ResponseOptions -from ._version import __version__ -from .exceptions import ( - ConnectTimeoutError, - HeaderParsingError, - NameResolutionError, - NewConnectionError, - ProxyError, - SystemTimeWarning, -) -from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection, ssl_ -from .util.request import body_to_chunks -from .util.ssl_ import assert_fingerprint as _assert_fingerprint -from .util.ssl_ import ( - create_urllib3_context, - is_ipaddress, - resolve_cert_reqs, - resolve_ssl_version, - ssl_wrap_socket, -) -from .util.ssl_match_hostname import CertificateError, match_hostname -from .util.url import Url - -# Not a no-op, we're adding this to the namespace so it can be imported. -ConnectionError = ConnectionError -BrokenPipeError = BrokenPipeError - - -log = logging.getLogger(__name__) - -port_by_scheme = {"http": 80, "https": 443} - -# When it comes time to update this value as a part of regular maintenance -# (ie test_recent_date is failing) update it to ~6 months before the current date. -RECENT_DATE = datetime.date(2023, 6, 1) - -_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") - -_HAS_SYS_AUDIT = hasattr(sys, "audit") - - -class HTTPConnection(_HTTPConnection): - """ - Based on :class:`http.client.HTTPConnection` but provides an extra constructor - backwards-compatibility layer between older and newer Pythons. - - Additional keyword parameters are used to configure attributes of the connection. - Accepted parameters include: - - - ``source_address``: Set the source address for the current connection. - - ``socket_options``: Set specific options on the underlying socket. If not specified, then - defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling - Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. - - For example, if you wish to enable TCP Keep Alive in addition to the defaults, - you might pass: - - .. code-block:: python - - HTTPConnection.default_socket_options + [ - (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), - ] - - Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). - """ - - default_port: typing.ClassVar[int] = port_by_scheme["http"] # type: ignore[misc] - - #: Disable Nagle's algorithm by default. - #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` - default_socket_options: typing.ClassVar[connection._TYPE_SOCKET_OPTIONS] = [ - (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) - ] - - #: Whether this connection verifies the host's certificate. - is_verified: bool = False - - #: Whether this proxy connection verified the proxy host's certificate. - # If no proxy is currently connected to the value will be ``None``. - proxy_is_verified: bool | None = None - - blocksize: int - source_address: tuple[str, int] | None - socket_options: connection._TYPE_SOCKET_OPTIONS | None - - _has_connected_to_proxy: bool - _response_options: _ResponseOptions | None - _tunnel_host: str | None - _tunnel_port: int | None - _tunnel_scheme: str | None - - def __init__( - self, - host: str, - port: int | None = None, - *, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - blocksize: int = 16384, - socket_options: None - | (connection._TYPE_SOCKET_OPTIONS) = default_socket_options, - proxy: Url | None = None, - proxy_config: ProxyConfig | None = None, - ) -> None: - super().__init__( - host=host, - port=port, - timeout=Timeout.resolve_default_timeout(timeout), - source_address=source_address, - blocksize=blocksize, - ) - self.socket_options = socket_options - self.proxy = proxy - self.proxy_config = proxy_config - - self._has_connected_to_proxy = False - self._response_options = None - self._tunnel_host: str | None = None - self._tunnel_port: int | None = None - self._tunnel_scheme: str | None = None - - @property - def host(self) -> str: - """ - Getter method to remove any trailing dots that indicate the hostname is an FQDN. - - In general, SSL certificates don't include the trailing dot indicating a - fully-qualified domain name, and thus, they don't validate properly when - checked against a domain name that includes the dot. In addition, some - servers may not expect to receive the trailing dot when provided. - - However, the hostname with trailing dot is critical to DNS resolution; doing a - lookup with the trailing dot will properly only resolve the appropriate FQDN, - whereas a lookup without a trailing dot will search the system's search domain - list. Thus, it's important to keep the original host around for use only in - those cases where it's appropriate (i.e., when doing DNS lookup to establish the - actual TCP connection across which we're going to send HTTP requests). - """ - return self._dns_host.rstrip(".") - - @host.setter - def host(self, value: str) -> None: - """ - Setter for the `host` property. - - We assume that only urllib3 uses the _dns_host attribute; httplib itself - only uses `host`, and it seems reasonable that other libraries follow suit. - """ - self._dns_host = value - - def _new_conn(self) -> socket.socket: - """Establish a socket connection and set nodelay settings on it. - - :return: New socket connection. - """ - try: - sock = connection.create_connection( - (self._dns_host, self.port), - self.timeout, - source_address=self.source_address, - socket_options=self.socket_options, - ) - except socket.gaierror as e: - raise NameResolutionError(self.host, self, e) from e - except SocketTimeout as e: - raise ConnectTimeoutError( - self, - f"Connection to {self.host} timed out. (connect timeout={self.timeout})", - ) from e - - except OSError as e: - raise NewConnectionError( - self, f"Failed to establish a new connection: {e}" - ) from e - - # Audit hooks are only available in Python 3.8+ - if _HAS_SYS_AUDIT: - sys.audit("http.client.connect", self, self.host, self.port) - - return sock - - def set_tunnel( - self, - host: str, - port: int | None = None, - headers: typing.Mapping[str, str] | None = None, - scheme: str = "http", - ) -> None: - if scheme not in ("http", "https"): - raise ValueError( - f"Invalid proxy scheme for tunneling: {scheme!r}, must be either 'http' or 'https'" - ) - super().set_tunnel(host, port=port, headers=headers) - self._tunnel_scheme = scheme - - def connect(self) -> None: - self.sock = self._new_conn() - if self._tunnel_host: - # If we're tunneling it means we're connected to our proxy. - self._has_connected_to_proxy = True - - # TODO: Fix tunnel so it doesn't depend on self.sock state. - self._tunnel() # type: ignore[attr-defined] - - # If there's a proxy to be connected to we are fully connected. - # This is set twice (once above and here) due to forwarding proxies - # not using tunnelling. - self._has_connected_to_proxy = bool(self.proxy) - - if self._has_connected_to_proxy: - self.proxy_is_verified = False - - @property - def is_closed(self) -> bool: - return self.sock is None - - @property - def is_connected(self) -> bool: - if self.sock is None: - return False - return not wait_for_read(self.sock, timeout=0.0) - - @property - def has_connected_to_proxy(self) -> bool: - return self._has_connected_to_proxy - - @property - def proxy_is_forwarding(self) -> bool: - """ - Return True if a forwarding proxy is configured, else return False - """ - return bool(self.proxy) and self._tunnel_host is None - - def close(self) -> None: - try: - super().close() - finally: - # Reset all stateful properties so connection - # can be re-used without leaking prior configs. - self.sock = None - self.is_verified = False - self.proxy_is_verified = None - self._has_connected_to_proxy = False - self._response_options = None - self._tunnel_host = None - self._tunnel_port = None - self._tunnel_scheme = None - - def putrequest( - self, - method: str, - url: str, - skip_host: bool = False, - skip_accept_encoding: bool = False, - ) -> None: - """""" - # Empty docstring because the indentation of CPython's implementation - # is broken but we don't want this method in our documentation. - match = _CONTAINS_CONTROL_CHAR_RE.search(method) - if match: - raise ValueError( - f"Method cannot contain non-token characters {method!r} (found at least {match.group()!r})" - ) - - return super().putrequest( - method, url, skip_host=skip_host, skip_accept_encoding=skip_accept_encoding - ) - - def putheader(self, header: str, *values: str) -> None: # type: ignore[override] - """""" - if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): - super().putheader(header, *values) - elif to_str(header.lower()) not in SKIPPABLE_HEADERS: - skippable_headers = "', '".join( - [str.title(header) for header in sorted(SKIPPABLE_HEADERS)] - ) - raise ValueError( - f"urllib3.util.SKIP_HEADER only supports '{skippable_headers}'" - ) - - # `request` method's signature intentionally violates LSP. - # urllib3's API is different from `http.client.HTTPConnection` and the subclassing is only incidental. - def request( # type: ignore[override] - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - *, - chunked: bool = False, - preload_content: bool = True, - decode_content: bool = True, - enforce_content_length: bool = True, - ) -> None: - # Update the inner socket's timeout value to send the request. - # This only triggers if the connection is re-used. - if self.sock is not None: - self.sock.settimeout(self.timeout) - - # Store these values to be fed into the HTTPResponse - # object later. TODO: Remove this in favor of a real - # HTTP lifecycle mechanism. - - # We have to store these before we call .request() - # because sometimes we can still salvage a response - # off the wire even if we aren't able to completely - # send the request body. - self._response_options = _ResponseOptions( - request_method=method, - request_url=url, - preload_content=preload_content, - decode_content=decode_content, - enforce_content_length=enforce_content_length, - ) - - if headers is None: - headers = {} - header_keys = frozenset(to_str(k.lower()) for k in headers) - skip_accept_encoding = "accept-encoding" in header_keys - skip_host = "host" in header_keys - self.putrequest( - method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host - ) - - # Transform the body into an iterable of sendall()-able chunks - # and detect if an explicit Content-Length is doable. - chunks_and_cl = body_to_chunks(body, method=method, blocksize=self.blocksize) - chunks = chunks_and_cl.chunks - content_length = chunks_and_cl.content_length - - # When chunked is explicit set to 'True' we respect that. - if chunked: - if "transfer-encoding" not in header_keys: - self.putheader("Transfer-Encoding", "chunked") - else: - # Detect whether a framing mechanism is already in use. If so - # we respect that value, otherwise we pick chunked vs content-length - # depending on the type of 'body'. - if "content-length" in header_keys: - chunked = False - elif "transfer-encoding" in header_keys: - chunked = True - - # Otherwise we go off the recommendation of 'body_to_chunks()'. - else: - chunked = False - if content_length is None: - if chunks is not None: - chunked = True - self.putheader("Transfer-Encoding", "chunked") - else: - self.putheader("Content-Length", str(content_length)) - - # Now that framing headers are out of the way we send all the other headers. - if "user-agent" not in header_keys: - self.putheader("User-Agent", _get_default_user_agent()) - for header, value in headers.items(): - self.putheader(header, value) - self.endheaders() - - # If we're given a body we start sending that in chunks. - if chunks is not None: - for chunk in chunks: - # Sending empty chunks isn't allowed for TE: chunked - # as it indicates the end of the body. - if not chunk: - continue - if isinstance(chunk, str): - chunk = chunk.encode("utf-8") - if chunked: - self.send(b"%x\r\n%b\r\n" % (len(chunk), chunk)) - else: - self.send(chunk) - - # Regardless of whether we have a body or not, if we're in - # chunked mode we want to send an explicit empty chunk. - if chunked: - self.send(b"0\r\n\r\n") - - def request_chunked( - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - ) -> None: - """ - Alternative to the common request method, which sends the - body with chunked encoding and not as one block - """ - warnings.warn( - "HTTPConnection.request_chunked() is deprecated and will be removed " - "in urllib3 v2.1.0. Instead use HTTPConnection.request(..., chunked=True).", - category=DeprecationWarning, - stacklevel=2, - ) - self.request(method, url, body=body, headers=headers, chunked=True) - - def getresponse( # type: ignore[override] - self, - ) -> HTTPResponse: - """ - Get the response from the server. - - If the HTTPConnection is in the correct state, returns an instance of HTTPResponse or of whatever object is returned by the response_class variable. - - If a request has not been sent or if a previous response has not be handled, ResponseNotReady is raised. If the HTTP response indicates that the connection should be closed, then it will be closed before the response is returned. When the connection is closed, the underlying socket is closed. - """ - # Raise the same error as http.client.HTTPConnection - if self._response_options is None: - raise ResponseNotReady() - - # Reset this attribute for being used again. - resp_options = self._response_options - self._response_options = None - - # Since the connection's timeout value may have been updated - # we need to set the timeout on the socket. - self.sock.settimeout(self.timeout) - - # This is needed here to avoid circular import errors - from .response import HTTPResponse - - # Get the response from http.client.HTTPConnection - httplib_response = super().getresponse() - - try: - assert_header_parsing(httplib_response.msg) - except (HeaderParsingError, TypeError) as hpe: - log.warning( - "Failed to parse headers (url=%s): %s", - _url_from_connection(self, resp_options.request_url), - hpe, - exc_info=True, - ) - - headers = HTTPHeaderDict(httplib_response.msg.items()) - - response = HTTPResponse( - body=httplib_response, - headers=headers, - status=httplib_response.status, - version=httplib_response.version, - version_string=getattr(self, "_http_vsn_str", "HTTP/?"), - reason=httplib_response.reason, - preload_content=resp_options.preload_content, - decode_content=resp_options.decode_content, - original_response=httplib_response, - enforce_content_length=resp_options.enforce_content_length, - request_method=resp_options.request_method, - request_url=resp_options.request_url, - ) - return response - - -class HTTPSConnection(HTTPConnection): - """ - Many of the parameters to this constructor are passed to the underlying SSL - socket by means of :py:func:`urllib3.util.ssl_wrap_socket`. - """ - - default_port = port_by_scheme["https"] # type: ignore[misc] - - cert_reqs: int | str | None = None - ca_certs: str | None = None - ca_cert_dir: str | None = None - ca_cert_data: None | str | bytes = None - ssl_version: int | str | None = None - ssl_minimum_version: int | None = None - ssl_maximum_version: int | None = None - assert_fingerprint: str | None = None - - def __init__( - self, - host: str, - port: int | None = None, - *, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - blocksize: int = 16384, - socket_options: None - | (connection._TYPE_SOCKET_OPTIONS) = HTTPConnection.default_socket_options, - proxy: Url | None = None, - proxy_config: ProxyConfig | None = None, - cert_reqs: int | str | None = None, - assert_hostname: None | str | typing.Literal[False] = None, - assert_fingerprint: str | None = None, - server_hostname: str | None = None, - ssl_context: ssl.SSLContext | None = None, - ca_certs: str | None = None, - ca_cert_dir: str | None = None, - ca_cert_data: None | str | bytes = None, - ssl_minimum_version: int | None = None, - ssl_maximum_version: int | None = None, - ssl_version: int | str | None = None, # Deprecated - cert_file: str | None = None, - key_file: str | None = None, - key_password: str | None = None, - ) -> None: - super().__init__( - host, - port=port, - timeout=timeout, - source_address=source_address, - blocksize=blocksize, - socket_options=socket_options, - proxy=proxy, - proxy_config=proxy_config, - ) - - self.key_file = key_file - self.cert_file = cert_file - self.key_password = key_password - self.ssl_context = ssl_context - self.server_hostname = server_hostname - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - self.ssl_version = ssl_version - self.ssl_minimum_version = ssl_minimum_version - self.ssl_maximum_version = ssl_maximum_version - self.ca_certs = ca_certs and os.path.expanduser(ca_certs) - self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) - self.ca_cert_data = ca_cert_data - - # cert_reqs depends on ssl_context so calculate last. - if cert_reqs is None: - if self.ssl_context is not None: - cert_reqs = self.ssl_context.verify_mode - else: - cert_reqs = resolve_cert_reqs(None) - self.cert_reqs = cert_reqs - - def set_cert( - self, - key_file: str | None = None, - cert_file: str | None = None, - cert_reqs: int | str | None = None, - key_password: str | None = None, - ca_certs: str | None = None, - assert_hostname: None | str | typing.Literal[False] = None, - assert_fingerprint: str | None = None, - ca_cert_dir: str | None = None, - ca_cert_data: None | str | bytes = None, - ) -> None: - """ - This method should only be called once, before the connection is used. - """ - warnings.warn( - "HTTPSConnection.set_cert() is deprecated and will be removed " - "in urllib3 v2.1.0. Instead provide the parameters to the " - "HTTPSConnection constructor.", - category=DeprecationWarning, - stacklevel=2, - ) - - # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also - # have an SSLContext object in which case we'll use its verify_mode. - if cert_reqs is None: - if self.ssl_context is not None: - cert_reqs = self.ssl_context.verify_mode - else: - cert_reqs = resolve_cert_reqs(None) - - self.key_file = key_file - self.cert_file = cert_file - self.cert_reqs = cert_reqs - self.key_password = key_password - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - self.ca_certs = ca_certs and os.path.expanduser(ca_certs) - self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) - self.ca_cert_data = ca_cert_data - - def connect(self) -> None: - sock: socket.socket | ssl.SSLSocket - self.sock = sock = self._new_conn() - server_hostname: str = self.host - tls_in_tls = False - - # Do we need to establish a tunnel? - if self._tunnel_host is not None: - # We're tunneling to an HTTPS origin so need to do TLS-in-TLS. - if self._tunnel_scheme == "https": - # _connect_tls_proxy will verify and assign proxy_is_verified - self.sock = sock = self._connect_tls_proxy(self.host, sock) - tls_in_tls = True - elif self._tunnel_scheme == "http": - self.proxy_is_verified = False - - # If we're tunneling it means we're connected to our proxy. - self._has_connected_to_proxy = True - - self._tunnel() # type: ignore[attr-defined] - # Override the host with the one we're requesting data from. - server_hostname = self._tunnel_host - - if self.server_hostname is not None: - server_hostname = self.server_hostname - - is_time_off = datetime.date.today() < RECENT_DATE - if is_time_off: - warnings.warn( - ( - f"System time is way off (before {RECENT_DATE}). This will probably " - "lead to SSL verification errors" - ), - SystemTimeWarning, - ) - - # Remove trailing '.' from fqdn hostnames to allow certificate validation - server_hostname_rm_dot = server_hostname.rstrip(".") - - sock_and_verified = _ssl_wrap_socket_and_match_hostname( - sock=sock, - cert_reqs=self.cert_reqs, - ssl_version=self.ssl_version, - ssl_minimum_version=self.ssl_minimum_version, - ssl_maximum_version=self.ssl_maximum_version, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - ca_cert_data=self.ca_cert_data, - cert_file=self.cert_file, - key_file=self.key_file, - key_password=self.key_password, - server_hostname=server_hostname_rm_dot, - ssl_context=self.ssl_context, - tls_in_tls=tls_in_tls, - assert_hostname=self.assert_hostname, - assert_fingerprint=self.assert_fingerprint, - ) - self.sock = sock_and_verified.socket - - # Forwarding proxies can never have a verified target since - # the proxy is the one doing the verification. Should instead - # use a CONNECT tunnel in order to verify the target. - # See: https://github.com/urllib3/urllib3/issues/3267. - if self.proxy_is_forwarding: - self.is_verified = False - else: - self.is_verified = sock_and_verified.is_verified - - # If there's a proxy to be connected to we are fully connected. - # This is set twice (once above and here) due to forwarding proxies - # not using tunnelling. - self._has_connected_to_proxy = bool(self.proxy) - - # Set `self.proxy_is_verified` unless it's already set while - # establishing a tunnel. - if self._has_connected_to_proxy and self.proxy_is_verified is None: - self.proxy_is_verified = sock_and_verified.is_verified - - def _connect_tls_proxy(self, hostname: str, sock: socket.socket) -> ssl.SSLSocket: - """ - Establish a TLS connection to the proxy using the provided SSL context. - """ - # `_connect_tls_proxy` is called when self._tunnel_host is truthy. - proxy_config = typing.cast(ProxyConfig, self.proxy_config) - ssl_context = proxy_config.ssl_context - sock_and_verified = _ssl_wrap_socket_and_match_hostname( - sock, - cert_reqs=self.cert_reqs, - ssl_version=self.ssl_version, - ssl_minimum_version=self.ssl_minimum_version, - ssl_maximum_version=self.ssl_maximum_version, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - ca_cert_data=self.ca_cert_data, - server_hostname=hostname, - ssl_context=ssl_context, - assert_hostname=proxy_config.assert_hostname, - assert_fingerprint=proxy_config.assert_fingerprint, - # Features that aren't implemented for proxies yet: - cert_file=None, - key_file=None, - key_password=None, - tls_in_tls=False, - ) - self.proxy_is_verified = sock_and_verified.is_verified - return sock_and_verified.socket # type: ignore[return-value] - - -class _WrappedAndVerifiedSocket(typing.NamedTuple): - """ - Wrapped socket and whether the connection is - verified after the TLS handshake - """ - - socket: ssl.SSLSocket | SSLTransport - is_verified: bool - - -def _ssl_wrap_socket_and_match_hostname( - sock: socket.socket, - *, - cert_reqs: None | str | int, - ssl_version: None | str | int, - ssl_minimum_version: int | None, - ssl_maximum_version: int | None, - cert_file: str | None, - key_file: str | None, - key_password: str | None, - ca_certs: str | None, - ca_cert_dir: str | None, - ca_cert_data: None | str | bytes, - assert_hostname: None | str | typing.Literal[False], - assert_fingerprint: str | None, - server_hostname: str | None, - ssl_context: ssl.SSLContext | None, - tls_in_tls: bool = False, -) -> _WrappedAndVerifiedSocket: - """Logic for constructing an SSLContext from all TLS parameters, passing - that down into ssl_wrap_socket, and then doing certificate verification - either via hostname or fingerprint. This function exists to guarantee - that both proxies and targets have the same behavior when connecting via TLS. - """ - default_ssl_context = False - if ssl_context is None: - default_ssl_context = True - context = create_urllib3_context( - ssl_version=resolve_ssl_version(ssl_version), - ssl_minimum_version=ssl_minimum_version, - ssl_maximum_version=ssl_maximum_version, - cert_reqs=resolve_cert_reqs(cert_reqs), - ) - else: - context = ssl_context - - context.verify_mode = resolve_cert_reqs(cert_reqs) - - # In some cases, we want to verify hostnames ourselves - if ( - # `ssl` can't verify fingerprints or alternate hostnames - assert_fingerprint - or assert_hostname - # assert_hostname can be set to False to disable hostname checking - or assert_hostname is False - # We still support OpenSSL 1.0.2, which prevents us from verifying - # hostnames easily: https://github.com/pyca/pyopenssl/pull/933 - or ssl_.IS_PYOPENSSL - or not ssl_.HAS_NEVER_CHECK_COMMON_NAME - ): - context.check_hostname = False - - # Try to load OS default certs if none are given. We need to do the hasattr() check - # for custom pyOpenSSL SSLContext objects because they don't support - # load_default_certs(). - if ( - not ca_certs - and not ca_cert_dir - and not ca_cert_data - and default_ssl_context - and hasattr(context, "load_default_certs") - ): - context.load_default_certs() - - # Ensure that IPv6 addresses are in the proper format and don't have a - # scope ID. Python's SSL module fails to recognize scoped IPv6 addresses - # and interprets them as DNS hostnames. - if server_hostname is not None: - normalized = server_hostname.strip("[]") - if "%" in normalized: - normalized = normalized[: normalized.rfind("%")] - if is_ipaddress(normalized): - server_hostname = normalized - - ssl_sock = ssl_wrap_socket( - sock=sock, - keyfile=key_file, - certfile=cert_file, - key_password=key_password, - ca_certs=ca_certs, - ca_cert_dir=ca_cert_dir, - ca_cert_data=ca_cert_data, - server_hostname=server_hostname, - ssl_context=context, - tls_in_tls=tls_in_tls, - ) - - try: - if assert_fingerprint: - _assert_fingerprint( - ssl_sock.getpeercert(binary_form=True), assert_fingerprint - ) - elif ( - context.verify_mode != ssl.CERT_NONE - and not context.check_hostname - and assert_hostname is not False - ): - cert: _TYPE_PEER_CERT_RET_DICT = ssl_sock.getpeercert() # type: ignore[assignment] - - # Need to signal to our match_hostname whether to use 'commonName' or not. - # If we're using our own constructed SSLContext we explicitly set 'False' - # because PyPy hard-codes 'True' from SSLContext.hostname_checks_common_name. - if default_ssl_context: - hostname_checks_common_name = False - else: - hostname_checks_common_name = ( - getattr(context, "hostname_checks_common_name", False) or False - ) - - _match_hostname( - cert, - assert_hostname or server_hostname, # type: ignore[arg-type] - hostname_checks_common_name, - ) - - return _WrappedAndVerifiedSocket( - socket=ssl_sock, - is_verified=context.verify_mode == ssl.CERT_REQUIRED - or bool(assert_fingerprint), - ) - except BaseException: - ssl_sock.close() - raise - - -def _match_hostname( - cert: _TYPE_PEER_CERT_RET_DICT | None, - asserted_hostname: str, - hostname_checks_common_name: bool = False, -) -> None: - # Our upstream implementation of ssl.match_hostname() - # only applies this normalization to IP addresses so it doesn't - # match DNS SANs so we do the same thing! - stripped_hostname = asserted_hostname.strip("[]") - if is_ipaddress(stripped_hostname): - asserted_hostname = stripped_hostname - - try: - match_hostname(cert, asserted_hostname, hostname_checks_common_name) - except CertificateError as e: - log.warning( - "Certificate did not match expected hostname: %s. Certificate: %s", - asserted_hostname, - cert, - ) - # Add cert to exception and reraise so client code can inspect - # the cert when catching the exception, if they want to - e._peer_cert = cert # type: ignore[attr-defined] - raise - - -def _wrap_proxy_error(err: Exception, proxy_scheme: str | None) -> ProxyError: - # Look for the phrase 'wrong version number', if found - # then we should warn the user that we're very sure that - # this proxy is HTTP-only and they have a configuration issue. - error_normalized = " ".join(re.split("[^a-z]", str(err).lower())) - is_likely_http_proxy = ( - "wrong version number" in error_normalized - or "unknown protocol" in error_normalized - or "record layer failure" in error_normalized - ) - http_proxy_warning = ( - ". Your proxy appears to only use HTTP and not HTTPS, " - "try changing your proxy URL to be HTTP. See: " - "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" - "#https-proxy-error-http-proxy" - ) - new_err = ProxyError( - f"Unable to connect to proxy" - f"{http_proxy_warning if is_likely_http_proxy and proxy_scheme == 'https' else ''}", - err, - ) - new_err.__cause__ = err - return new_err - - -def _get_default_user_agent() -> str: - return f"python-urllib3/{__version__}" - - -class DummyConnection: - """Used to detect a failed ConnectionCls import.""" - - -if not ssl: - HTTPSConnection = DummyConnection # type: ignore[misc, assignment] # noqa: F811 - - -VerifiedHTTPSConnection = HTTPSConnection - - -def _url_from_connection( - conn: HTTPConnection | HTTPSConnection, path: str | None = None -) -> str: - """Returns the URL from a given connection. This is mainly used for testing and logging.""" - - scheme = "https" if isinstance(conn, HTTPSConnection) else "http" - - return Url(scheme=scheme, host=conn.host, port=conn.port, path=path).url diff --git a/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py b/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py deleted file mode 100644 index a2c3cf6..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py +++ /dev/null @@ -1,1182 +0,0 @@ -from __future__ import annotations - -import errno -import logging -import queue -import sys -import typing -import warnings -import weakref -from socket import timeout as SocketTimeout -from types import TracebackType - -from ._base_connection import _TYPE_BODY -from ._collections import HTTPHeaderDict -from ._request_methods import RequestMethods -from .connection import ( - BaseSSLError, - BrokenPipeError, - DummyConnection, - HTTPConnection, - HTTPException, - HTTPSConnection, - ProxyConfig, - _wrap_proxy_error, -) -from .connection import port_by_scheme as port_by_scheme -from .exceptions import ( - ClosedPoolError, - EmptyPoolError, - FullPoolError, - HostChangedError, - InsecureRequestWarning, - LocationValueError, - MaxRetryError, - NewConnectionError, - ProtocolError, - ProxyError, - ReadTimeoutError, - SSLError, - TimeoutError, -) -from .response import BaseHTTPResponse -from .util.connection import is_connection_dropped -from .util.proxy import connection_requires_http_tunnel -from .util.request import _TYPE_BODY_POSITION, set_file_position -from .util.retry import Retry -from .util.ssl_match_hostname import CertificateError -from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_DEFAULT, Timeout -from .util.url import Url, _encode_target -from .util.url import _normalize_host as normalize_host -from .util.url import parse_url -from .util.util import to_str - -if typing.TYPE_CHECKING: - import ssl - - from typing_extensions import Self - - from ._base_connection import BaseHTTPConnection, BaseHTTPSConnection - -log = logging.getLogger(__name__) - -_TYPE_TIMEOUT = typing.Union[Timeout, float, _TYPE_DEFAULT, None] - - -# Pool objects -class ConnectionPool: - """ - Base class for all connection pools, such as - :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. - - .. note:: - ConnectionPool.urlopen() does not normalize or percent-encode target URIs - which is useful if your target server doesn't support percent-encoded - target URIs. - """ - - scheme: str | None = None - QueueCls = queue.LifoQueue - - def __init__(self, host: str, port: int | None = None) -> None: - if not host: - raise LocationValueError("No host specified.") - - self.host = _normalize_host(host, scheme=self.scheme) - self.port = port - - # This property uses 'normalize_host()' (not '_normalize_host()') - # to avoid removing square braces around IPv6 addresses. - # This value is sent to `HTTPConnection.set_tunnel()` if called - # because square braces are required for HTTP CONNECT tunneling. - self._tunnel_host = normalize_host(host, scheme=self.scheme).lower() - - def __str__(self) -> str: - return f"{type(self).__name__}(host={self.host!r}, port={self.port!r})" - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> typing.Literal[False]: - self.close() - # Return False to re-raise any potential exceptions - return False - - def close(self) -> None: - """ - Close all pooled connections and disable the pool. - """ - - -# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 -_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} - - -class HTTPConnectionPool(ConnectionPool, RequestMethods): - """ - Thread-safe connection pool for one host. - - :param host: - Host used for this HTTP Connection (e.g. "localhost"), passed into - :class:`http.client.HTTPConnection`. - - :param port: - Port used for this HTTP Connection (None is equivalent to 80), passed - into :class:`http.client.HTTPConnection`. - - :param timeout: - Socket timeout in seconds for each individual connection. This can - be a float or integer, which sets the timeout for the HTTP request, - or an instance of :class:`urllib3.util.Timeout` which gives you more - fine-grained control over request timeouts. After the constructor has - been parsed, this is always a `urllib3.util.Timeout` object. - - :param maxsize: - Number of connections to save that can be reused. More than 1 is useful - in multithreaded situations. If ``block`` is set to False, more - connections will be created but they will not be saved once they've - been used. - - :param block: - If set to True, no more than ``maxsize`` connections will be used at - a time. When no free connections are available, the call will block - until a connection has been released. This is a useful side effect for - particular multithreaded situations where one does not want to use more - than maxsize connections per host to prevent flooding. - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - - :param retries: - Retry configuration to use by default with requests in this pool. - - :param _proxy: - Parsed proxy URL, should not be used directly, instead, see - :class:`urllib3.ProxyManager` - - :param _proxy_headers: - A dictionary with proxy headers, should not be used directly, - instead, see :class:`urllib3.ProxyManager` - - :param \\**conn_kw: - Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, - :class:`urllib3.connection.HTTPSConnection` instances. - """ - - scheme = "http" - ConnectionCls: ( - type[BaseHTTPConnection] | type[BaseHTTPSConnection] - ) = HTTPConnection - - def __init__( - self, - host: str, - port: int | None = None, - timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT, - maxsize: int = 1, - block: bool = False, - headers: typing.Mapping[str, str] | None = None, - retries: Retry | bool | int | None = None, - _proxy: Url | None = None, - _proxy_headers: typing.Mapping[str, str] | None = None, - _proxy_config: ProxyConfig | None = None, - **conn_kw: typing.Any, - ): - ConnectionPool.__init__(self, host, port) - RequestMethods.__init__(self, headers) - - if not isinstance(timeout, Timeout): - timeout = Timeout.from_float(timeout) - - if retries is None: - retries = Retry.DEFAULT - - self.timeout = timeout - self.retries = retries - - self.pool: queue.LifoQueue[typing.Any] | None = self.QueueCls(maxsize) - self.block = block - - self.proxy = _proxy - self.proxy_headers = _proxy_headers or {} - self.proxy_config = _proxy_config - - # Fill the queue up so that doing get() on it will block properly - for _ in range(maxsize): - self.pool.put(None) - - # These are mostly for testing and debugging purposes. - self.num_connections = 0 - self.num_requests = 0 - self.conn_kw = conn_kw - - if self.proxy: - # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. - # We cannot know if the user has added default socket options, so we cannot replace the - # list. - self.conn_kw.setdefault("socket_options", []) - - self.conn_kw["proxy"] = self.proxy - self.conn_kw["proxy_config"] = self.proxy_config - - # Do not pass 'self' as callback to 'finalize'. - # Then the 'finalize' would keep an endless living (leak) to self. - # By just passing a reference to the pool allows the garbage collector - # to free self if nobody else has a reference to it. - pool = self.pool - - # Close all the HTTPConnections in the pool before the - # HTTPConnectionPool object is garbage collected. - weakref.finalize(self, _close_pool_connections, pool) - - def _new_conn(self) -> BaseHTTPConnection: - """ - Return a fresh :class:`HTTPConnection`. - """ - self.num_connections += 1 - log.debug( - "Starting new HTTP connection (%d): %s:%s", - self.num_connections, - self.host, - self.port or "80", - ) - - conn = self.ConnectionCls( - host=self.host, - port=self.port, - timeout=self.timeout.connect_timeout, - **self.conn_kw, - ) - return conn - - def _get_conn(self, timeout: float | None = None) -> BaseHTTPConnection: - """ - Get a connection. Will return a pooled connection if one is available. - - If no connections are available and :prop:`.block` is ``False``, then a - fresh connection is returned. - - :param timeout: - Seconds to wait before giving up and raising - :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and - :prop:`.block` is ``True``. - """ - conn = None - - if self.pool is None: - raise ClosedPoolError(self, "Pool is closed.") - - try: - conn = self.pool.get(block=self.block, timeout=timeout) - - except AttributeError: # self.pool is None - raise ClosedPoolError(self, "Pool is closed.") from None # Defensive: - - except queue.Empty: - if self.block: - raise EmptyPoolError( - self, - "Pool is empty and a new connection can't be opened due to blocking mode.", - ) from None - pass # Oh well, we'll create a new connection then - - # If this is a persistent connection, check if it got disconnected - if conn and is_connection_dropped(conn): - log.debug("Resetting dropped connection: %s", self.host) - conn.close() - - return conn or self._new_conn() - - def _put_conn(self, conn: BaseHTTPConnection | None) -> None: - """ - Put a connection back into the pool. - - :param conn: - Connection object for the current host and port as returned by - :meth:`._new_conn` or :meth:`._get_conn`. - - If the pool is already full, the connection is closed and discarded - because we exceeded maxsize. If connections are discarded frequently, - then maxsize should be increased. - - If the pool is closed, then the connection will be closed and discarded. - """ - if self.pool is not None: - try: - self.pool.put(conn, block=False) - return # Everything is dandy, done. - except AttributeError: - # self.pool is None. - pass - except queue.Full: - # Connection never got put back into the pool, close it. - if conn: - conn.close() - - if self.block: - # This should never happen if you got the conn from self._get_conn - raise FullPoolError( - self, - "Pool reached maximum size and no more connections are allowed.", - ) from None - - log.warning( - "Connection pool is full, discarding connection: %s. Connection pool size: %s", - self.host, - self.pool.qsize(), - ) - - # Connection never got put back into the pool, close it. - if conn: - conn.close() - - def _validate_conn(self, conn: BaseHTTPConnection) -> None: - """ - Called right before a request is made, after the socket is created. - """ - - def _prepare_proxy(self, conn: BaseHTTPConnection) -> None: - # Nothing to do for HTTP connections. - pass - - def _get_timeout(self, timeout: _TYPE_TIMEOUT) -> Timeout: - """Helper that always returns a :class:`urllib3.util.Timeout`""" - if timeout is _DEFAULT_TIMEOUT: - return self.timeout.clone() - - if isinstance(timeout, Timeout): - return timeout.clone() - else: - # User passed us an int/float. This is for backwards compatibility, - # can be removed later - return Timeout.from_float(timeout) - - def _raise_timeout( - self, - err: BaseSSLError | OSError | SocketTimeout, - url: str, - timeout_value: _TYPE_TIMEOUT | None, - ) -> None: - """Is the error actually a timeout? Will raise a ReadTimeout or pass""" - - if isinstance(err, SocketTimeout): - raise ReadTimeoutError( - self, url, f"Read timed out. (read timeout={timeout_value})" - ) from err - - # See the above comment about EAGAIN in Python 3. - if hasattr(err, "errno") and err.errno in _blocking_errnos: - raise ReadTimeoutError( - self, url, f"Read timed out. (read timeout={timeout_value})" - ) from err - - def _make_request( - self, - conn: BaseHTTPConnection, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - retries: Retry | None = None, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - chunked: bool = False, - response_conn: BaseHTTPConnection | None = None, - preload_content: bool = True, - decode_content: bool = True, - enforce_content_length: bool = True, - ) -> BaseHTTPResponse: - """ - Perform a request on a given urllib connection object taken from our - pool. - - :param conn: - a connection from one of our connection pools - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param body: - Data to send in the request body, either :class:`str`, :class:`bytes`, - an iterable of :class:`str`/:class:`bytes`, or a file-like object. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - - :param retries: - Configure the number of retries to allow before raising a - :class:`~urllib3.exceptions.MaxRetryError` exception. - - Pass ``None`` to retry until you receive a response. Pass a - :class:`~urllib3.util.retry.Retry` object for fine-grained control - over different types of retries. - Pass an integer number to retry connection errors that many times, - but no other types of errors. Pass zero to never retry. - - If ``False``, then retries are disabled and any exception is raised - immediately. Also, instead of raising a MaxRetryError on redirects, - the redirect response will be returned. - - :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. - - :param timeout: - If specified, overrides the default timeout for this one - request. It may be a float (in seconds) or an instance of - :class:`urllib3.util.Timeout`. - - :param chunked: - If True, urllib3 will send the body using chunked transfer - encoding. Otherwise, urllib3 will send the body using the standard - content-length form. Defaults to False. - - :param response_conn: - Set this to ``None`` if you will handle releasing the connection or - set the connection to have the response release it. - - :param preload_content: - If True, the response's body will be preloaded during construction. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param enforce_content_length: - Enforce content length checking. Body returned by server must match - value of Content-Length header, if present. Otherwise, raise error. - """ - self.num_requests += 1 - - timeout_obj = self._get_timeout(timeout) - timeout_obj.start_connect() - conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout) - - try: - # Trigger any extra validation we need to do. - try: - self._validate_conn(conn) - except (SocketTimeout, BaseSSLError) as e: - self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) - raise - - # _validate_conn() starts the connection to an HTTPS proxy - # so we need to wrap errors with 'ProxyError' here too. - except ( - OSError, - NewConnectionError, - TimeoutError, - BaseSSLError, - CertificateError, - SSLError, - ) as e: - new_e: Exception = e - if isinstance(e, (BaseSSLError, CertificateError)): - new_e = SSLError(e) - # If the connection didn't successfully connect to it's proxy - # then there - if isinstance( - new_e, (OSError, NewConnectionError, TimeoutError, SSLError) - ) and (conn and conn.proxy and not conn.has_connected_to_proxy): - new_e = _wrap_proxy_error(new_e, conn.proxy.scheme) - raise new_e - - # conn.request() calls http.client.*.request, not the method in - # urllib3.request. It also calls makefile (recv) on the socket. - try: - conn.request( - method, - url, - body=body, - headers=headers, - chunked=chunked, - preload_content=preload_content, - decode_content=decode_content, - enforce_content_length=enforce_content_length, - ) - - # We are swallowing BrokenPipeError (errno.EPIPE) since the server is - # legitimately able to close the connection after sending a valid response. - # With this behaviour, the received response is still readable. - except BrokenPipeError: - pass - except OSError as e: - # MacOS/Linux - # EPROTOTYPE and ECONNRESET are needed on macOS - # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ - # Condition changed later to emit ECONNRESET instead of only EPROTOTYPE. - if e.errno != errno.EPROTOTYPE and e.errno != errno.ECONNRESET: - raise - - # Reset the timeout for the recv() on the socket - read_timeout = timeout_obj.read_timeout - - if not conn.is_closed: - # In Python 3 socket.py will catch EAGAIN and return None when you - # try and read into the file pointer created by http.client, which - # instead raises a BadStatusLine exception. Instead of catching - # the exception and assuming all BadStatusLine exceptions are read - # timeouts, check for a zero timeout before making the request. - if read_timeout == 0: - raise ReadTimeoutError( - self, url, f"Read timed out. (read timeout={read_timeout})" - ) - conn.timeout = read_timeout - - # Receive the response from the server - try: - response = conn.getresponse() - except (BaseSSLError, OSError) as e: - self._raise_timeout(err=e, url=url, timeout_value=read_timeout) - raise - - # Set properties that are used by the pooling layer. - response.retries = retries - response._connection = response_conn # type: ignore[attr-defined] - response._pool = self # type: ignore[attr-defined] - - log.debug( - '%s://%s:%s "%s %s HTTP/%s" %s %s', - self.scheme, - self.host, - self.port, - method, - url, - response.version, - response.status, - response.length_remaining, - ) - - return response - - def close(self) -> None: - """ - Close all pooled connections and disable the pool. - """ - if self.pool is None: - return - # Disable access to the pool - old_pool, self.pool = self.pool, None - - # Close all the HTTPConnections in the pool. - _close_pool_connections(old_pool) - - def is_same_host(self, url: str) -> bool: - """ - Check if the given ``url`` is a member of the same host as this - connection pool. - """ - if url.startswith("/"): - return True - - # TODO: Add optional support for socket.gethostbyname checking. - scheme, _, host, port, *_ = parse_url(url) - scheme = scheme or "http" - if host is not None: - host = _normalize_host(host, scheme=scheme) - - # Use explicit default port for comparison when none is given - if self.port and not port: - port = port_by_scheme.get(scheme) - elif not self.port and port == port_by_scheme.get(scheme): - port = None - - return (scheme, host, port) == (self.scheme, self.host, self.port) - - def urlopen( # type: ignore[override] - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - retries: Retry | bool | int | None = None, - redirect: bool = True, - assert_same_host: bool = True, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - pool_timeout: int | None = None, - release_conn: bool | None = None, - chunked: bool = False, - body_pos: _TYPE_BODY_POSITION | None = None, - preload_content: bool = True, - decode_content: bool = True, - **response_kw: typing.Any, - ) -> BaseHTTPResponse: - """ - Get a connection from the pool and perform an HTTP request. This is the - lowest level call for making a request, so you'll need to specify all - the raw details. - - .. note:: - - More commonly, it's appropriate to use a convenience method - such as :meth:`request`. - - .. note:: - - `release_conn` will only behave as expected if - `preload_content=False` because we want to make - `preload_content=False` the default behaviour someday soon without - breaking backwards compatibility. - - :param method: - HTTP request method (such as GET, POST, PUT, etc.) - - :param url: - The URL to perform the request on. - - :param body: - Data to send in the request body, either :class:`str`, :class:`bytes`, - an iterable of :class:`str`/:class:`bytes`, or a file-like object. - - :param headers: - Dictionary of custom headers to send, such as User-Agent, - If-None-Match, etc. If None, pool headers are used. If provided, - these headers completely replace any pool-specific headers. - - :param retries: - Configure the number of retries to allow before raising a - :class:`~urllib3.exceptions.MaxRetryError` exception. - - If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a - :class:`~urllib3.util.retry.Retry` object for fine-grained control - over different types of retries. - Pass an integer number to retry connection errors that many times, - but no other types of errors. Pass zero to never retry. - - If ``False``, then retries are disabled and any exception is raised - immediately. Also, instead of raising a MaxRetryError on redirects, - the redirect response will be returned. - - :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. - - :param redirect: - If True, automatically handle redirects (status codes 301, 302, - 303, 307, 308). Each redirect counts as a retry. Disabling retries - will disable redirect, too. - - :param assert_same_host: - If ``True``, will make sure that the host of the pool requests is - consistent else will raise HostChangedError. When ``False``, you can - use the pool on an HTTP proxy and request foreign hosts. - - :param timeout: - If specified, overrides the default timeout for this one - request. It may be a float (in seconds) or an instance of - :class:`urllib3.util.Timeout`. - - :param pool_timeout: - If set and the pool is set to block=True, then this method will - block for ``pool_timeout`` seconds and raise EmptyPoolError if no - connection is available within the time period. - - :param bool preload_content: - If True, the response's body will be preloaded into memory. - - :param bool decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param release_conn: - If False, then the urlopen call will not release the connection - back into the pool once a response is received (but will release if - you read the entire contents of the response such as when - `preload_content=True`). This is useful if you're not preloading - the response's content immediately. You will need to call - ``r.release_conn()`` on the response ``r`` to return the connection - back into the pool. If None, it takes the value of ``preload_content`` - which defaults to ``True``. - - :param bool chunked: - If True, urllib3 will send the body using chunked transfer - encoding. Otherwise, urllib3 will send the body using the standard - content-length form. Defaults to False. - - :param int body_pos: - Position to seek to in file-like body in the event of a retry or - redirect. Typically this won't need to be set because urllib3 will - auto-populate the value when needed. - """ - parsed_url = parse_url(url) - destination_scheme = parsed_url.scheme - - if headers is None: - headers = self.headers - - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect, default=self.retries) - - if release_conn is None: - release_conn = preload_content - - # Check host - if assert_same_host and not self.is_same_host(url): - raise HostChangedError(self, url, retries) - - # Ensure that the URL we're connecting to is properly encoded - if url.startswith("/"): - url = to_str(_encode_target(url)) - else: - url = to_str(parsed_url.url) - - conn = None - - # Track whether `conn` needs to be released before - # returning/raising/recursing. Update this variable if necessary, and - # leave `release_conn` constant throughout the function. That way, if - # the function recurses, the original value of `release_conn` will be - # passed down into the recursive call, and its value will be respected. - # - # See issue #651 [1] for details. - # - # [1] - release_this_conn = release_conn - - http_tunnel_required = connection_requires_http_tunnel( - self.proxy, self.proxy_config, destination_scheme - ) - - # Merge the proxy headers. Only done when not using HTTP CONNECT. We - # have to copy the headers dict so we can safely change it without those - # changes being reflected in anyone else's copy. - if not http_tunnel_required: - headers = headers.copy() # type: ignore[attr-defined] - headers.update(self.proxy_headers) # type: ignore[union-attr] - - # Must keep the exception bound to a separate variable or else Python 3 - # complains about UnboundLocalError. - err = None - - # Keep track of whether we cleanly exited the except block. This - # ensures we do proper cleanup in finally. - clean_exit = False - - # Rewind body position, if needed. Record current position - # for future rewinds in the event of a redirect/retry. - body_pos = set_file_position(body, body_pos) - - try: - # Request a connection from the queue. - timeout_obj = self._get_timeout(timeout) - conn = self._get_conn(timeout=pool_timeout) - - conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] - - # Is this a closed/new connection that requires CONNECT tunnelling? - if self.proxy is not None and http_tunnel_required and conn.is_closed: - try: - self._prepare_proxy(conn) - except (BaseSSLError, OSError, SocketTimeout) as e: - self._raise_timeout( - err=e, url=self.proxy.url, timeout_value=conn.timeout - ) - raise - - # If we're going to release the connection in ``finally:``, then - # the response doesn't need to know about the connection. Otherwise - # it will also try to release it and we'll have a double-release - # mess. - response_conn = conn if not release_conn else None - - # Make the request on the HTTPConnection object - response = self._make_request( - conn, - method, - url, - timeout=timeout_obj, - body=body, - headers=headers, - chunked=chunked, - retries=retries, - response_conn=response_conn, - preload_content=preload_content, - decode_content=decode_content, - **response_kw, - ) - - # Everything went great! - clean_exit = True - - except EmptyPoolError: - # Didn't get a connection from the pool, no need to clean up - clean_exit = True - release_this_conn = False - raise - - except ( - TimeoutError, - HTTPException, - OSError, - ProtocolError, - BaseSSLError, - SSLError, - CertificateError, - ProxyError, - ) as e: - # Discard the connection for these exceptions. It will be - # replaced during the next _get_conn() call. - clean_exit = False - new_e: Exception = e - if isinstance(e, (BaseSSLError, CertificateError)): - new_e = SSLError(e) - if isinstance( - new_e, - ( - OSError, - NewConnectionError, - TimeoutError, - SSLError, - HTTPException, - ), - ) and (conn and conn.proxy and not conn.has_connected_to_proxy): - new_e = _wrap_proxy_error(new_e, conn.proxy.scheme) - elif isinstance(new_e, (OSError, HTTPException)): - new_e = ProtocolError("Connection aborted.", new_e) - - retries = retries.increment( - method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2] - ) - retries.sleep() - - # Keep track of the error for the retry warning. - err = e - - finally: - if not clean_exit: - # We hit some kind of exception, handled or otherwise. We need - # to throw the connection away unless explicitly told not to. - # Close the connection, set the variable to None, and make sure - # we put the None back in the pool to avoid leaking it. - if conn: - conn.close() - conn = None - release_this_conn = True - - if release_this_conn: - # Put the connection back to be reused. If the connection is - # expired then it will be None, which will get replaced with a - # fresh connection during _get_conn. - self._put_conn(conn) - - if not conn: - # Try again - log.warning( - "Retrying (%r) after connection broken by '%r': %s", retries, err, url - ) - return self.urlopen( - method, - url, - body, - headers, - retries, - redirect, - assert_same_host, - timeout=timeout, - pool_timeout=pool_timeout, - release_conn=release_conn, - chunked=chunked, - body_pos=body_pos, - preload_content=preload_content, - decode_content=decode_content, - **response_kw, - ) - - # Handle redirect? - redirect_location = redirect and response.get_redirect_location() - if redirect_location: - if response.status == 303: - # Change the method according to RFC 9110, Section 15.4.4. - method = "GET" - # And lose the body not to transfer anything sensitive. - body = None - headers = HTTPHeaderDict(headers)._prepare_for_method_change() - - try: - retries = retries.increment(method, url, response=response, _pool=self) - except MaxRetryError: - if retries.raise_on_redirect: - response.drain_conn() - raise - return response - - response.drain_conn() - retries.sleep_for_retry(response) - log.debug("Redirecting %s -> %s", url, redirect_location) - return self.urlopen( - method, - redirect_location, - body, - headers, - retries=retries, - redirect=redirect, - assert_same_host=assert_same_host, - timeout=timeout, - pool_timeout=pool_timeout, - release_conn=release_conn, - chunked=chunked, - body_pos=body_pos, - preload_content=preload_content, - decode_content=decode_content, - **response_kw, - ) - - # Check if we should retry the HTTP response. - has_retry_after = bool(response.headers.get("Retry-After")) - if retries.is_retry(method, response.status, has_retry_after): - try: - retries = retries.increment(method, url, response=response, _pool=self) - except MaxRetryError: - if retries.raise_on_status: - response.drain_conn() - raise - return response - - response.drain_conn() - retries.sleep(response) - log.debug("Retry: %s", url) - return self.urlopen( - method, - url, - body, - headers, - retries=retries, - redirect=redirect, - assert_same_host=assert_same_host, - timeout=timeout, - pool_timeout=pool_timeout, - release_conn=release_conn, - chunked=chunked, - body_pos=body_pos, - preload_content=preload_content, - decode_content=decode_content, - **response_kw, - ) - - return response - - -class HTTPSConnectionPool(HTTPConnectionPool): - """ - Same as :class:`.HTTPConnectionPool`, but HTTPS. - - :class:`.HTTPSConnection` uses one of ``assert_fingerprint``, - ``assert_hostname`` and ``host`` in this order to verify connections. - If ``assert_hostname`` is False, no verification is done. - - The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, - ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl` - is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade - the connection socket into an SSL socket. - """ - - scheme = "https" - ConnectionCls: type[BaseHTTPSConnection] = HTTPSConnection - - def __init__( - self, - host: str, - port: int | None = None, - timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT, - maxsize: int = 1, - block: bool = False, - headers: typing.Mapping[str, str] | None = None, - retries: Retry | bool | int | None = None, - _proxy: Url | None = None, - _proxy_headers: typing.Mapping[str, str] | None = None, - key_file: str | None = None, - cert_file: str | None = None, - cert_reqs: int | str | None = None, - key_password: str | None = None, - ca_certs: str | None = None, - ssl_version: int | str | None = None, - ssl_minimum_version: ssl.TLSVersion | None = None, - ssl_maximum_version: ssl.TLSVersion | None = None, - assert_hostname: str | typing.Literal[False] | None = None, - assert_fingerprint: str | None = None, - ca_cert_dir: str | None = None, - **conn_kw: typing.Any, - ) -> None: - super().__init__( - host, - port, - timeout, - maxsize, - block, - headers, - retries, - _proxy, - _proxy_headers, - **conn_kw, - ) - - self.key_file = key_file - self.cert_file = cert_file - self.cert_reqs = cert_reqs - self.key_password = key_password - self.ca_certs = ca_certs - self.ca_cert_dir = ca_cert_dir - self.ssl_version = ssl_version - self.ssl_minimum_version = ssl_minimum_version - self.ssl_maximum_version = ssl_maximum_version - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - - def _prepare_proxy(self, conn: HTTPSConnection) -> None: # type: ignore[override] - """Establishes a tunnel connection through HTTP CONNECT.""" - if self.proxy and self.proxy.scheme == "https": - tunnel_scheme = "https" - else: - tunnel_scheme = "http" - - conn.set_tunnel( - scheme=tunnel_scheme, - host=self._tunnel_host, - port=self.port, - headers=self.proxy_headers, - ) - conn.connect() - - def _new_conn(self) -> BaseHTTPSConnection: - """ - Return a fresh :class:`urllib3.connection.HTTPConnection`. - """ - self.num_connections += 1 - log.debug( - "Starting new HTTPS connection (%d): %s:%s", - self.num_connections, - self.host, - self.port or "443", - ) - - if not self.ConnectionCls or self.ConnectionCls is DummyConnection: # type: ignore[comparison-overlap] - raise ImportError( - "Can't connect to HTTPS URL because the SSL module is not available." - ) - - actual_host: str = self.host - actual_port = self.port - if self.proxy is not None and self.proxy.host is not None: - actual_host = self.proxy.host - actual_port = self.proxy.port - - return self.ConnectionCls( - host=actual_host, - port=actual_port, - timeout=self.timeout.connect_timeout, - cert_file=self.cert_file, - key_file=self.key_file, - key_password=self.key_password, - cert_reqs=self.cert_reqs, - ca_certs=self.ca_certs, - ca_cert_dir=self.ca_cert_dir, - assert_hostname=self.assert_hostname, - assert_fingerprint=self.assert_fingerprint, - ssl_version=self.ssl_version, - ssl_minimum_version=self.ssl_minimum_version, - ssl_maximum_version=self.ssl_maximum_version, - **self.conn_kw, - ) - - def _validate_conn(self, conn: BaseHTTPConnection) -> None: - """ - Called right before a request is made, after the socket is created. - """ - super()._validate_conn(conn) - - # Force connect early to allow us to validate the connection. - if conn.is_closed: - conn.connect() - - # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791 - if not conn.is_verified and not conn.proxy_is_verified: - warnings.warn( - ( - f"Unverified HTTPS request is being made to host '{conn.host}'. " - "Adding certificate verification is strongly advised. See: " - "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" - "#tls-warnings" - ), - InsecureRequestWarning, - ) - - -def connection_from_url(url: str, **kw: typing.Any) -> HTTPConnectionPool: - """ - Given a url, return an :class:`.ConnectionPool` instance of its host. - - This is a shortcut for not having to parse out the scheme, host, and port - of the url before creating an :class:`.ConnectionPool` instance. - - :param url: - Absolute URL string that must include the scheme. Port is optional. - - :param \\**kw: - Passes additional parameters to the constructor of the appropriate - :class:`.ConnectionPool`. Useful for specifying things like - timeout, maxsize, headers, etc. - - Example:: - - >>> conn = connection_from_url('http://google.com/') - >>> r = conn.request('GET', '/') - """ - scheme, _, host, port, *_ = parse_url(url) - scheme = scheme or "http" - port = port or port_by_scheme.get(scheme, 80) - if scheme == "https": - return HTTPSConnectionPool(host, port=port, **kw) # type: ignore[arg-type] - else: - return HTTPConnectionPool(host, port=port, **kw) # type: ignore[arg-type] - - -@typing.overload -def _normalize_host(host: None, scheme: str | None) -> None: - ... - - -@typing.overload -def _normalize_host(host: str, scheme: str | None) -> str: - ... - - -def _normalize_host(host: str | None, scheme: str | None) -> str | None: - """ - Normalize hosts for comparisons and use with sockets. - """ - - host = normalize_host(host, scheme) - - # httplib doesn't like it when we include brackets in IPv6 addresses - # Specifically, if we include brackets but also pass the port then - # httplib crazily doubles up the square brackets on the Host header. - # Instead, we need to make sure we never pass ``None`` as the port. - # However, for backward compatibility reasons we can't actually - # *assert* that. See http://bugs.python.org/issue28539 - if host and host.startswith("[") and host.endswith("]"): - host = host[1:-1] - return host - - -def _url_from_pool( - pool: HTTPConnectionPool | HTTPSConnectionPool, path: str | None = None -) -> str: - """Returns the URL from a given connection pool. This is mainly used for testing and logging.""" - return Url(scheme=pool.scheme, host=pool.host, port=pool.port, path=path).url - - -def _close_pool_connections(pool: queue.LifoQueue[typing.Any]) -> None: - """Drains a queue of connections and closes each one.""" - try: - while True: - conn = pool.get(block=False) - if conn: - conn.close() - except queue.Empty: - pass # Done. diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/__init__.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/contrib/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 3ba5e1cfce28db0385301cdf5827454f5a9dfaac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 186 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 z 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 Ref%c}yhjNC$#Wc({|h=78$$p9 diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__init__.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__init__.py deleted file mode 100644 index 8a3c5be..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -import urllib3.connection - -from ...connectionpool import HTTPConnectionPool, HTTPSConnectionPool -from .connection import EmscriptenHTTPConnection, EmscriptenHTTPSConnection - - -def inject_into_urllib3() -> None: - # override connection classes to use emscripten specific classes - # n.b. mypy complains about the overriding of classes below - # if it isn't ignored - HTTPConnectionPool.ConnectionCls = EmscriptenHTTPConnection - HTTPSConnectionPool.ConnectionCls = EmscriptenHTTPSConnection - urllib3.connection.HTTPConnection = EmscriptenHTTPConnection # type: ignore[misc,assignment] - urllib3.connection.HTTPSConnection = EmscriptenHTTPSConnection # type: ignore[misc,assignment] diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index c0a96115ced4480562270241695e0cea20768ad0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 894 zcmaJljA6uwIw+et~GiPVx6Q4|lvf~*w^L={MgsDe&jPQktsxAvWNzC_B#(Erds zz=HTGOh`dUr&J6bP&TA=>cqP=c9IUf;px5a-S^(zckg?(T0vmW`)?50tG0*5sI;gv5pH-!7B#Ez=({v-XUAo)47nXx=Z04q7o74; zH*>Js>NCb=Uxu8ey;)I%1yy@#fg-Fo)iO+!haCVl^`|B0sVax{>b6mXE{ zzvL1UvQQGzO2(=lvQzkIKqMzij;Oz}?sfF%O0jeIXi<>NQRG!2K;^j4#onE7bUd P4u7M!*QSNLctQLJX+G9| diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-312.pyc deleted file mode 100644 index ec64a217342347e657b3a9cffe24b5b6b6418710..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10233 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-312.pyc deleted file mode 100644 index 25c112f9e6b17f379361ce94dc58f4870e7f1892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18366 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 F{R=@zVPgOQ diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-312.pyc deleted file mode 100644 index de64c889f3fe7336f4ca06539128931562bc1fb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12699 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/connection.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/connection.py deleted file mode 100644 index 2ceb457..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/connection.py +++ /dev/null @@ -1,254 +0,0 @@ -from __future__ import annotations - -import os -import typing - -# use http.client.HTTPException for consistency with non-emscripten -from http.client import HTTPException as HTTPException # noqa: F401 -from http.client import ResponseNotReady - -from ..._base_connection import _TYPE_BODY -from ...connection import HTTPConnection, ProxyConfig, port_by_scheme -from ...exceptions import TimeoutError -from ...response import BaseHTTPResponse -from ...util.connection import _TYPE_SOCKET_OPTIONS -from ...util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT -from ...util.url import Url -from .fetch import _RequestError, _TimeoutError, send_request, send_streaming_request -from .request import EmscriptenRequest -from .response import EmscriptenHttpResponseWrapper, EmscriptenResponse - -if typing.TYPE_CHECKING: - from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection - - -class EmscriptenHTTPConnection: - default_port: typing.ClassVar[int] = port_by_scheme["http"] - default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] - - timeout: None | (float) - - host: str - port: int - blocksize: int - source_address: tuple[str, int] | None - socket_options: _TYPE_SOCKET_OPTIONS | None - - proxy: Url | None - proxy_config: ProxyConfig | None - - is_verified: bool = False - proxy_is_verified: bool | None = None - - _response: EmscriptenResponse | None - - def __init__( - self, - host: str, - port: int = 0, - *, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - blocksize: int = 8192, - socket_options: _TYPE_SOCKET_OPTIONS | None = None, - proxy: Url | None = None, - proxy_config: ProxyConfig | None = None, - ) -> None: - self.host = host - self.port = port - self.timeout = timeout if isinstance(timeout, float) else 0.0 - self.scheme = "http" - self._closed = True - self._response = None - # ignore these things because we don't - # have control over that stuff - self.proxy = None - self.proxy_config = None - self.blocksize = blocksize - self.source_address = None - self.socket_options = None - self.is_verified = False - - def set_tunnel( - self, - host: str, - port: int | None = 0, - headers: typing.Mapping[str, str] | None = None, - scheme: str = "http", - ) -> None: - pass - - def connect(self) -> None: - pass - - def request( - self, - method: str, - url: str, - body: _TYPE_BODY | None = None, - headers: typing.Mapping[str, str] | None = None, - # We know *at least* botocore is depending on the order of the - # first 3 parameters so to be safe we only mark the later ones - # as keyword-only to ensure we have space to extend. - *, - chunked: bool = False, - preload_content: bool = True, - decode_content: bool = True, - enforce_content_length: bool = True, - ) -> None: - self._closed = False - if url.startswith("/"): - # no scheme / host / port included, make a full url - url = f"{self.scheme}://{self.host}:{self.port}" + url - request = EmscriptenRequest( - url=url, - method=method, - timeout=self.timeout if self.timeout else 0, - decode_content=decode_content, - ) - request.set_body(body) - if headers: - for k, v in headers.items(): - request.set_header(k, v) - self._response = None - try: - if not preload_content: - self._response = send_streaming_request(request) - if self._response is None: - self._response = send_request(request) - except _TimeoutError as e: - raise TimeoutError(e.message) from e - except _RequestError as e: - raise HTTPException(e.message) from e - - def getresponse(self) -> BaseHTTPResponse: - if self._response is not None: - return EmscriptenHttpResponseWrapper( - internal_response=self._response, - url=self._response.request.url, - connection=self, - ) - else: - raise ResponseNotReady() - - def close(self) -> None: - self._closed = True - self._response = None - - @property - def is_closed(self) -> bool: - """Whether the connection either is brand new or has been previously closed. - If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` - properties must be False. - """ - return self._closed - - @property - def is_connected(self) -> bool: - """Whether the connection is actively connected to any origin (proxy or target)""" - return True - - @property - def has_connected_to_proxy(self) -> bool: - """Whether the connection has successfully connected to its proxy. - This returns False if no proxy is in use. Used to determine whether - errors are coming from the proxy layer or from tunnelling to the target origin. - """ - return False - - -class EmscriptenHTTPSConnection(EmscriptenHTTPConnection): - default_port = port_by_scheme["https"] - # all this is basically ignored, as browser handles https - cert_reqs: int | str | None = None - ca_certs: str | None = None - ca_cert_dir: str | None = None - ca_cert_data: None | str | bytes = None - cert_file: str | None - key_file: str | None - key_password: str | None - ssl_context: typing.Any | None - ssl_version: int | str | None = None - ssl_minimum_version: int | None = None - ssl_maximum_version: int | None = None - assert_hostname: None | str | typing.Literal[False] - assert_fingerprint: str | None = None - - def __init__( - self, - host: str, - port: int = 0, - *, - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - blocksize: int = 16384, - socket_options: None - | _TYPE_SOCKET_OPTIONS = HTTPConnection.default_socket_options, - proxy: Url | None = None, - proxy_config: ProxyConfig | None = None, - cert_reqs: int | str | None = None, - assert_hostname: None | str | typing.Literal[False] = None, - assert_fingerprint: str | None = None, - server_hostname: str | None = None, - ssl_context: typing.Any | None = None, - ca_certs: str | None = None, - ca_cert_dir: str | None = None, - ca_cert_data: None | str | bytes = None, - ssl_minimum_version: int | None = None, - ssl_maximum_version: int | None = None, - ssl_version: int | str | None = None, # Deprecated - cert_file: str | None = None, - key_file: str | None = None, - key_password: str | None = None, - ) -> None: - super().__init__( - host, - port=port, - timeout=timeout, - source_address=source_address, - blocksize=blocksize, - socket_options=socket_options, - proxy=proxy, - proxy_config=proxy_config, - ) - self.scheme = "https" - - self.key_file = key_file - self.cert_file = cert_file - self.key_password = key_password - self.ssl_context = ssl_context - self.server_hostname = server_hostname - self.assert_hostname = assert_hostname - self.assert_fingerprint = assert_fingerprint - self.ssl_version = ssl_version - self.ssl_minimum_version = ssl_minimum_version - self.ssl_maximum_version = ssl_maximum_version - self.ca_certs = ca_certs and os.path.expanduser(ca_certs) - self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) - self.ca_cert_data = ca_cert_data - - self.cert_reqs = None - - # The browser will automatically verify all requests. - # We have no control over that setting. - self.is_verified = True - - def set_cert( - self, - key_file: str | None = None, - cert_file: str | None = None, - cert_reqs: int | str | None = None, - key_password: str | None = None, - ca_certs: str | None = None, - assert_hostname: None | str | typing.Literal[False] = None, - assert_fingerprint: str | None = None, - ca_cert_dir: str | None = None, - ca_cert_data: None | str | bytes = None, - ) -> None: - pass - - -# verify that this class implements BaseHTTP(s) connection correctly -if typing.TYPE_CHECKING: - _supports_http_protocol: BaseHTTPConnection = EmscriptenHTTPConnection("", 0) - _supports_https_protocol: BaseHTTPSConnection = EmscriptenHTTPSConnection("", 0) diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js deleted file mode 100644 index 243b862..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js +++ /dev/null @@ -1,110 +0,0 @@ -let Status = { - SUCCESS_HEADER: -1, - SUCCESS_EOF: -2, - ERROR_TIMEOUT: -3, - ERROR_EXCEPTION: -4, -}; - -let connections = {}; -let nextConnectionID = 1; -const encoder = new TextEncoder(); - -self.addEventListener("message", async function (event) { - if (event.data.close) { - let connectionID = event.data.close; - delete connections[connectionID]; - return; - } else if (event.data.getMore) { - let connectionID = event.data.getMore; - let { curOffset, value, reader, intBuffer, byteBuffer } = - connections[connectionID]; - // if we still have some in buffer, then just send it back straight away - if (!value || curOffset >= value.length) { - // read another buffer if required - try { - let readResponse = await reader.read(); - - if (readResponse.done) { - // read everything - clear connection and return - delete connections[connectionID]; - Atomics.store(intBuffer, 0, Status.SUCCESS_EOF); - Atomics.notify(intBuffer, 0); - // finished reading successfully - // return from event handler - return; - } - curOffset = 0; - connections[connectionID].value = readResponse.value; - value = readResponse.value; - } catch (error) { - console.log("Request exception:", error); - let errorBytes = encoder.encode(error.message); - let written = errorBytes.length; - byteBuffer.set(errorBytes); - intBuffer[1] = written; - Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION); - Atomics.notify(intBuffer, 0); - } - } - - // send as much buffer as we can - let curLen = value.length - curOffset; - if (curLen > byteBuffer.length) { - curLen = byteBuffer.length; - } - byteBuffer.set(value.subarray(curOffset, curOffset + curLen), 0); - - Atomics.store(intBuffer, 0, curLen); // store current length in bytes - Atomics.notify(intBuffer, 0); - curOffset += curLen; - connections[connectionID].curOffset = curOffset; - - return; - } else { - // start fetch - let connectionID = nextConnectionID; - nextConnectionID += 1; - const intBuffer = new Int32Array(event.data.buffer); - const byteBuffer = new Uint8Array(event.data.buffer, 8); - try { - const response = await fetch(event.data.url, event.data.fetchParams); - // return the headers first via textencoder - var headers = []; - for (const pair of response.headers.entries()) { - headers.push([pair[0], pair[1]]); - } - let headerObj = { - headers: headers, - status: response.status, - connectionID, - }; - const headerText = JSON.stringify(headerObj); - let headerBytes = encoder.encode(headerText); - let written = headerBytes.length; - byteBuffer.set(headerBytes); - intBuffer[1] = written; - // make a connection - connections[connectionID] = { - reader: response.body.getReader(), - intBuffer: intBuffer, - byteBuffer: byteBuffer, - value: undefined, - curOffset: 0, - }; - // set header ready - Atomics.store(intBuffer, 0, Status.SUCCESS_HEADER); - Atomics.notify(intBuffer, 0); - // all fetching after this goes through a new postmessage call with getMore - // this allows for parallel requests - } catch (error) { - console.log("Request exception:", error); - let errorBytes = encoder.encode(error.message); - let written = errorBytes.length; - byteBuffer.set(errorBytes); - intBuffer[1] = written; - Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION); - Atomics.notify(intBuffer, 0); - } - } -}); -self.postMessage({ inited: true }); diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/fetch.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/fetch.py deleted file mode 100644 index 8d197ea..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/fetch.py +++ /dev/null @@ -1,418 +0,0 @@ -""" -Support for streaming http requests in emscripten. - -A few caveats - - -Firstly, you can't do streaming http in the main UI thread, because atomics.wait isn't allowed. -Streaming only works if you're running pyodide in a web worker. - -Secondly, this uses an extra web worker and SharedArrayBuffer to do the asynchronous fetch -operation, so it requires that you have crossOriginIsolation enabled, by serving over https -(or from localhost) with the two headers below set: - - Cross-Origin-Opener-Policy: same-origin - Cross-Origin-Embedder-Policy: require-corp - -You can tell if cross origin isolation is successfully enabled by looking at the global crossOriginIsolated variable in -javascript console. If it isn't, streaming requests will fallback to XMLHttpRequest, i.e. getting the whole -request into a buffer and then returning it. it shows a warning in the javascript console in this case. - -Finally, the webworker which does the streaming fetch is created on initial import, but will only be started once -control is returned to javascript. Call `await wait_for_streaming_ready()` to wait for streaming fetch. - -NB: in this code, there are a lot of javascript objects. They are named js_* -to make it clear what type of object they are. -""" -from __future__ import annotations - -import io -import json -from email.parser import Parser -from importlib.resources import files -from typing import TYPE_CHECKING, Any - -import js # type: ignore[import-not-found] -from pyodide.ffi import ( # type: ignore[import-not-found] - JsArray, - JsException, - JsProxy, - to_js, -) - -if TYPE_CHECKING: - from typing_extensions import Buffer - -from .request import EmscriptenRequest -from .response import EmscriptenResponse - -""" -There are some headers that trigger unintended CORS preflight requests. -See also https://github.com/koenvo/pyodide-http/issues/22 -""" -HEADERS_TO_IGNORE = ("user-agent",) - -SUCCESS_HEADER = -1 -SUCCESS_EOF = -2 -ERROR_TIMEOUT = -3 -ERROR_EXCEPTION = -4 - -_STREAMING_WORKER_CODE = ( - files(__package__) - .joinpath("emscripten_fetch_worker.js") - .read_text(encoding="utf-8") -) - - -class _RequestError(Exception): - def __init__( - self, - message: str | None = None, - *, - request: EmscriptenRequest | None = None, - response: EmscriptenResponse | None = None, - ): - self.request = request - self.response = response - self.message = message - super().__init__(self.message) - - -class _StreamingError(_RequestError): - pass - - -class _TimeoutError(_RequestError): - pass - - -def _obj_from_dict(dict_val: dict[str, Any]) -> JsProxy: - return to_js(dict_val, dict_converter=js.Object.fromEntries) - - -class _ReadStream(io.RawIOBase): - def __init__( - self, - int_buffer: JsArray, - byte_buffer: JsArray, - timeout: float, - worker: JsProxy, - connection_id: int, - request: EmscriptenRequest, - ): - self.int_buffer = int_buffer - self.byte_buffer = byte_buffer - self.read_pos = 0 - self.read_len = 0 - self.connection_id = connection_id - self.worker = worker - self.timeout = int(1000 * timeout) if timeout > 0 else None - self.is_live = True - self._is_closed = False - self.request: EmscriptenRequest | None = request - - def __del__(self) -> None: - self.close() - - # this is compatible with _base_connection - def is_closed(self) -> bool: - return self._is_closed - - # for compatibility with RawIOBase - @property - def closed(self) -> bool: - return self.is_closed() - - def close(self) -> None: - if not self.is_closed(): - self.read_len = 0 - self.read_pos = 0 - self.int_buffer = None - self.byte_buffer = None - self._is_closed = True - self.request = None - if self.is_live: - self.worker.postMessage(_obj_from_dict({"close": self.connection_id})) - self.is_live = False - super().close() - - def readable(self) -> bool: - return True - - def writable(self) -> bool: - return False - - def seekable(self) -> bool: - return False - - def readinto(self, byte_obj: Buffer) -> int: - if not self.int_buffer: - raise _StreamingError( - "No buffer for stream in _ReadStream.readinto", - request=self.request, - response=None, - ) - if self.read_len == 0: - # wait for the worker to send something - js.Atomics.store(self.int_buffer, 0, ERROR_TIMEOUT) - self.worker.postMessage(_obj_from_dict({"getMore": self.connection_id})) - if ( - js.Atomics.wait(self.int_buffer, 0, ERROR_TIMEOUT, self.timeout) - == "timed-out" - ): - raise _TimeoutError - data_len = self.int_buffer[0] - if data_len > 0: - self.read_len = data_len - self.read_pos = 0 - elif data_len == ERROR_EXCEPTION: - string_len = self.int_buffer[1] - # decode the error string - js_decoder = js.TextDecoder.new() - json_str = js_decoder.decode(self.byte_buffer.slice(0, string_len)) - raise _StreamingError( - f"Exception thrown in fetch: {json_str}", - request=self.request, - response=None, - ) - else: - # EOF, free the buffers and return zero - # and free the request - self.is_live = False - self.close() - return 0 - # copy from int32array to python bytes - ret_length = min(self.read_len, len(memoryview(byte_obj))) - subarray = self.byte_buffer.subarray( - self.read_pos, self.read_pos + ret_length - ).to_py() - memoryview(byte_obj)[0:ret_length] = subarray - self.read_len -= ret_length - self.read_pos += ret_length - return ret_length - - -class _StreamingFetcher: - def __init__(self) -> None: - # make web-worker and data buffer on startup - self.streaming_ready = False - - js_data_blob = js.Blob.new( - [_STREAMING_WORKER_CODE], _obj_from_dict({"type": "application/javascript"}) - ) - - def promise_resolver(js_resolve_fn: JsProxy, js_reject_fn: JsProxy) -> None: - def onMsg(e: JsProxy) -> None: - self.streaming_ready = True - js_resolve_fn(e) - - def onErr(e: JsProxy) -> None: - js_reject_fn(e) # Defensive: never happens in ci - - self.js_worker.onmessage = onMsg - self.js_worker.onerror = onErr - - js_data_url = js.URL.createObjectURL(js_data_blob) - self.js_worker = js.globalThis.Worker.new(js_data_url) - self.js_worker_ready_promise = js.globalThis.Promise.new(promise_resolver) - - def send(self, request: EmscriptenRequest) -> EmscriptenResponse: - headers = { - k: v for k, v in request.headers.items() if k not in HEADERS_TO_IGNORE - } - - body = request.body - fetch_data = {"headers": headers, "body": to_js(body), "method": request.method} - # start the request off in the worker - timeout = int(1000 * request.timeout) if request.timeout > 0 else None - js_shared_buffer = js.SharedArrayBuffer.new(1048576) - js_int_buffer = js.Int32Array.new(js_shared_buffer) - js_byte_buffer = js.Uint8Array.new(js_shared_buffer, 8) - - js.Atomics.store(js_int_buffer, 0, ERROR_TIMEOUT) - js.Atomics.notify(js_int_buffer, 0) - js_absolute_url = js.URL.new(request.url, js.location).href - self.js_worker.postMessage( - _obj_from_dict( - { - "buffer": js_shared_buffer, - "url": js_absolute_url, - "fetchParams": fetch_data, - } - ) - ) - # wait for the worker to send something - js.Atomics.wait(js_int_buffer, 0, ERROR_TIMEOUT, timeout) - if js_int_buffer[0] == ERROR_TIMEOUT: - raise _TimeoutError( - "Timeout connecting to streaming request", - request=request, - response=None, - ) - elif js_int_buffer[0] == SUCCESS_HEADER: - # got response - # header length is in second int of intBuffer - string_len = js_int_buffer[1] - # decode the rest to a JSON string - js_decoder = js.TextDecoder.new() - # this does a copy (the slice) because decode can't work on shared array - # for some silly reason - json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len)) - # get it as an object - response_obj = json.loads(json_str) - return EmscriptenResponse( - request=request, - status_code=response_obj["status"], - headers=response_obj["headers"], - body=_ReadStream( - js_int_buffer, - js_byte_buffer, - request.timeout, - self.js_worker, - response_obj["connectionID"], - request, - ), - ) - elif js_int_buffer[0] == ERROR_EXCEPTION: - string_len = js_int_buffer[1] - # decode the error string - js_decoder = js.TextDecoder.new() - json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len)) - raise _StreamingError( - f"Exception thrown in fetch: {json_str}", request=request, response=None - ) - else: - raise _StreamingError( - f"Unknown status from worker in fetch: {js_int_buffer[0]}", - request=request, - response=None, - ) - - -# check if we are in a worker or not -def is_in_browser_main_thread() -> bool: - return hasattr(js, "window") and hasattr(js, "self") and js.self == js.window - - -def is_cross_origin_isolated() -> bool: - return hasattr(js, "crossOriginIsolated") and js.crossOriginIsolated - - -def is_in_node() -> bool: - return ( - hasattr(js, "process") - and hasattr(js.process, "release") - and hasattr(js.process.release, "name") - and js.process.release.name == "node" - ) - - -def is_worker_available() -> bool: - return hasattr(js, "Worker") and hasattr(js, "Blob") - - -_fetcher: _StreamingFetcher | None = None - -if is_worker_available() and ( - (is_cross_origin_isolated() and not is_in_browser_main_thread()) - and (not is_in_node()) -): - _fetcher = _StreamingFetcher() -else: - _fetcher = None - - -def send_streaming_request(request: EmscriptenRequest) -> EmscriptenResponse | None: - if _fetcher and streaming_ready(): - return _fetcher.send(request) - else: - _show_streaming_warning() - return None - - -_SHOWN_TIMEOUT_WARNING = False - - -def _show_timeout_warning() -> None: - global _SHOWN_TIMEOUT_WARNING - if not _SHOWN_TIMEOUT_WARNING: - _SHOWN_TIMEOUT_WARNING = True - message = "Warning: Timeout is not available on main browser thread" - js.console.warn(message) - - -_SHOWN_STREAMING_WARNING = False - - -def _show_streaming_warning() -> None: - global _SHOWN_STREAMING_WARNING - if not _SHOWN_STREAMING_WARNING: - _SHOWN_STREAMING_WARNING = True - message = "Can't stream HTTP requests because: \n" - if not is_cross_origin_isolated(): - message += " Page is not cross-origin isolated\n" - if is_in_browser_main_thread(): - message += " Python is running in main browser thread\n" - if not is_worker_available(): - message += " Worker or Blob classes are not available in this environment." # Defensive: this is always False in browsers that we test in - if streaming_ready() is False: - message += """ Streaming fetch worker isn't ready. If you want to be sure that streaming fetch -is working, you need to call: 'await urllib3.contrib.emscripten.fetch.wait_for_streaming_ready()`""" - from js import console - - console.warn(message) - - -def send_request(request: EmscriptenRequest) -> EmscriptenResponse: - try: - js_xhr = js.XMLHttpRequest.new() - - if not is_in_browser_main_thread(): - js_xhr.responseType = "arraybuffer" - if request.timeout: - js_xhr.timeout = int(request.timeout * 1000) - else: - js_xhr.overrideMimeType("text/plain; charset=ISO-8859-15") - if request.timeout: - # timeout isn't available on the main thread - show a warning in console - # if it is set - _show_timeout_warning() - - js_xhr.open(request.method, request.url, False) - for name, value in request.headers.items(): - if name.lower() not in HEADERS_TO_IGNORE: - js_xhr.setRequestHeader(name, value) - - js_xhr.send(to_js(request.body)) - - headers = dict(Parser().parsestr(js_xhr.getAllResponseHeaders())) - - if not is_in_browser_main_thread(): - body = js_xhr.response.to_py().tobytes() - else: - body = js_xhr.response.encode("ISO-8859-15") - return EmscriptenResponse( - status_code=js_xhr.status, headers=headers, body=body, request=request - ) - except JsException as err: - if err.name == "TimeoutError": - raise _TimeoutError(err.message, request=request) - elif err.name == "NetworkError": - raise _RequestError(err.message, request=request) - else: - # general http error - raise _RequestError(err.message, request=request) - - -def streaming_ready() -> bool | None: - if _fetcher: - return _fetcher.streaming_ready - else: - return None # no fetcher, return None to signify that - - -async def wait_for_streaming_ready() -> bool: - if _fetcher: - await _fetcher.js_worker_ready_promise - return True - else: - return False diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/request.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/request.py deleted file mode 100644 index e692e69..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/request.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field - -from ..._base_connection import _TYPE_BODY - - -@dataclass -class EmscriptenRequest: - method: str - url: str - params: dict[str, str] | None = None - body: _TYPE_BODY | None = None - headers: dict[str, str] = field(default_factory=dict) - timeout: float = 0 - decode_content: bool = True - - def set_header(self, name: str, value: str) -> None: - self.headers[name.capitalize()] = value - - def set_body(self, body: _TYPE_BODY | None) -> None: - self.body = body diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/response.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/response.py deleted file mode 100644 index cd3d80e..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/response.py +++ /dev/null @@ -1,285 +0,0 @@ -from __future__ import annotations - -import json as _json -import logging -import typing -from contextlib import contextmanager -from dataclasses import dataclass -from http.client import HTTPException as HTTPException -from io import BytesIO, IOBase - -from ...exceptions import InvalidHeader, TimeoutError -from ...response import BaseHTTPResponse -from ...util.retry import Retry -from .request import EmscriptenRequest - -if typing.TYPE_CHECKING: - from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection - -log = logging.getLogger(__name__) - - -@dataclass -class EmscriptenResponse: - status_code: int - headers: dict[str, str] - body: IOBase | bytes - request: EmscriptenRequest - - -class EmscriptenHttpResponseWrapper(BaseHTTPResponse): - def __init__( - self, - internal_response: EmscriptenResponse, - url: str | None = None, - connection: BaseHTTPConnection | BaseHTTPSConnection | None = None, - ): - self._pool = None # set by pool class - self._body = None - self._response = internal_response - self._url = url - self._connection = connection - self._closed = False - super().__init__( - headers=internal_response.headers, - status=internal_response.status_code, - request_url=url, - version=0, - version_string="HTTP/?", - reason="", - decode_content=True, - ) - self.length_remaining = self._init_length(self._response.request.method) - self.length_is_certain = False - - @property - def url(self) -> str | None: - return self._url - - @url.setter - def url(self, url: str | None) -> None: - self._url = url - - @property - def connection(self) -> BaseHTTPConnection | BaseHTTPSConnection | None: - return self._connection - - @property - def retries(self) -> Retry | None: - return self._retries - - @retries.setter - def retries(self, retries: Retry | None) -> None: - # Override the request_url if retries has a redirect location. - self._retries = retries - - def stream( - self, amt: int | None = 2**16, decode_content: bool | None = None - ) -> typing.Generator[bytes, None, None]: - """ - A generator wrapper for the read() method. A call will block until - ``amt`` bytes have been read from the connection or until the - connection is closed. - - :param amt: - How much of the content to read. The generator will return up to - much data per iteration, but may return less. This is particularly - likely when using compressed data. However, the empty string will - never be returned. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - while True: - data = self.read(amt=amt, decode_content=decode_content) - - if data: - yield data - else: - break - - def _init_length(self, request_method: str | None) -> int | None: - length: int | None - content_length: str | None = self.headers.get("content-length") - - if content_length is not None: - try: - # RFC 7230 section 3.3.2 specifies multiple content lengths can - # be sent in a single Content-Length header - # (e.g. Content-Length: 42, 42). This line ensures the values - # are all valid ints and that as long as the `set` length is 1, - # all values are the same. Otherwise, the header is invalid. - lengths = {int(val) for val in content_length.split(",")} - if len(lengths) > 1: - raise InvalidHeader( - "Content-Length contained multiple " - "unmatching values (%s)" % content_length - ) - length = lengths.pop() - except ValueError: - length = None - else: - if length < 0: - length = None - - else: # if content_length is None - length = None - - # Check for responses that shouldn't include a body - if ( - self.status in (204, 304) - or 100 <= self.status < 200 - or request_method == "HEAD" - ): - length = 0 - - return length - - def read( - self, - amt: int | None = None, - decode_content: bool | None = None, # ignored because browser decodes always - cache_content: bool = False, - ) -> bytes: - if ( - self._closed - or self._response is None - or (isinstance(self._response.body, IOBase) and self._response.body.closed) - ): - return b"" - - with self._error_catcher(): - # body has been preloaded as a string by XmlHttpRequest - if not isinstance(self._response.body, IOBase): - self.length_remaining = len(self._response.body) - self.length_is_certain = True - # wrap body in IOStream - self._response.body = BytesIO(self._response.body) - if amt is not None and amt >= 0: - # don't cache partial content - cache_content = False - data = self._response.body.read(amt) - if self.length_remaining is not None: - self.length_remaining = max(self.length_remaining - len(data), 0) - if (self.length_is_certain and self.length_remaining == 0) or len( - data - ) < amt: - # definitely finished reading, close response stream - self._response.body.close() - return typing.cast(bytes, data) - else: # read all we can (and cache it) - data = self._response.body.read() - if cache_content: - self._body = data - if self.length_remaining is not None: - self.length_remaining = max(self.length_remaining - len(data), 0) - if len(data) == 0 or ( - self.length_is_certain and self.length_remaining == 0 - ): - # definitely finished reading, close response stream - self._response.body.close() - return typing.cast(bytes, data) - - def read_chunked( - self, - amt: int | None = None, - decode_content: bool | None = None, - ) -> typing.Generator[bytes, None, None]: - # chunked is handled by browser - while True: - bytes = self.read(amt, decode_content) - if not bytes: - break - yield bytes - - def release_conn(self) -> None: - if not self._pool or not self._connection: - return None - - self._pool._put_conn(self._connection) - self._connection = None - - def drain_conn(self) -> None: - self.close() - - @property - def data(self) -> bytes: - if self._body: - return self._body - else: - return self.read(cache_content=True) - - def json(self) -> typing.Any: - """ - Deserializes the body of the HTTP response as a Python object. - - The body of the HTTP response must be encoded using UTF-8, as per - `RFC 8529 Section 8.1 `_. - - To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to - your custom decoder instead. - - If the body of the HTTP response is not decodable to UTF-8, a - `UnicodeDecodeError` will be raised. If the body of the HTTP response is not a - valid JSON document, a `json.JSONDecodeError` will be raised. - - Read more :ref:`here `. - - :returns: The body of the HTTP response as a Python object. - """ - data = self.data.decode("utf-8") - return _json.loads(data) - - def close(self) -> None: - if not self._closed: - if isinstance(self._response.body, IOBase): - self._response.body.close() - if self._connection: - self._connection.close() - self._connection = None - self._closed = True - - @contextmanager - def _error_catcher(self) -> typing.Generator[None, None, None]: - """ - Catch Emscripten specific exceptions thrown by fetch.py, - instead re-raising urllib3 variants, so that low-level exceptions - are not leaked in the high-level api. - - On exit, release the connection back to the pool. - """ - from .fetch import _RequestError, _TimeoutError # avoid circular import - - clean_exit = False - - try: - yield - # If no exception is thrown, we should avoid cleaning up - # unnecessarily. - clean_exit = True - except _TimeoutError as e: - raise TimeoutError(str(e)) - except _RequestError as e: - raise HTTPException(str(e)) - finally: - # If we didn't terminate cleanly, we need to throw away our - # connection. - if not clean_exit: - # The response may not be closed but we're not going to use it - # anymore so close it now - if ( - isinstance(self._response.body, IOBase) - and not self._response.body.closed - ): - self._response.body.close() - # release the connection back to the pool - self.release_conn() - else: - # If we have read everything from the response stream, - # return the connection back to the pool. - if ( - isinstance(self._response.body, IOBase) - and self._response.body.closed - ): - self.release_conn() diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/pyopenssl.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/pyopenssl.py deleted file mode 100644 index b89a6da..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/pyopenssl.py +++ /dev/null @@ -1,548 +0,0 @@ -""" -Module for using pyOpenSSL as a TLS backend. This module was relevant before -the standard library ``ssl`` module supported SNI, but now that we've dropped -support for Python 2.7 all relevant Python versions support SNI so -**this module is no longer recommended**. - -This needs the following packages installed: - -* `pyOpenSSL`_ (tested with 16.0.0) -* `cryptography`_ (minimum 1.3.4, from pyopenssl) -* `idna`_ (minimum 2.0) - -However, pyOpenSSL depends on cryptography, so while we use all three directly here we -end up having relatively few packages required. - -You can install them with the following command: - -.. code-block:: bash - - $ python -m pip install pyopenssl cryptography idna - -To activate certificate checking, call -:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code -before you begin making HTTP requests. This can be done in a ``sitecustomize`` -module, or at any other time before your application begins using ``urllib3``, -like this: - -.. code-block:: python - - try: - import urllib3.contrib.pyopenssl - urllib3.contrib.pyopenssl.inject_into_urllib3() - except ImportError: - pass - -.. _pyopenssl: https://www.pyopenssl.org -.. _cryptography: https://cryptography.io -.. _idna: https://github.com/kjd/idna -""" - -from __future__ import annotations - -import OpenSSL.SSL # type: ignore[import-untyped] -from cryptography import x509 - -try: - from cryptography.x509 import UnsupportedExtension # type: ignore[attr-defined] -except ImportError: - # UnsupportedExtension is gone in cryptography >= 2.1.0 - class UnsupportedExtension(Exception): # type: ignore[no-redef] - pass - - -import logging -import ssl -import typing -from io import BytesIO -from socket import socket as socket_cls -from socket import timeout - -from .. import util - -if typing.TYPE_CHECKING: - from OpenSSL.crypto import X509 # type: ignore[import-untyped] - - -__all__ = ["inject_into_urllib3", "extract_from_urllib3"] - -# Map from urllib3 to PyOpenSSL compatible parameter-values. -_openssl_versions: dict[int, int] = { - util.ssl_.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined] - util.ssl_.PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined] - ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, -} - -if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"): - _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD - -if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"): - _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD - - -_stdlib_to_openssl_verify = { - ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, - ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, - ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER - + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, -} -_openssl_to_stdlib_verify = {v: k for k, v in _stdlib_to_openssl_verify.items()} - -# The SSLvX values are the most likely to be missing in the future -# but we check them all just to be sure. -_OP_NO_SSLv2_OR_SSLv3: int = getattr(OpenSSL.SSL, "OP_NO_SSLv2", 0) | getattr( - OpenSSL.SSL, "OP_NO_SSLv3", 0 -) -_OP_NO_TLSv1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1", 0) -_OP_NO_TLSv1_1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_1", 0) -_OP_NO_TLSv1_2: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_2", 0) -_OP_NO_TLSv1_3: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_3", 0) - -_openssl_to_ssl_minimum_version: dict[int, int] = { - ssl.TLSVersion.MINIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3, - ssl.TLSVersion.TLSv1: _OP_NO_SSLv2_OR_SSLv3, - ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1, - ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1, - ssl.TLSVersion.TLSv1_3: ( - _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 - ), - ssl.TLSVersion.MAXIMUM_SUPPORTED: ( - _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 - ), -} -_openssl_to_ssl_maximum_version: dict[int, int] = { - ssl.TLSVersion.MINIMUM_SUPPORTED: ( - _OP_NO_SSLv2_OR_SSLv3 - | _OP_NO_TLSv1 - | _OP_NO_TLSv1_1 - | _OP_NO_TLSv1_2 - | _OP_NO_TLSv1_3 - ), - ssl.TLSVersion.TLSv1: ( - _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3 - ), - ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3, - ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_3, - ssl.TLSVersion.TLSv1_3: _OP_NO_SSLv2_OR_SSLv3, - ssl.TLSVersion.MAXIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3, -} - -# OpenSSL will only write 16K at a time -SSL_WRITE_BLOCKSIZE = 16384 - -orig_util_SSLContext = util.ssl_.SSLContext - - -log = logging.getLogger(__name__) - - -def inject_into_urllib3() -> None: - "Monkey-patch urllib3 with PyOpenSSL-backed SSL-support." - - _validate_dependencies_met() - - util.SSLContext = PyOpenSSLContext # type: ignore[assignment] - util.ssl_.SSLContext = PyOpenSSLContext # type: ignore[assignment] - util.IS_PYOPENSSL = True - util.ssl_.IS_PYOPENSSL = True - - -def extract_from_urllib3() -> None: - "Undo monkey-patching by :func:`inject_into_urllib3`." - - util.SSLContext = orig_util_SSLContext - util.ssl_.SSLContext = orig_util_SSLContext - util.IS_PYOPENSSL = False - util.ssl_.IS_PYOPENSSL = False - - -def _validate_dependencies_met() -> None: - """ - Verifies that PyOpenSSL's package-level dependencies have been met. - Throws `ImportError` if they are not met. - """ - # Method added in `cryptography==1.1`; not available in older versions - from cryptography.x509.extensions import Extensions - - if getattr(Extensions, "get_extension_for_class", None) is None: - raise ImportError( - "'cryptography' module missing required functionality. " - "Try upgrading to v1.3.4 or newer." - ) - - # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 - # attribute is only present on those versions. - from OpenSSL.crypto import X509 - - x509 = X509() - if getattr(x509, "_x509", None) is None: - raise ImportError( - "'pyOpenSSL' module missing required functionality. " - "Try upgrading to v0.14 or newer." - ) - - -def _dnsname_to_stdlib(name: str) -> str | None: - """ - Converts a dNSName SubjectAlternativeName field to the form used by the - standard library on the given Python version. - - Cryptography produces a dNSName as a unicode string that was idna-decoded - from ASCII bytes. We need to idna-encode that string to get it back, and - then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib - uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). - - If the name cannot be idna-encoded then we return None signalling that - the name given should be skipped. - """ - - def idna_encode(name: str) -> bytes | None: - """ - Borrowed wholesale from the Python Cryptography Project. It turns out - that we can't just safely call `idna.encode`: it can explode for - wildcard names. This avoids that problem. - """ - import idna - - try: - for prefix in ["*.", "."]: - if name.startswith(prefix): - name = name[len(prefix) :] - return prefix.encode("ascii") + idna.encode(name) - return idna.encode(name) - except idna.core.IDNAError: - return None - - # Don't send IPv6 addresses through the IDNA encoder. - if ":" in name: - return name - - encoded_name = idna_encode(name) - if encoded_name is None: - return None - return encoded_name.decode("utf-8") - - -def get_subj_alt_name(peer_cert: X509) -> list[tuple[str, str]]: - """ - Given an PyOpenSSL certificate, provides all the subject alternative names. - """ - cert = peer_cert.to_cryptography() - - # We want to find the SAN extension. Ask Cryptography to locate it (it's - # faster than looping in Python) - try: - ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value - except x509.ExtensionNotFound: - # No such extension, return the empty list. - return [] - except ( - x509.DuplicateExtension, - UnsupportedExtension, - x509.UnsupportedGeneralNameType, - UnicodeError, - ) as e: - # A problem has been found with the quality of the certificate. Assume - # no SAN field is present. - log.warning( - "A problem was encountered with the certificate that prevented " - "urllib3 from finding the SubjectAlternativeName field. This can " - "affect certificate validation. The error was %s", - e, - ) - return [] - - # We want to return dNSName and iPAddress fields. We need to cast the IPs - # back to strings because the match_hostname function wants them as - # strings. - # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 - # decoded. This is pretty frustrating, but that's what the standard library - # does with certificates, and so we need to attempt to do the same. - # We also want to skip over names which cannot be idna encoded. - names = [ - ("DNS", name) - for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) - if name is not None - ] - names.extend( - ("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress) - ) - - return names - - -class WrappedSocket: - """API-compatibility wrapper for Python OpenSSL's Connection-class.""" - - def __init__( - self, - connection: OpenSSL.SSL.Connection, - socket: socket_cls, - suppress_ragged_eofs: bool = True, - ) -> None: - self.connection = connection - self.socket = socket - self.suppress_ragged_eofs = suppress_ragged_eofs - self._io_refs = 0 - self._closed = False - - def fileno(self) -> int: - return self.socket.fileno() - - # Copy-pasted from Python 3.5 source code - def _decref_socketios(self) -> None: - if self._io_refs > 0: - self._io_refs -= 1 - if self._closed: - self.close() - - def recv(self, *args: typing.Any, **kwargs: typing.Any) -> bytes: - try: - data = self.connection.recv(*args, **kwargs) - except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): - return b"" - else: - raise OSError(e.args[0], str(e)) from e - except OpenSSL.SSL.ZeroReturnError: - if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return b"" - else: - raise - except OpenSSL.SSL.WantReadError as e: - if not util.wait_for_read(self.socket, self.socket.gettimeout()): - raise timeout("The read operation timed out") from e - else: - return self.recv(*args, **kwargs) - - # TLS 1.3 post-handshake authentication - except OpenSSL.SSL.Error as e: - raise ssl.SSLError(f"read error: {e!r}") from e - else: - return data # type: ignore[no-any-return] - - def recv_into(self, *args: typing.Any, **kwargs: typing.Any) -> int: - try: - return self.connection.recv_into(*args, **kwargs) # type: ignore[no-any-return] - except OpenSSL.SSL.SysCallError as e: - if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): - return 0 - else: - raise OSError(e.args[0], str(e)) from e - except OpenSSL.SSL.ZeroReturnError: - if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: - return 0 - else: - raise - except OpenSSL.SSL.WantReadError as e: - if not util.wait_for_read(self.socket, self.socket.gettimeout()): - raise timeout("The read operation timed out") from e - else: - return self.recv_into(*args, **kwargs) - - # TLS 1.3 post-handshake authentication - except OpenSSL.SSL.Error as e: - raise ssl.SSLError(f"read error: {e!r}") from e - - def settimeout(self, timeout: float) -> None: - return self.socket.settimeout(timeout) - - def _send_until_done(self, data: bytes) -> int: - while True: - try: - return self.connection.send(data) # type: ignore[no-any-return] - except OpenSSL.SSL.WantWriteError as e: - if not util.wait_for_write(self.socket, self.socket.gettimeout()): - raise timeout() from e - continue - except OpenSSL.SSL.SysCallError as e: - raise OSError(e.args[0], str(e)) from e - - def sendall(self, data: bytes) -> None: - total_sent = 0 - while total_sent < len(data): - sent = self._send_until_done( - data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE] - ) - total_sent += sent - - def shutdown(self) -> None: - # FIXME rethrow compatible exceptions should we ever use this - self.connection.shutdown() - - def close(self) -> None: - self._closed = True - if self._io_refs <= 0: - self._real_close() - - def _real_close(self) -> None: - try: - return self.connection.close() # type: ignore[no-any-return] - except OpenSSL.SSL.Error: - return - - def getpeercert( - self, binary_form: bool = False - ) -> dict[str, list[typing.Any]] | None: - x509 = self.connection.get_peer_certificate() - - if not x509: - return x509 # type: ignore[no-any-return] - - if binary_form: - return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) # type: ignore[no-any-return] - - return { - "subject": ((("commonName", x509.get_subject().CN),),), # type: ignore[dict-item] - "subjectAltName": get_subj_alt_name(x509), - } - - def version(self) -> str: - return self.connection.get_protocol_version_name() # type: ignore[no-any-return] - - -WrappedSocket.makefile = socket_cls.makefile # type: ignore[attr-defined] - - -class PyOpenSSLContext: - """ - I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible - for translating the interface of the standard library ``SSLContext`` object - to calls into PyOpenSSL. - """ - - def __init__(self, protocol: int) -> None: - self.protocol = _openssl_versions[protocol] - self._ctx = OpenSSL.SSL.Context(self.protocol) - self._options = 0 - self.check_hostname = False - self._minimum_version: int = ssl.TLSVersion.MINIMUM_SUPPORTED - self._maximum_version: int = ssl.TLSVersion.MAXIMUM_SUPPORTED - - @property - def options(self) -> int: - return self._options - - @options.setter - def options(self, value: int) -> None: - self._options = value - self._set_ctx_options() - - @property - def verify_mode(self) -> int: - return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] - - @verify_mode.setter - def verify_mode(self, value: ssl.VerifyMode) -> None: - self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback) - - def set_default_verify_paths(self) -> None: - self._ctx.set_default_verify_paths() - - def set_ciphers(self, ciphers: bytes | str) -> None: - if isinstance(ciphers, str): - ciphers = ciphers.encode("utf-8") - self._ctx.set_cipher_list(ciphers) - - def load_verify_locations( - self, - cafile: str | None = None, - capath: str | None = None, - cadata: bytes | None = None, - ) -> None: - if cafile is not None: - cafile = cafile.encode("utf-8") # type: ignore[assignment] - if capath is not None: - capath = capath.encode("utf-8") # type: ignore[assignment] - try: - self._ctx.load_verify_locations(cafile, capath) - if cadata is not None: - self._ctx.load_verify_locations(BytesIO(cadata)) - except OpenSSL.SSL.Error as e: - raise ssl.SSLError(f"unable to load trusted certificates: {e!r}") from e - - def load_cert_chain( - self, - certfile: str, - keyfile: str | None = None, - password: str | None = None, - ) -> None: - try: - self._ctx.use_certificate_chain_file(certfile) - if password is not None: - if not isinstance(password, bytes): - password = password.encode("utf-8") # type: ignore[assignment] - self._ctx.set_passwd_cb(lambda *_: password) - self._ctx.use_privatekey_file(keyfile or certfile) - except OpenSSL.SSL.Error as e: - raise ssl.SSLError(f"Unable to load certificate chain: {e!r}") from e - - def set_alpn_protocols(self, protocols: list[bytes | str]) -> None: - protocols = [util.util.to_bytes(p, "ascii") for p in protocols] - return self._ctx.set_alpn_protos(protocols) # type: ignore[no-any-return] - - def wrap_socket( - self, - sock: socket_cls, - server_side: bool = False, - do_handshake_on_connect: bool = True, - suppress_ragged_eofs: bool = True, - server_hostname: bytes | str | None = None, - ) -> WrappedSocket: - cnx = OpenSSL.SSL.Connection(self._ctx, sock) - - # If server_hostname is an IP, don't use it for SNI, per RFC6066 Section 3 - if server_hostname and not util.ssl_.is_ipaddress(server_hostname): - if isinstance(server_hostname, str): - server_hostname = server_hostname.encode("utf-8") - cnx.set_tlsext_host_name(server_hostname) - - cnx.set_connect_state() - - while True: - try: - cnx.do_handshake() - except OpenSSL.SSL.WantReadError as e: - if not util.wait_for_read(sock, sock.gettimeout()): - raise timeout("select timed out") from e - continue - except OpenSSL.SSL.Error as e: - raise ssl.SSLError(f"bad handshake: {e!r}") from e - break - - return WrappedSocket(cnx, sock) - - def _set_ctx_options(self) -> None: - self._ctx.set_options( - self._options - | _openssl_to_ssl_minimum_version[self._minimum_version] - | _openssl_to_ssl_maximum_version[self._maximum_version] - ) - - @property - def minimum_version(self) -> int: - return self._minimum_version - - @minimum_version.setter - def minimum_version(self, minimum_version: int) -> None: - self._minimum_version = minimum_version - self._set_ctx_options() - - @property - def maximum_version(self) -> int: - return self._maximum_version - - @maximum_version.setter - def maximum_version(self, maximum_version: int) -> None: - self._maximum_version = maximum_version - self._set_ctx_options() - - -def _verify_callback( - cnx: OpenSSL.SSL.Connection, - x509: X509, - err_no: int, - err_depth: int, - return_code: int, -) -> bool: - return err_no == 0 diff --git a/.venv/lib/python3.12/site-packages/urllib3/contrib/socks.py b/.venv/lib/python3.12/site-packages/urllib3/contrib/socks.py deleted file mode 100644 index c62b5e0..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/contrib/socks.py +++ /dev/null @@ -1,228 +0,0 @@ -""" -This module contains provisional support for SOCKS proxies from within -urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and -SOCKS5. To enable its functionality, either install PySocks or install this -module with the ``socks`` extra. - -The SOCKS implementation supports the full range of urllib3 features. It also -supports the following SOCKS features: - -- SOCKS4A (``proxy_url='socks4a://...``) -- SOCKS4 (``proxy_url='socks4://...``) -- SOCKS5 with remote DNS (``proxy_url='socks5h://...``) -- SOCKS5 with local DNS (``proxy_url='socks5://...``) -- Usernames and passwords for the SOCKS proxy - -.. note:: - It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in - your ``proxy_url`` to ensure that DNS resolution is done from the remote - server instead of client-side when connecting to a domain name. - -SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 -supports IPv4, IPv6, and domain names. - -When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` -will be sent as the ``userid`` section of the SOCKS request: - -.. code-block:: python - - proxy_url="socks4a://@proxy-host" - -When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion -of the ``proxy_url`` will be sent as the username/password to authenticate -with the proxy: - -.. code-block:: python - - proxy_url="socks5h://:@proxy-host" - -""" - -from __future__ import annotations - -try: - import socks # type: ignore[import-not-found] -except ImportError: - import warnings - - from ..exceptions import DependencyWarning - - warnings.warn( - ( - "SOCKS support in urllib3 requires the installation of optional " - "dependencies: specifically, PySocks. For more information, see " - "https://urllib3.readthedocs.io/en/latest/advanced-usage.html#socks-proxies" - ), - DependencyWarning, - ) - raise - -import typing -from socket import timeout as SocketTimeout - -from ..connection import HTTPConnection, HTTPSConnection -from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool -from ..exceptions import ConnectTimeoutError, NewConnectionError -from ..poolmanager import PoolManager -from ..util.url import parse_url - -try: - import ssl -except ImportError: - ssl = None # type: ignore[assignment] - - -class _TYPE_SOCKS_OPTIONS(typing.TypedDict): - socks_version: int - proxy_host: str | None - proxy_port: str | None - username: str | None - password: str | None - rdns: bool - - -class SOCKSConnection(HTTPConnection): - """ - A plain-text HTTP connection that connects via a SOCKS proxy. - """ - - def __init__( - self, - _socks_options: _TYPE_SOCKS_OPTIONS, - *args: typing.Any, - **kwargs: typing.Any, - ) -> None: - self._socks_options = _socks_options - super().__init__(*args, **kwargs) - - def _new_conn(self) -> socks.socksocket: - """ - Establish a new connection via the SOCKS proxy. - """ - extra_kw: dict[str, typing.Any] = {} - if self.source_address: - extra_kw["source_address"] = self.source_address - - if self.socket_options: - extra_kw["socket_options"] = self.socket_options - - try: - conn = socks.create_connection( - (self.host, self.port), - proxy_type=self._socks_options["socks_version"], - proxy_addr=self._socks_options["proxy_host"], - proxy_port=self._socks_options["proxy_port"], - proxy_username=self._socks_options["username"], - proxy_password=self._socks_options["password"], - proxy_rdns=self._socks_options["rdns"], - timeout=self.timeout, - **extra_kw, - ) - - except SocketTimeout as e: - raise ConnectTimeoutError( - self, - f"Connection to {self.host} timed out. (connect timeout={self.timeout})", - ) from e - - except socks.ProxyError as e: - # This is fragile as hell, but it seems to be the only way to raise - # useful errors here. - if e.socket_err: - error = e.socket_err - if isinstance(error, SocketTimeout): - raise ConnectTimeoutError( - self, - f"Connection to {self.host} timed out. (connect timeout={self.timeout})", - ) from e - else: - # Adding `from e` messes with coverage somehow, so it's omitted. - # See #2386. - raise NewConnectionError( - self, f"Failed to establish a new connection: {error}" - ) - else: - raise NewConnectionError( - self, f"Failed to establish a new connection: {e}" - ) from e - - except OSError as e: # Defensive: PySocks should catch all these. - raise NewConnectionError( - self, f"Failed to establish a new connection: {e}" - ) from e - - return conn - - -# We don't need to duplicate the Verified/Unverified distinction from -# urllib3/connection.py here because the HTTPSConnection will already have been -# correctly set to either the Verified or Unverified form by that module. This -# means the SOCKSHTTPSConnection will automatically be the correct type. -class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): - pass - - -class SOCKSHTTPConnectionPool(HTTPConnectionPool): - ConnectionCls = SOCKSConnection - - -class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): - ConnectionCls = SOCKSHTTPSConnection - - -class SOCKSProxyManager(PoolManager): - """ - A version of the urllib3 ProxyManager that routes connections via the - defined SOCKS proxy. - """ - - pool_classes_by_scheme = { - "http": SOCKSHTTPConnectionPool, - "https": SOCKSHTTPSConnectionPool, - } - - def __init__( - self, - proxy_url: str, - username: str | None = None, - password: str | None = None, - num_pools: int = 10, - headers: typing.Mapping[str, str] | None = None, - **connection_pool_kw: typing.Any, - ): - parsed = parse_url(proxy_url) - - if username is None and password is None and parsed.auth is not None: - split = parsed.auth.split(":") - if len(split) == 2: - username, password = split - if parsed.scheme == "socks5": - socks_version = socks.PROXY_TYPE_SOCKS5 - rdns = False - elif parsed.scheme == "socks5h": - socks_version = socks.PROXY_TYPE_SOCKS5 - rdns = True - elif parsed.scheme == "socks4": - socks_version = socks.PROXY_TYPE_SOCKS4 - rdns = False - elif parsed.scheme == "socks4a": - socks_version = socks.PROXY_TYPE_SOCKS4 - rdns = True - else: - raise ValueError(f"Unable to determine SOCKS version from {proxy_url}") - - self.proxy_url = proxy_url - - socks_options = { - "socks_version": socks_version, - "proxy_host": parsed.host, - "proxy_port": parsed.port, - "username": username, - "password": password, - "rdns": rdns, - } - connection_pool_kw["_socks_options"] = socks_options - - super().__init__(num_pools, headers, **connection_pool_kw) - - self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/.venv/lib/python3.12/site-packages/urllib3/exceptions.py b/.venv/lib/python3.12/site-packages/urllib3/exceptions.py deleted file mode 100644 index b0792f0..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/exceptions.py +++ /dev/null @@ -1,321 +0,0 @@ -from __future__ import annotations - -import socket -import typing -import warnings -from email.errors import MessageDefect -from http.client import IncompleteRead as httplib_IncompleteRead - -if typing.TYPE_CHECKING: - from .connection import HTTPConnection - from .connectionpool import ConnectionPool - from .response import HTTPResponse - from .util.retry import Retry - -# Base Exceptions - - -class HTTPError(Exception): - """Base exception used by this module.""" - - -class HTTPWarning(Warning): - """Base warning used by this module.""" - - -_TYPE_REDUCE_RESULT = typing.Tuple[ - typing.Callable[..., object], typing.Tuple[object, ...] -] - - -class PoolError(HTTPError): - """Base exception for errors caused within a pool.""" - - def __init__(self, pool: ConnectionPool, message: str) -> None: - self.pool = pool - super().__init__(f"{pool}: {message}") - - def __reduce__(self) -> _TYPE_REDUCE_RESULT: - # For pickling purposes. - return self.__class__, (None, None) - - -class RequestError(PoolError): - """Base exception for PoolErrors that have associated URLs.""" - - def __init__(self, pool: ConnectionPool, url: str, message: str) -> None: - self.url = url - super().__init__(pool, message) - - def __reduce__(self) -> _TYPE_REDUCE_RESULT: - # For pickling purposes. - return self.__class__, (None, self.url, None) - - -class SSLError(HTTPError): - """Raised when SSL certificate fails in an HTTPS connection.""" - - -class ProxyError(HTTPError): - """Raised when the connection to a proxy fails.""" - - # The original error is also available as __cause__. - original_error: Exception - - def __init__(self, message: str, error: Exception) -> None: - super().__init__(message, error) - self.original_error = error - - -class DecodeError(HTTPError): - """Raised when automatic decoding based on Content-Type fails.""" - - -class ProtocolError(HTTPError): - """Raised when something unexpected happens mid-request/response.""" - - -#: Renamed to ProtocolError but aliased for backwards compatibility. -ConnectionError = ProtocolError - - -# Leaf Exceptions - - -class MaxRetryError(RequestError): - """Raised when the maximum number of retries is exceeded. - - :param pool: The connection pool - :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` - :param str url: The requested Url - :param reason: The underlying error - :type reason: :class:`Exception` - - """ - - def __init__( - self, pool: ConnectionPool, url: str, reason: Exception | None = None - ) -> None: - self.reason = reason - - message = f"Max retries exceeded with url: {url} (Caused by {reason!r})" - - super().__init__(pool, url, message) - - -class HostChangedError(RequestError): - """Raised when an existing pool gets a request for a foreign host.""" - - def __init__( - self, pool: ConnectionPool, url: str, retries: Retry | int = 3 - ) -> None: - message = f"Tried to open a foreign host with url: {url}" - super().__init__(pool, url, message) - self.retries = retries - - -class TimeoutStateError(HTTPError): - """Raised when passing an invalid state to a timeout""" - - -class TimeoutError(HTTPError): - """Raised when a socket timeout error occurs. - - Catching this error will catch both :exc:`ReadTimeoutErrors - ` and :exc:`ConnectTimeoutErrors `. - """ - - -class ReadTimeoutError(TimeoutError, RequestError): - """Raised when a socket timeout occurs while receiving data from a server""" - - -# This timeout error does not have a URL attached and needs to inherit from the -# base HTTPError -class ConnectTimeoutError(TimeoutError): - """Raised when a socket timeout occurs while connecting to a server""" - - -class NewConnectionError(ConnectTimeoutError, HTTPError): - """Raised when we fail to establish a new connection. Usually ECONNREFUSED.""" - - def __init__(self, conn: HTTPConnection, message: str) -> None: - self.conn = conn - super().__init__(f"{conn}: {message}") - - @property - def pool(self) -> HTTPConnection: - warnings.warn( - "The 'pool' property is deprecated and will be removed " - "in urllib3 v2.1.0. Use 'conn' instead.", - DeprecationWarning, - stacklevel=2, - ) - - return self.conn - - -class NameResolutionError(NewConnectionError): - """Raised when host name resolution fails.""" - - def __init__(self, host: str, conn: HTTPConnection, reason: socket.gaierror): - message = f"Failed to resolve '{host}' ({reason})" - super().__init__(conn, message) - - -class EmptyPoolError(PoolError): - """Raised when a pool runs out of connections and no more are allowed.""" - - -class FullPoolError(PoolError): - """Raised when we try to add a connection to a full pool in blocking mode.""" - - -class ClosedPoolError(PoolError): - """Raised when a request enters a pool after the pool has been closed.""" - - -class LocationValueError(ValueError, HTTPError): - """Raised when there is something wrong with a given URL input.""" - - -class LocationParseError(LocationValueError): - """Raised when get_host or similar fails to parse the URL input.""" - - def __init__(self, location: str) -> None: - message = f"Failed to parse: {location}" - super().__init__(message) - - self.location = location - - -class URLSchemeUnknown(LocationValueError): - """Raised when a URL input has an unsupported scheme.""" - - def __init__(self, scheme: str): - message = f"Not supported URL scheme {scheme}" - super().__init__(message) - - self.scheme = scheme - - -class ResponseError(HTTPError): - """Used as a container for an error reason supplied in a MaxRetryError.""" - - GENERIC_ERROR = "too many error responses" - SPECIFIC_ERROR = "too many {status_code} error responses" - - -class SecurityWarning(HTTPWarning): - """Warned when performing security reducing actions""" - - -class InsecureRequestWarning(SecurityWarning): - """Warned when making an unverified HTTPS request.""" - - -class NotOpenSSLWarning(SecurityWarning): - """Warned when using unsupported SSL library""" - - -class SystemTimeWarning(SecurityWarning): - """Warned when system time is suspected to be wrong""" - - -class InsecurePlatformWarning(SecurityWarning): - """Warned when certain TLS/SSL configuration is not available on a platform.""" - - -class DependencyWarning(HTTPWarning): - """ - Warned when an attempt is made to import a module with missing optional - dependencies. - """ - - -class ResponseNotChunked(ProtocolError, ValueError): - """Response needs to be chunked in order to read it as chunks.""" - - -class BodyNotHttplibCompatible(HTTPError): - """ - Body should be :class:`http.client.HTTPResponse` like - (have an fp attribute which returns raw chunks) for read_chunked(). - """ - - -class IncompleteRead(HTTPError, httplib_IncompleteRead): - """ - Response length doesn't match expected Content-Length - - Subclass of :class:`http.client.IncompleteRead` to allow int value - for ``partial`` to avoid creating large objects on streamed reads. - """ - - partial: int # type: ignore[assignment] - expected: int - - def __init__(self, partial: int, expected: int) -> None: - self.partial = partial - self.expected = expected - - def __repr__(self) -> str: - return "IncompleteRead(%i bytes read, %i more expected)" % ( - self.partial, - self.expected, - ) - - -class InvalidChunkLength(HTTPError, httplib_IncompleteRead): - """Invalid chunk length in a chunked response.""" - - def __init__(self, response: HTTPResponse, length: bytes) -> None: - self.partial: int = response.tell() # type: ignore[assignment] - self.expected: int | None = response.length_remaining - self.response = response - self.length = length - - def __repr__(self) -> str: - return "InvalidChunkLength(got length %r, %i bytes read)" % ( - self.length, - self.partial, - ) - - -class InvalidHeader(HTTPError): - """The header provided was somehow invalid.""" - - -class ProxySchemeUnknown(AssertionError, URLSchemeUnknown): - """ProxyManager does not support the supplied scheme""" - - # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. - - def __init__(self, scheme: str | None) -> None: - # 'localhost' is here because our URL parser parses - # localhost:8080 -> scheme=localhost, remove if we fix this. - if scheme == "localhost": - scheme = None - if scheme is None: - message = "Proxy URL had no scheme, should start with http:// or https://" - else: - message = f"Proxy URL had unsupported scheme {scheme}, should use http:// or https://" - super().__init__(message) - - -class ProxySchemeUnsupported(ValueError): - """Fetching HTTPS resources through HTTPS proxies is unsupported""" - - -class HeaderParsingError(HTTPError): - """Raised by assert_header_parsing, but we convert it to a log.warning statement.""" - - def __init__( - self, defects: list[MessageDefect], unparsed_data: bytes | str | None - ) -> None: - message = f"{defects or 'Unknown'}, unparsed data: {unparsed_data!r}" - super().__init__(message) - - -class UnrewindableBodyError(HTTPError): - """urllib3 encountered an error when trying to rewind a body""" diff --git a/.venv/lib/python3.12/site-packages/urllib3/fields.py b/.venv/lib/python3.12/site-packages/urllib3/fields.py deleted file mode 100644 index 3e258a5..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/fields.py +++ /dev/null @@ -1,341 +0,0 @@ -from __future__ import annotations - -import email.utils -import mimetypes -import typing - -_TYPE_FIELD_VALUE = typing.Union[str, bytes] -_TYPE_FIELD_VALUE_TUPLE = typing.Union[ - _TYPE_FIELD_VALUE, - typing.Tuple[str, _TYPE_FIELD_VALUE], - typing.Tuple[str, _TYPE_FIELD_VALUE, str], -] - - -def guess_content_type( - filename: str | None, default: str = "application/octet-stream" -) -> str: - """ - Guess the "Content-Type" of a file. - - :param filename: - The filename to guess the "Content-Type" of using :mod:`mimetypes`. - :param default: - If no "Content-Type" can be guessed, default to `default`. - """ - if filename: - return mimetypes.guess_type(filename)[0] or default - return default - - -def format_header_param_rfc2231(name: str, value: _TYPE_FIELD_VALUE) -> str: - """ - Helper function to format and quote a single header parameter using the - strategy defined in RFC 2231. - - Particularly useful for header parameters which might contain - non-ASCII values, like file names. This follows - `RFC 2388 Section 4.4 `_. - - :param name: - The name of the parameter, a string expected to be ASCII only. - :param value: - The value of the parameter, provided as ``bytes`` or `str``. - :returns: - An RFC-2231-formatted unicode string. - - .. deprecated:: 2.0.0 - Will be removed in urllib3 v2.1.0. This is not valid for - ``multipart/form-data`` header parameters. - """ - import warnings - - warnings.warn( - "'format_header_param_rfc2231' is deprecated and will be " - "removed in urllib3 v2.1.0. This is not valid for " - "multipart/form-data header parameters.", - DeprecationWarning, - stacklevel=2, - ) - - if isinstance(value, bytes): - value = value.decode("utf-8") - - if not any(ch in value for ch in '"\\\r\n'): - result = f'{name}="{value}"' - try: - result.encode("ascii") - except (UnicodeEncodeError, UnicodeDecodeError): - pass - else: - return result - - value = email.utils.encode_rfc2231(value, "utf-8") - value = f"{name}*={value}" - - return value - - -def format_multipart_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str: - """ - Format and quote a single multipart header parameter. - - This follows the `WHATWG HTML Standard`_ as of 2021/06/10, matching - the behavior of current browser and curl versions. Values are - assumed to be UTF-8. The ``\\n``, ``\\r``, and ``"`` characters are - percent encoded. - - .. _WHATWG HTML Standard: - https://html.spec.whatwg.org/multipage/ - form-control-infrastructure.html#multipart-form-data - - :param name: - The name of the parameter, an ASCII-only ``str``. - :param value: - The value of the parameter, a ``str`` or UTF-8 encoded - ``bytes``. - :returns: - A string ``name="value"`` with the escaped value. - - .. versionchanged:: 2.0.0 - Matches the WHATWG HTML Standard as of 2021/06/10. Control - characters are no longer percent encoded. - - .. versionchanged:: 2.0.0 - Renamed from ``format_header_param_html5`` and - ``format_header_param``. The old names will be removed in - urllib3 v2.1.0. - """ - if isinstance(value, bytes): - value = value.decode("utf-8") - - # percent encode \n \r " - value = value.translate({10: "%0A", 13: "%0D", 34: "%22"}) - return f'{name}="{value}"' - - -def format_header_param_html5(name: str, value: _TYPE_FIELD_VALUE) -> str: - """ - .. deprecated:: 2.0.0 - Renamed to :func:`format_multipart_header_param`. Will be - removed in urllib3 v2.1.0. - """ - import warnings - - warnings.warn( - "'format_header_param_html5' has been renamed to " - "'format_multipart_header_param'. The old name will be " - "removed in urllib3 v2.1.0.", - DeprecationWarning, - stacklevel=2, - ) - return format_multipart_header_param(name, value) - - -def format_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str: - """ - .. deprecated:: 2.0.0 - Renamed to :func:`format_multipart_header_param`. Will be - removed in urllib3 v2.1.0. - """ - import warnings - - warnings.warn( - "'format_header_param' has been renamed to " - "'format_multipart_header_param'. The old name will be " - "removed in urllib3 v2.1.0.", - DeprecationWarning, - stacklevel=2, - ) - return format_multipart_header_param(name, value) - - -class RequestField: - """ - A data container for request body parameters. - - :param name: - The name of this request field. Must be unicode. - :param data: - The data/value body. - :param filename: - An optional filename of the request field. Must be unicode. - :param headers: - An optional dict-like object of headers to initially use for the field. - - .. versionchanged:: 2.0.0 - The ``header_formatter`` parameter is deprecated and will - be removed in urllib3 v2.1.0. - """ - - def __init__( - self, - name: str, - data: _TYPE_FIELD_VALUE, - filename: str | None = None, - headers: typing.Mapping[str, str] | None = None, - header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None, - ): - self._name = name - self._filename = filename - self.data = data - self.headers: dict[str, str | None] = {} - if headers: - self.headers = dict(headers) - - if header_formatter is not None: - import warnings - - warnings.warn( - "The 'header_formatter' parameter is deprecated and " - "will be removed in urllib3 v2.1.0.", - DeprecationWarning, - stacklevel=2, - ) - self.header_formatter = header_formatter - else: - self.header_formatter = format_multipart_header_param - - @classmethod - def from_tuples( - cls, - fieldname: str, - value: _TYPE_FIELD_VALUE_TUPLE, - header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None, - ) -> RequestField: - """ - A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. - - Supports constructing :class:`~urllib3.fields.RequestField` from - parameter of key/value strings AND key/filetuple. A filetuple is a - (filename, data, MIME type) tuple where the MIME type is optional. - For example:: - - 'foo': 'bar', - 'fakefile': ('foofile.txt', 'contents of foofile'), - 'realfile': ('barfile.txt', open('realfile').read()), - 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), - 'nonamefile': 'contents of nonamefile field', - - Field names and filenames must be unicode. - """ - filename: str | None - content_type: str | None - data: _TYPE_FIELD_VALUE - - if isinstance(value, tuple): - if len(value) == 3: - filename, data, content_type = value - else: - filename, data = value - content_type = guess_content_type(filename) - else: - filename = None - content_type = None - data = value - - request_param = cls( - fieldname, data, filename=filename, header_formatter=header_formatter - ) - request_param.make_multipart(content_type=content_type) - - return request_param - - def _render_part(self, name: str, value: _TYPE_FIELD_VALUE) -> str: - """ - Override this method to change how each multipart header - parameter is formatted. By default, this calls - :func:`format_multipart_header_param`. - - :param name: - The name of the parameter, an ASCII-only ``str``. - :param value: - The value of the parameter, a ``str`` or UTF-8 encoded - ``bytes``. - - :meta public: - """ - return self.header_formatter(name, value) - - def _render_parts( - self, - header_parts: ( - dict[str, _TYPE_FIELD_VALUE | None] - | typing.Sequence[tuple[str, _TYPE_FIELD_VALUE | None]] - ), - ) -> str: - """ - Helper function to format and quote a single header. - - Useful for single headers that are composed of multiple items. E.g., - 'Content-Disposition' fields. - - :param header_parts: - A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format - as `k1="v1"; k2="v2"; ...`. - """ - iterable: typing.Iterable[tuple[str, _TYPE_FIELD_VALUE | None]] - - parts = [] - if isinstance(header_parts, dict): - iterable = header_parts.items() - else: - iterable = header_parts - - for name, value in iterable: - if value is not None: - parts.append(self._render_part(name, value)) - - return "; ".join(parts) - - def render_headers(self) -> str: - """ - Renders the headers for this request field. - """ - lines = [] - - sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"] - for sort_key in sort_keys: - if self.headers.get(sort_key, False): - lines.append(f"{sort_key}: {self.headers[sort_key]}") - - for header_name, header_value in self.headers.items(): - if header_name not in sort_keys: - if header_value: - lines.append(f"{header_name}: {header_value}") - - lines.append("\r\n") - return "\r\n".join(lines) - - def make_multipart( - self, - content_disposition: str | None = None, - content_type: str | None = None, - content_location: str | None = None, - ) -> None: - """ - Makes this request field into a multipart request field. - - This method overrides "Content-Disposition", "Content-Type" and - "Content-Location" headers to the request parameter. - - :param content_disposition: - The 'Content-Disposition' of the request body. Defaults to 'form-data' - :param content_type: - The 'Content-Type' of the request body. - :param content_location: - The 'Content-Location' of the request body. - - """ - content_disposition = (content_disposition or "form-data") + "; ".join( - [ - "", - self._render_parts( - (("name", self._name), ("filename", self._filename)) - ), - ] - ) - - self.headers["Content-Disposition"] = content_disposition - self.headers["Content-Type"] = content_type - self.headers["Content-Location"] = content_location diff --git a/.venv/lib/python3.12/site-packages/urllib3/filepost.py b/.venv/lib/python3.12/site-packages/urllib3/filepost.py deleted file mode 100644 index 1c90a21..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/filepost.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -import binascii -import codecs -import os -import typing -from io import BytesIO - -from .fields import _TYPE_FIELD_VALUE_TUPLE, RequestField - -writer = codecs.lookup("utf-8")[3] - -_TYPE_FIELDS_SEQUENCE = typing.Sequence[ - typing.Union[typing.Tuple[str, _TYPE_FIELD_VALUE_TUPLE], RequestField] -] -_TYPE_FIELDS = typing.Union[ - _TYPE_FIELDS_SEQUENCE, - typing.Mapping[str, _TYPE_FIELD_VALUE_TUPLE], -] - - -def choose_boundary() -> str: - """ - Our embarrassingly-simple replacement for mimetools.choose_boundary. - """ - return binascii.hexlify(os.urandom(16)).decode() - - -def iter_field_objects(fields: _TYPE_FIELDS) -> typing.Iterable[RequestField]: - """ - Iterate over fields. - - Supports list of (k, v) tuples and dicts, and lists of - :class:`~urllib3.fields.RequestField`. - - """ - iterable: typing.Iterable[RequestField | tuple[str, _TYPE_FIELD_VALUE_TUPLE]] - - if isinstance(fields, typing.Mapping): - iterable = fields.items() - else: - iterable = fields - - for field in iterable: - if isinstance(field, RequestField): - yield field - else: - yield RequestField.from_tuples(*field) - - -def encode_multipart_formdata( - fields: _TYPE_FIELDS, boundary: str | None = None -) -> tuple[bytes, str]: - """ - Encode a dictionary of ``fields`` using the multipart/form-data MIME format. - - :param fields: - Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). - Values are processed by :func:`urllib3.fields.RequestField.from_tuples`. - - :param boundary: - If not specified, then a random boundary will be generated using - :func:`urllib3.filepost.choose_boundary`. - """ - body = BytesIO() - if boundary is None: - boundary = choose_boundary() - - for field in iter_field_objects(fields): - body.write(f"--{boundary}\r\n".encode("latin-1")) - - writer(body).write(field.render_headers()) - data = field.data - - if isinstance(data, int): - data = str(data) # Backwards compatibility - - if isinstance(data, str): - writer(body).write(data) - else: - body.write(data) - - body.write(b"\r\n") - - body.write(f"--{boundary}--\r\n".encode("latin-1")) - - content_type = f"multipart/form-data; boundary={boundary}" - - return body.getvalue(), content_type diff --git a/.venv/lib/python3.12/site-packages/urllib3/http2.py b/.venv/lib/python3.12/site-packages/urllib3/http2.py deleted file mode 100644 index ceb4060..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/http2.py +++ /dev/null @@ -1,230 +0,0 @@ -from __future__ import annotations - -import threading -import types -import typing - -import h2.config # type: ignore[import-untyped] -import h2.connection # type: ignore[import-untyped] -import h2.events # type: ignore[import-untyped] - -import urllib3.connection -import urllib3.util.ssl_ -from urllib3.response import BaseHTTPResponse - -from ._collections import HTTPHeaderDict -from .connection import HTTPSConnection -from .connectionpool import HTTPSConnectionPool - -orig_HTTPSConnection = HTTPSConnection - -T = typing.TypeVar("T") - - -class _LockedObject(typing.Generic[T]): - """ - A wrapper class that hides a specific object behind a lock. - - The goal here is to provide a simple way to protect access to an object - that cannot safely be simultaneously accessed from multiple threads. The - intended use of this class is simple: take hold of it with a context - manager, which returns the protected object. - """ - - def __init__(self, obj: T): - self.lock = threading.RLock() - self._obj = obj - - def __enter__(self) -> T: - self.lock.acquire() - return self._obj - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: types.TracebackType | None, - ) -> None: - self.lock.release() - - -class HTTP2Connection(HTTPSConnection): - def __init__( - self, host: str, port: int | None = None, **kwargs: typing.Any - ) -> None: - self._h2_conn = self._new_h2_conn() - self._h2_stream: int | None = None - self._h2_headers: list[tuple[bytes, bytes]] = [] - - if "proxy" in kwargs or "proxy_config" in kwargs: # Defensive: - raise NotImplementedError("Proxies aren't supported with HTTP/2") - - super().__init__(host, port, **kwargs) - - def _new_h2_conn(self) -> _LockedObject[h2.connection.H2Connection]: - config = h2.config.H2Configuration(client_side=True) - return _LockedObject(h2.connection.H2Connection(config=config)) - - def connect(self) -> None: - super().connect() - - with self._h2_conn as h2_conn: - h2_conn.initiate_connection() - self.sock.sendall(h2_conn.data_to_send()) - - def putrequest( - self, - method: str, - url: str, - skip_host: bool = False, - skip_accept_encoding: bool = False, - ) -> None: - with self._h2_conn as h2_conn: - self._request_url = url - self._h2_stream = h2_conn.get_next_available_stream_id() - - if ":" in self.host: - authority = f"[{self.host}]:{self.port or 443}" - else: - authority = f"{self.host}:{self.port or 443}" - - self._h2_headers.extend( - ( - (b":scheme", b"https"), - (b":method", method.encode()), - (b":authority", authority.encode()), - (b":path", url.encode()), - ) - ) - - def putheader(self, header: str, *values: str) -> None: # type: ignore[override] - for value in values: - self._h2_headers.append( - (header.encode("utf-8").lower(), value.encode("utf-8")) - ) - - def endheaders(self) -> None: # type: ignore[override] - with self._h2_conn as h2_conn: - h2_conn.send_headers( - stream_id=self._h2_stream, - headers=self._h2_headers, - end_stream=True, - ) - if data_to_send := h2_conn.data_to_send(): - self.sock.sendall(data_to_send) - - def send(self, data: bytes) -> None: # type: ignore[override] # Defensive: - if not data: - return - raise NotImplementedError("Sending data isn't supported yet") - - def getresponse( # type: ignore[override] - self, - ) -> HTTP2Response: - status = None - data = bytearray() - with self._h2_conn as h2_conn: - end_stream = False - while not end_stream: - # TODO: Arbitrary read value. - if received_data := self.sock.recv(65535): - events = h2_conn.receive_data(received_data) - for event in events: - if isinstance(event, h2.events.ResponseReceived): - headers = HTTPHeaderDict() - for header, value in event.headers: - if header == b":status": - status = int(value.decode()) - else: - headers.add( - header.decode("ascii"), value.decode("ascii") - ) - - elif isinstance(event, h2.events.DataReceived): - data += event.data - h2_conn.acknowledge_received_data( - event.flow_controlled_length, event.stream_id - ) - - elif isinstance(event, h2.events.StreamEnded): - end_stream = True - - if data_to_send := h2_conn.data_to_send(): - self.sock.sendall(data_to_send) - - # We always close to not have to handle connection management. - self.close() - - assert status is not None - return HTTP2Response( - status=status, - headers=headers, - request_url=self._request_url, - data=bytes(data), - ) - - def close(self) -> None: - with self._h2_conn as h2_conn: - try: - h2_conn.close_connection() - if data := h2_conn.data_to_send(): - self.sock.sendall(data) - except Exception: - pass - - # Reset all our HTTP/2 connection state. - self._h2_conn = self._new_h2_conn() - self._h2_stream = None - self._h2_headers = [] - - super().close() - - -class HTTP2Response(BaseHTTPResponse): - # TODO: This is a woefully incomplete response object, but works for non-streaming. - def __init__( - self, - status: int, - headers: HTTPHeaderDict, - request_url: str, - data: bytes, - decode_content: bool = False, # TODO: support decoding - ) -> None: - super().__init__( - status=status, - headers=headers, - # Following CPython, we map HTTP versions to major * 10 + minor integers - version=20, - version_string="HTTP/2", - # No reason phrase in HTTP/2 - reason=None, - decode_content=decode_content, - request_url=request_url, - ) - self._data = data - self.length_remaining = 0 - - @property - def data(self) -> bytes: - return self._data - - def get_redirect_location(self) -> None: - return None - - def close(self) -> None: - pass - - -def inject_into_urllib3() -> None: - HTTPSConnectionPool.ConnectionCls = HTTP2Connection - urllib3.connection.HTTPSConnection = HTTP2Connection # type: ignore[misc] - - # TODO: Offer 'http/1.1' as well, but for testing purposes this is handy. - urllib3.util.ssl_.ALPN_PROTOCOLS = ["h2"] - - -def extract_from_urllib3() -> None: - HTTPSConnectionPool.ConnectionCls = orig_HTTPSConnection - urllib3.connection.HTTPSConnection = orig_HTTPSConnection # type: ignore[misc] - - urllib3.util.ssl_.ALPN_PROTOCOLS = ["http/1.1"] diff --git a/.venv/lib/python3.12/site-packages/urllib3/poolmanager.py b/.venv/lib/python3.12/site-packages/urllib3/poolmanager.py deleted file mode 100644 index 085d1db..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/poolmanager.py +++ /dev/null @@ -1,637 +0,0 @@ -from __future__ import annotations - -import functools -import logging -import typing -import warnings -from types import TracebackType -from urllib.parse import urljoin - -from ._collections import HTTPHeaderDict, RecentlyUsedContainer -from ._request_methods import RequestMethods -from .connection import ProxyConfig -from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme -from .exceptions import ( - LocationValueError, - MaxRetryError, - ProxySchemeUnknown, - URLSchemeUnknown, -) -from .response import BaseHTTPResponse -from .util.connection import _TYPE_SOCKET_OPTIONS -from .util.proxy import connection_requires_http_tunnel -from .util.retry import Retry -from .util.timeout import Timeout -from .util.url import Url, parse_url - -if typing.TYPE_CHECKING: - import ssl - - from typing_extensions import Self - -__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] - - -log = logging.getLogger(__name__) - -SSL_KEYWORDS = ( - "key_file", - "cert_file", - "cert_reqs", - "ca_certs", - "ca_cert_data", - "ssl_version", - "ssl_minimum_version", - "ssl_maximum_version", - "ca_cert_dir", - "ssl_context", - "key_password", - "server_hostname", -) -# Default value for `blocksize` - a new parameter introduced to -# http.client.HTTPConnection & http.client.HTTPSConnection in Python 3.7 -_DEFAULT_BLOCKSIZE = 16384 - - -class PoolKey(typing.NamedTuple): - """ - All known keyword arguments that could be provided to the pool manager, its - pools, or the underlying connections. - - All custom key schemes should include the fields in this key at a minimum. - """ - - key_scheme: str - key_host: str - key_port: int | None - key_timeout: Timeout | float | int | None - key_retries: Retry | bool | int | None - key_block: bool | None - key_source_address: tuple[str, int] | None - key_key_file: str | None - key_key_password: str | None - key_cert_file: str | None - key_cert_reqs: str | None - key_ca_certs: str | None - key_ca_cert_data: str | bytes | None - key_ssl_version: int | str | None - key_ssl_minimum_version: ssl.TLSVersion | None - key_ssl_maximum_version: ssl.TLSVersion | None - key_ca_cert_dir: str | None - key_ssl_context: ssl.SSLContext | None - key_maxsize: int | None - key_headers: frozenset[tuple[str, str]] | None - key__proxy: Url | None - key__proxy_headers: frozenset[tuple[str, str]] | None - key__proxy_config: ProxyConfig | None - key_socket_options: _TYPE_SOCKET_OPTIONS | None - key__socks_options: frozenset[tuple[str, str]] | None - key_assert_hostname: bool | str | None - key_assert_fingerprint: str | None - key_server_hostname: str | None - key_blocksize: int | None - - -def _default_key_normalizer( - key_class: type[PoolKey], request_context: dict[str, typing.Any] -) -> PoolKey: - """ - Create a pool key out of a request context dictionary. - - According to RFC 3986, both the scheme and host are case-insensitive. - Therefore, this function normalizes both before constructing the pool - key for an HTTPS request. If you wish to change this behaviour, provide - alternate callables to ``key_fn_by_scheme``. - - :param key_class: - The class to use when constructing the key. This should be a namedtuple - with the ``scheme`` and ``host`` keys at a minimum. - :type key_class: namedtuple - :param request_context: - A dictionary-like object that contain the context for a request. - :type request_context: dict - - :return: A namedtuple that can be used as a connection pool key. - :rtype: PoolKey - """ - # Since we mutate the dictionary, make a copy first - context = request_context.copy() - context["scheme"] = context["scheme"].lower() - context["host"] = context["host"].lower() - - # These are both dictionaries and need to be transformed into frozensets - for key in ("headers", "_proxy_headers", "_socks_options"): - if key in context and context[key] is not None: - context[key] = frozenset(context[key].items()) - - # The socket_options key may be a list and needs to be transformed into a - # tuple. - socket_opts = context.get("socket_options") - if socket_opts is not None: - context["socket_options"] = tuple(socket_opts) - - # Map the kwargs to the names in the namedtuple - this is necessary since - # namedtuples can't have fields starting with '_'. - for key in list(context.keys()): - context["key_" + key] = context.pop(key) - - # Default to ``None`` for keys missing from the context - for field in key_class._fields: - if field not in context: - context[field] = None - - # Default key_blocksize to _DEFAULT_BLOCKSIZE if missing from the context - if context.get("key_blocksize") is None: - context["key_blocksize"] = _DEFAULT_BLOCKSIZE - - return key_class(**context) - - -#: A dictionary that maps a scheme to a callable that creates a pool key. -#: This can be used to alter the way pool keys are constructed, if desired. -#: Each PoolManager makes a copy of this dictionary so they can be configured -#: globally here, or individually on the instance. -key_fn_by_scheme = { - "http": functools.partial(_default_key_normalizer, PoolKey), - "https": functools.partial(_default_key_normalizer, PoolKey), -} - -pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool} - - -class PoolManager(RequestMethods): - """ - Allows for arbitrary requests while transparently keeping track of - necessary connection pools for you. - - :param num_pools: - Number of connection pools to cache before discarding the least - recently used pool. - - :param headers: - Headers to include with all requests, unless other headers are given - explicitly. - - :param \\**connection_pool_kw: - Additional parameters are used to create fresh - :class:`urllib3.connectionpool.ConnectionPool` instances. - - Example: - - .. code-block:: python - - import urllib3 - - http = urllib3.PoolManager(num_pools=2) - - resp1 = http.request("GET", "https://google.com/") - resp2 = http.request("GET", "https://google.com/mail") - resp3 = http.request("GET", "https://yahoo.com/") - - print(len(http.pools)) - # 2 - - """ - - proxy: Url | None = None - proxy_config: ProxyConfig | None = None - - def __init__( - self, - num_pools: int = 10, - headers: typing.Mapping[str, str] | None = None, - **connection_pool_kw: typing.Any, - ) -> None: - super().__init__(headers) - self.connection_pool_kw = connection_pool_kw - - self.pools: RecentlyUsedContainer[PoolKey, HTTPConnectionPool] - self.pools = RecentlyUsedContainer(num_pools) - - # Locally set the pool classes and keys so other PoolManagers can - # override them. - self.pool_classes_by_scheme = pool_classes_by_scheme - self.key_fn_by_scheme = key_fn_by_scheme.copy() - - def __enter__(self) -> Self: - return self - - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> typing.Literal[False]: - self.clear() - # Return False to re-raise any potential exceptions - return False - - def _new_pool( - self, - scheme: str, - host: str, - port: int, - request_context: dict[str, typing.Any] | None = None, - ) -> HTTPConnectionPool: - """ - Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and - any additional pool keyword arguments. - - If ``request_context`` is provided, it is provided as keyword arguments - to the pool class used. This method is used to actually create the - connection pools handed out by :meth:`connection_from_url` and - companion methods. It is intended to be overridden for customization. - """ - pool_cls: type[HTTPConnectionPool] = self.pool_classes_by_scheme[scheme] - if request_context is None: - request_context = self.connection_pool_kw.copy() - - # Default blocksize to _DEFAULT_BLOCKSIZE if missing or explicitly - # set to 'None' in the request_context. - if request_context.get("blocksize") is None: - request_context["blocksize"] = _DEFAULT_BLOCKSIZE - - # Although the context has everything necessary to create the pool, - # this function has historically only used the scheme, host, and port - # in the positional args. When an API change is acceptable these can - # be removed. - for key in ("scheme", "host", "port"): - request_context.pop(key, None) - - if scheme == "http": - for kw in SSL_KEYWORDS: - request_context.pop(kw, None) - - return pool_cls(host, port, **request_context) - - def clear(self) -> None: - """ - Empty our store of pools and direct them all to close. - - This will not affect in-flight connections, but they will not be - re-used after completion. - """ - self.pools.clear() - - def connection_from_host( - self, - host: str | None, - port: int | None = None, - scheme: str | None = "http", - pool_kwargs: dict[str, typing.Any] | None = None, - ) -> HTTPConnectionPool: - """ - Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme. - - If ``port`` isn't given, it will be derived from the ``scheme`` using - ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is - provided, it is merged with the instance's ``connection_pool_kw`` - variable and used to create the new connection pool, if one is - needed. - """ - - if not host: - raise LocationValueError("No host specified.") - - request_context = self._merge_pool_kwargs(pool_kwargs) - request_context["scheme"] = scheme or "http" - if not port: - port = port_by_scheme.get(request_context["scheme"].lower(), 80) - request_context["port"] = port - request_context["host"] = host - - return self.connection_from_context(request_context) - - def connection_from_context( - self, request_context: dict[str, typing.Any] - ) -> HTTPConnectionPool: - """ - Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context. - - ``request_context`` must at least contain the ``scheme`` key and its - value must be a key in ``key_fn_by_scheme`` instance variable. - """ - if "strict" in request_context: - warnings.warn( - "The 'strict' parameter is no longer needed on Python 3+. " - "This will raise an error in urllib3 v2.1.0.", - DeprecationWarning, - ) - request_context.pop("strict") - - scheme = request_context["scheme"].lower() - pool_key_constructor = self.key_fn_by_scheme.get(scheme) - if not pool_key_constructor: - raise URLSchemeUnknown(scheme) - pool_key = pool_key_constructor(request_context) - - return self.connection_from_pool_key(pool_key, request_context=request_context) - - def connection_from_pool_key( - self, pool_key: PoolKey, request_context: dict[str, typing.Any] - ) -> HTTPConnectionPool: - """ - Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key. - - ``pool_key`` should be a namedtuple that only contains immutable - objects. At a minimum it must have the ``scheme``, ``host``, and - ``port`` fields. - """ - with self.pools.lock: - # If the scheme, host, or port doesn't match existing open - # connections, open a new ConnectionPool. - pool = self.pools.get(pool_key) - if pool: - return pool - - # Make a fresh ConnectionPool of the desired type - scheme = request_context["scheme"] - host = request_context["host"] - port = request_context["port"] - pool = self._new_pool(scheme, host, port, request_context=request_context) - self.pools[pool_key] = pool - - return pool - - def connection_from_url( - self, url: str, pool_kwargs: dict[str, typing.Any] | None = None - ) -> HTTPConnectionPool: - """ - Similar to :func:`urllib3.connectionpool.connection_from_url`. - - If ``pool_kwargs`` is not provided and a new pool needs to be - constructed, ``self.connection_pool_kw`` is used to initialize - the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` - is provided, it is used instead. Note that if a new pool does not - need to be created for the request, the provided ``pool_kwargs`` are - not used. - """ - u = parse_url(url) - return self.connection_from_host( - u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs - ) - - def _merge_pool_kwargs( - self, override: dict[str, typing.Any] | None - ) -> dict[str, typing.Any]: - """ - Merge a dictionary of override values for self.connection_pool_kw. - - This does not modify self.connection_pool_kw and returns a new dict. - Any keys in the override dictionary with a value of ``None`` are - removed from the merged dictionary. - """ - base_pool_kwargs = self.connection_pool_kw.copy() - if override: - for key, value in override.items(): - if value is None: - try: - del base_pool_kwargs[key] - except KeyError: - pass - else: - base_pool_kwargs[key] = value - return base_pool_kwargs - - def _proxy_requires_url_absolute_form(self, parsed_url: Url) -> bool: - """ - Indicates if the proxy requires the complete destination URL in the - request. Normally this is only needed when not using an HTTP CONNECT - tunnel. - """ - if self.proxy is None: - return False - - return not connection_requires_http_tunnel( - self.proxy, self.proxy_config, parsed_url.scheme - ) - - def urlopen( # type: ignore[override] - self, method: str, url: str, redirect: bool = True, **kw: typing.Any - ) -> BaseHTTPResponse: - """ - Same as :meth:`urllib3.HTTPConnectionPool.urlopen` - with custom cross-host redirect logic and only sends the request-uri - portion of the ``url``. - - The given ``url`` parameter must be absolute, such that an appropriate - :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. - """ - u = parse_url(url) - - if u.scheme is None: - warnings.warn( - "URLs without a scheme (ie 'https://') are deprecated and will raise an error " - "in a future version of urllib3. To avoid this DeprecationWarning ensure all URLs " - "start with 'https://' or 'http://'. Read more in this issue: " - "https://github.com/urllib3/urllib3/issues/2920", - category=DeprecationWarning, - stacklevel=2, - ) - - conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) - - kw["assert_same_host"] = False - kw["redirect"] = False - - if "headers" not in kw: - kw["headers"] = self.headers - - if self._proxy_requires_url_absolute_form(u): - response = conn.urlopen(method, url, **kw) - else: - response = conn.urlopen(method, u.request_uri, **kw) - - redirect_location = redirect and response.get_redirect_location() - if not redirect_location: - return response - - # Support relative URLs for redirecting. - redirect_location = urljoin(url, redirect_location) - - if response.status == 303: - # Change the method according to RFC 9110, Section 15.4.4. - method = "GET" - # And lose the body not to transfer anything sensitive. - kw["body"] = None - kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change() - - retries = kw.get("retries") - if not isinstance(retries, Retry): - retries = Retry.from_int(retries, redirect=redirect) - - # Strip headers marked as unsafe to forward to the redirected location. - # Check remove_headers_on_redirect to avoid a potential network call within - # conn.is_same_host() which may use socket.gethostbyname() in the future. - if retries.remove_headers_on_redirect and not conn.is_same_host( - redirect_location - ): - new_headers = kw["headers"].copy() - for header in kw["headers"]: - if header.lower() in retries.remove_headers_on_redirect: - new_headers.pop(header, None) - kw["headers"] = new_headers - - try: - retries = retries.increment(method, url, response=response, _pool=conn) - except MaxRetryError: - if retries.raise_on_redirect: - response.drain_conn() - raise - return response - - kw["retries"] = retries - kw["redirect"] = redirect - - log.info("Redirecting %s -> %s", url, redirect_location) - - response.drain_conn() - return self.urlopen(method, redirect_location, **kw) - - -class ProxyManager(PoolManager): - """ - Behaves just like :class:`PoolManager`, but sends all requests through - the defined proxy, using the CONNECT method for HTTPS URLs. - - :param proxy_url: - The URL of the proxy to be used. - - :param proxy_headers: - A dictionary containing headers that will be sent to the proxy. In case - of HTTP they are being sent with each request, while in the - HTTPS/CONNECT case they are sent only once. Could be used for proxy - authentication. - - :param proxy_ssl_context: - The proxy SSL context is used to establish the TLS connection to the - proxy when using HTTPS proxies. - - :param use_forwarding_for_https: - (Defaults to False) If set to True will forward requests to the HTTPS - proxy to be made on behalf of the client instead of creating a TLS - tunnel via the CONNECT method. **Enabling this flag means that request - and response headers and content will be visible from the HTTPS proxy** - whereas tunneling keeps request and response headers and content - private. IP address, target hostname, SNI, and port are always visible - to an HTTPS proxy even when this flag is disabled. - - :param proxy_assert_hostname: - The hostname of the certificate to verify against. - - :param proxy_assert_fingerprint: - The fingerprint of the certificate to verify against. - - Example: - - .. code-block:: python - - import urllib3 - - proxy = urllib3.ProxyManager("https://localhost:3128/") - - resp1 = proxy.request("GET", "https://google.com/") - resp2 = proxy.request("GET", "https://httpbin.org/") - - print(len(proxy.pools)) - # 1 - - resp3 = proxy.request("GET", "https://httpbin.org/") - resp4 = proxy.request("GET", "https://twitter.com/") - - print(len(proxy.pools)) - # 3 - - """ - - def __init__( - self, - proxy_url: str, - num_pools: int = 10, - headers: typing.Mapping[str, str] | None = None, - proxy_headers: typing.Mapping[str, str] | None = None, - proxy_ssl_context: ssl.SSLContext | None = None, - use_forwarding_for_https: bool = False, - proxy_assert_hostname: None | str | typing.Literal[False] = None, - proxy_assert_fingerprint: str | None = None, - **connection_pool_kw: typing.Any, - ) -> None: - if isinstance(proxy_url, HTTPConnectionPool): - str_proxy_url = f"{proxy_url.scheme}://{proxy_url.host}:{proxy_url.port}" - else: - str_proxy_url = proxy_url - proxy = parse_url(str_proxy_url) - - if proxy.scheme not in ("http", "https"): - raise ProxySchemeUnknown(proxy.scheme) - - if not proxy.port: - port = port_by_scheme.get(proxy.scheme, 80) - proxy = proxy._replace(port=port) - - self.proxy = proxy - self.proxy_headers = proxy_headers or {} - self.proxy_ssl_context = proxy_ssl_context - self.proxy_config = ProxyConfig( - proxy_ssl_context, - use_forwarding_for_https, - proxy_assert_hostname, - proxy_assert_fingerprint, - ) - - connection_pool_kw["_proxy"] = self.proxy - connection_pool_kw["_proxy_headers"] = self.proxy_headers - connection_pool_kw["_proxy_config"] = self.proxy_config - - super().__init__(num_pools, headers, **connection_pool_kw) - - def connection_from_host( - self, - host: str | None, - port: int | None = None, - scheme: str | None = "http", - pool_kwargs: dict[str, typing.Any] | None = None, - ) -> HTTPConnectionPool: - if scheme == "https": - return super().connection_from_host( - host, port, scheme, pool_kwargs=pool_kwargs - ) - - return super().connection_from_host( - self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs # type: ignore[union-attr] - ) - - def _set_proxy_headers( - self, url: str, headers: typing.Mapping[str, str] | None = None - ) -> typing.Mapping[str, str]: - """ - Sets headers needed by proxies: specifically, the Accept and Host - headers. Only sets headers not provided by the user. - """ - headers_ = {"Accept": "*/*"} - - netloc = parse_url(url).netloc - if netloc: - headers_["Host"] = netloc - - if headers: - headers_.update(headers) - return headers_ - - def urlopen( # type: ignore[override] - self, method: str, url: str, redirect: bool = True, **kw: typing.Any - ) -> BaseHTTPResponse: - "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." - u = parse_url(url) - if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme): - # For connections using HTTP CONNECT, httplib sets the necessary - # headers on the CONNECT to the proxy. If we're not using CONNECT, - # we'll definitely need to set 'Host' at the very least. - headers = kw.get("headers", self.headers) - kw["headers"] = self._set_proxy_headers(url, headers) - - return super().urlopen(method, url, redirect=redirect, **kw) - - -def proxy_from_url(url: str, **kw: typing.Any) -> ProxyManager: - return ProxyManager(proxy_url=url, **kw) diff --git a/.venv/lib/python3.12/site-packages/urllib3/py.typed b/.venv/lib/python3.12/site-packages/urllib3/py.typed deleted file mode 100644 index 5f3ea3d..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# Instruct type checkers to look for inline type annotations in this package. -# See PEP 561. diff --git a/.venv/lib/python3.12/site-packages/urllib3/response.py b/.venv/lib/python3.12/site-packages/urllib3/response.py deleted file mode 100644 index a0273d6..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/response.py +++ /dev/null @@ -1,1265 +0,0 @@ -from __future__ import annotations - -import collections -import io -import json as _json -import logging -import re -import sys -import typing -import warnings -import zlib -from contextlib import contextmanager -from http.client import HTTPMessage as _HttplibHTTPMessage -from http.client import HTTPResponse as _HttplibHTTPResponse -from socket import timeout as SocketTimeout - -if typing.TYPE_CHECKING: - from ._base_connection import BaseHTTPConnection - -try: - try: - import brotlicffi as brotli # type: ignore[import-not-found] - except ImportError: - import brotli # type: ignore[import-not-found] -except ImportError: - brotli = None - -try: - import zstandard as zstd -except (AttributeError, ImportError, ValueError): # Defensive: - HAS_ZSTD = False -else: - # The package 'zstandard' added the 'eof' property starting - # in v0.18.0 which we require to ensure a complete and - # valid zstd stream was fed into the ZstdDecoder. - # See: https://github.com/urllib3/urllib3/pull/2624 - _zstd_version = tuple( - map(int, re.search(r"^([0-9]+)\.([0-9]+)", zstd.__version__).groups()) # type: ignore[union-attr] - ) - if _zstd_version < (0, 18): # Defensive: - HAS_ZSTD = False - else: - HAS_ZSTD = True - -from . import util -from ._base_connection import _TYPE_BODY -from ._collections import HTTPHeaderDict -from .connection import BaseSSLError, HTTPConnection, HTTPException -from .exceptions import ( - BodyNotHttplibCompatible, - DecodeError, - HTTPError, - IncompleteRead, - InvalidChunkLength, - InvalidHeader, - ProtocolError, - ReadTimeoutError, - ResponseNotChunked, - SSLError, -) -from .util.response import is_fp_closed, is_response_to_head -from .util.retry import Retry - -if typing.TYPE_CHECKING: - from .connectionpool import HTTPConnectionPool - -log = logging.getLogger(__name__) - - -class ContentDecoder: - def decompress(self, data: bytes) -> bytes: - raise NotImplementedError() - - def flush(self) -> bytes: - raise NotImplementedError() - - -class DeflateDecoder(ContentDecoder): - def __init__(self) -> None: - self._first_try = True - self._data = b"" - self._obj = zlib.decompressobj() - - def decompress(self, data: bytes) -> bytes: - if not data: - return data - - if not self._first_try: - return self._obj.decompress(data) - - self._data += data - try: - decompressed = self._obj.decompress(data) - if decompressed: - self._first_try = False - self._data = None # type: ignore[assignment] - return decompressed - except zlib.error: - self._first_try = False - self._obj = zlib.decompressobj(-zlib.MAX_WBITS) - try: - return self.decompress(self._data) - finally: - self._data = None # type: ignore[assignment] - - def flush(self) -> bytes: - return self._obj.flush() - - -class GzipDecoderState: - FIRST_MEMBER = 0 - OTHER_MEMBERS = 1 - SWALLOW_DATA = 2 - - -class GzipDecoder(ContentDecoder): - def __init__(self) -> None: - self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) - self._state = GzipDecoderState.FIRST_MEMBER - - def decompress(self, data: bytes) -> bytes: - ret = bytearray() - if self._state == GzipDecoderState.SWALLOW_DATA or not data: - return bytes(ret) - while True: - try: - ret += self._obj.decompress(data) - except zlib.error: - previous_state = self._state - # Ignore data after the first error - self._state = GzipDecoderState.SWALLOW_DATA - if previous_state == GzipDecoderState.OTHER_MEMBERS: - # Allow trailing garbage acceptable in other gzip clients - return bytes(ret) - raise - data = self._obj.unused_data - if not data: - return bytes(ret) - self._state = GzipDecoderState.OTHER_MEMBERS - self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) - - def flush(self) -> bytes: - return self._obj.flush() - - -if brotli is not None: - - class BrotliDecoder(ContentDecoder): - # Supports both 'brotlipy' and 'Brotli' packages - # since they share an import name. The top branches - # are for 'brotlipy' and bottom branches for 'Brotli' - def __init__(self) -> None: - self._obj = brotli.Decompressor() - if hasattr(self._obj, "decompress"): - setattr(self, "decompress", self._obj.decompress) - else: - setattr(self, "decompress", self._obj.process) - - def flush(self) -> bytes: - if hasattr(self._obj, "flush"): - return self._obj.flush() # type: ignore[no-any-return] - return b"" - - -if HAS_ZSTD: - - class ZstdDecoder(ContentDecoder): - def __init__(self) -> None: - self._obj = zstd.ZstdDecompressor().decompressobj() - - def decompress(self, data: bytes) -> bytes: - if not data: - return b"" - data_parts = [self._obj.decompress(data)] - while self._obj.eof and self._obj.unused_data: - unused_data = self._obj.unused_data - self._obj = zstd.ZstdDecompressor().decompressobj() - data_parts.append(self._obj.decompress(unused_data)) - return b"".join(data_parts) - - def flush(self) -> bytes: - ret = self._obj.flush() # note: this is a no-op - if not self._obj.eof: - raise DecodeError("Zstandard data is incomplete") - return ret - - -class MultiDecoder(ContentDecoder): - """ - From RFC7231: - If one or more encodings have been applied to a representation, the - sender that applied the encodings MUST generate a Content-Encoding - header field that lists the content codings in the order in which - they were applied. - """ - - def __init__(self, modes: str) -> None: - self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")] - - def flush(self) -> bytes: - return self._decoders[0].flush() - - def decompress(self, data: bytes) -> bytes: - for d in reversed(self._decoders): - data = d.decompress(data) - return data - - -def _get_decoder(mode: str) -> ContentDecoder: - if "," in mode: - return MultiDecoder(mode) - - # According to RFC 9110 section 8.4.1.3, recipients should - # consider x-gzip equivalent to gzip - if mode in ("gzip", "x-gzip"): - return GzipDecoder() - - if brotli is not None and mode == "br": - return BrotliDecoder() - - if HAS_ZSTD and mode == "zstd": - return ZstdDecoder() - - return DeflateDecoder() - - -class BytesQueueBuffer: - """Memory-efficient bytes buffer - - To return decoded data in read() and still follow the BufferedIOBase API, we need a - buffer to always return the correct amount of bytes. - - This buffer should be filled using calls to put() - - Our maximum memory usage is determined by the sum of the size of: - - * self.buffer, which contains the full data - * the largest chunk that we will copy in get() - - The worst case scenario is a single chunk, in which case we'll make a full copy of - the data inside get(). - """ - - def __init__(self) -> None: - self.buffer: typing.Deque[bytes] = collections.deque() - self._size: int = 0 - - def __len__(self) -> int: - return self._size - - def put(self, data: bytes) -> None: - self.buffer.append(data) - self._size += len(data) - - def get(self, n: int) -> bytes: - if n == 0: - return b"" - elif not self.buffer: - raise RuntimeError("buffer is empty") - elif n < 0: - raise ValueError("n should be > 0") - - fetched = 0 - ret = io.BytesIO() - while fetched < n: - remaining = n - fetched - chunk = self.buffer.popleft() - chunk_length = len(chunk) - if remaining < chunk_length: - left_chunk, right_chunk = chunk[:remaining], chunk[remaining:] - ret.write(left_chunk) - self.buffer.appendleft(right_chunk) - self._size -= remaining - break - else: - ret.write(chunk) - self._size -= chunk_length - fetched += chunk_length - - if not self.buffer: - break - - return ret.getvalue() - - def get_all(self) -> bytes: - buffer = self.buffer - if not buffer: - assert self._size == 0 - return b"" - if len(buffer) == 1: - result = buffer.pop() - else: - ret = io.BytesIO() - ret.writelines(buffer.popleft() for _ in range(len(buffer))) - result = ret.getvalue() - self._size = 0 - return result - - -class BaseHTTPResponse(io.IOBase): - CONTENT_DECODERS = ["gzip", "x-gzip", "deflate"] - if brotli is not None: - CONTENT_DECODERS += ["br"] - if HAS_ZSTD: - CONTENT_DECODERS += ["zstd"] - REDIRECT_STATUSES = [301, 302, 303, 307, 308] - - DECODER_ERROR_CLASSES: tuple[type[Exception], ...] = (IOError, zlib.error) - if brotli is not None: - DECODER_ERROR_CLASSES += (brotli.error,) - - if HAS_ZSTD: - DECODER_ERROR_CLASSES += (zstd.ZstdError,) - - def __init__( - self, - *, - headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None, - status: int, - version: int, - version_string: str, - reason: str | None, - decode_content: bool, - request_url: str | None, - retries: Retry | None = None, - ) -> None: - if isinstance(headers, HTTPHeaderDict): - self.headers = headers - else: - self.headers = HTTPHeaderDict(headers) # type: ignore[arg-type] - self.status = status - self.version = version - self.version_string = version_string - self.reason = reason - self.decode_content = decode_content - self._has_decoded_content = False - self._request_url: str | None = request_url - self.retries = retries - - self.chunked = False - tr_enc = self.headers.get("transfer-encoding", "").lower() - # Don't incur the penalty of creating a list and then discarding it - encodings = (enc.strip() for enc in tr_enc.split(",")) - if "chunked" in encodings: - self.chunked = True - - self._decoder: ContentDecoder | None = None - self.length_remaining: int | None - - def get_redirect_location(self) -> str | None | typing.Literal[False]: - """ - Should we redirect and where to? - - :returns: Truthy redirect location string if we got a redirect status - code and valid location. ``None`` if redirect status and no - location. ``False`` if not a redirect status code. - """ - if self.status in self.REDIRECT_STATUSES: - return self.headers.get("location") - return False - - @property - def data(self) -> bytes: - raise NotImplementedError() - - def json(self) -> typing.Any: - """ - Deserializes the body of the HTTP response as a Python object. - - The body of the HTTP response must be encoded using UTF-8, as per - `RFC 8529 Section 8.1 `_. - - To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to - your custom decoder instead. - - If the body of the HTTP response is not decodable to UTF-8, a - `UnicodeDecodeError` will be raised. If the body of the HTTP response is not a - valid JSON document, a `json.JSONDecodeError` will be raised. - - Read more :ref:`here `. - - :returns: The body of the HTTP response as a Python object. - """ - data = self.data.decode("utf-8") - return _json.loads(data) - - @property - def url(self) -> str | None: - raise NotImplementedError() - - @url.setter - def url(self, url: str | None) -> None: - raise NotImplementedError() - - @property - def connection(self) -> BaseHTTPConnection | None: - raise NotImplementedError() - - @property - def retries(self) -> Retry | None: - return self._retries - - @retries.setter - def retries(self, retries: Retry | None) -> None: - # Override the request_url if retries has a redirect location. - if retries is not None and retries.history: - self.url = retries.history[-1].redirect_location - self._retries = retries - - def stream( - self, amt: int | None = 2**16, decode_content: bool | None = None - ) -> typing.Iterator[bytes]: - raise NotImplementedError() - - def read( - self, - amt: int | None = None, - decode_content: bool | None = None, - cache_content: bool = False, - ) -> bytes: - raise NotImplementedError() - - def read1( - self, - amt: int | None = None, - decode_content: bool | None = None, - ) -> bytes: - raise NotImplementedError() - - def read_chunked( - self, - amt: int | None = None, - decode_content: bool | None = None, - ) -> typing.Iterator[bytes]: - raise NotImplementedError() - - def release_conn(self) -> None: - raise NotImplementedError() - - def drain_conn(self) -> None: - raise NotImplementedError() - - def close(self) -> None: - raise NotImplementedError() - - def _init_decoder(self) -> None: - """ - Set-up the _decoder attribute if necessary. - """ - # Note: content-encoding value should be case-insensitive, per RFC 7230 - # Section 3.2 - content_encoding = self.headers.get("content-encoding", "").lower() - if self._decoder is None: - if content_encoding in self.CONTENT_DECODERS: - self._decoder = _get_decoder(content_encoding) - elif "," in content_encoding: - encodings = [ - e.strip() - for e in content_encoding.split(",") - if e.strip() in self.CONTENT_DECODERS - ] - if encodings: - self._decoder = _get_decoder(content_encoding) - - def _decode( - self, data: bytes, decode_content: bool | None, flush_decoder: bool - ) -> bytes: - """ - Decode the data passed in and potentially flush the decoder. - """ - if not decode_content: - if self._has_decoded_content: - raise RuntimeError( - "Calling read(decode_content=False) is not supported after " - "read(decode_content=True) was called." - ) - return data - - try: - if self._decoder: - data = self._decoder.decompress(data) - self._has_decoded_content = True - except self.DECODER_ERROR_CLASSES as e: - content_encoding = self.headers.get("content-encoding", "").lower() - raise DecodeError( - "Received response with content-encoding: %s, but " - "failed to decode it." % content_encoding, - e, - ) from e - if flush_decoder: - data += self._flush_decoder() - - return data - - def _flush_decoder(self) -> bytes: - """ - Flushes the decoder. Should only be called if the decoder is actually - being used. - """ - if self._decoder: - return self._decoder.decompress(b"") + self._decoder.flush() - return b"" - - # Compatibility methods for `io` module - def readinto(self, b: bytearray) -> int: - temp = self.read(len(b)) - if len(temp) == 0: - return 0 - else: - b[: len(temp)] = temp - return len(temp) - - # Compatibility methods for http.client.HTTPResponse - def getheaders(self) -> HTTPHeaderDict: - warnings.warn( - "HTTPResponse.getheaders() is deprecated and will be removed " - "in urllib3 v2.1.0. Instead access HTTPResponse.headers directly.", - category=DeprecationWarning, - stacklevel=2, - ) - return self.headers - - def getheader(self, name: str, default: str | None = None) -> str | None: - warnings.warn( - "HTTPResponse.getheader() is deprecated and will be removed " - "in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).", - category=DeprecationWarning, - stacklevel=2, - ) - return self.headers.get(name, default) - - # Compatibility method for http.cookiejar - def info(self) -> HTTPHeaderDict: - return self.headers - - def geturl(self) -> str | None: - return self.url - - -class HTTPResponse(BaseHTTPResponse): - """ - HTTP Response container. - - Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is - loaded and decoded on-demand when the ``data`` property is accessed. This - class is also compatible with the Python standard library's :mod:`io` - module, and can hence be treated as a readable object in the context of that - framework. - - Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`: - - :param preload_content: - If True, the response's body will be preloaded during construction. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param original_response: - When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse` - object, it's convenient to include the original for debug purposes. It's - otherwise unused. - - :param retries: - The retries contains the last :class:`~urllib3.util.retry.Retry` that - was used during the request. - - :param enforce_content_length: - Enforce content length checking. Body returned by server must match - value of Content-Length header, if present. Otherwise, raise error. - """ - - def __init__( - self, - body: _TYPE_BODY = "", - headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None, - status: int = 0, - version: int = 0, - version_string: str = "HTTP/?", - reason: str | None = None, - preload_content: bool = True, - decode_content: bool = True, - original_response: _HttplibHTTPResponse | None = None, - pool: HTTPConnectionPool | None = None, - connection: HTTPConnection | None = None, - msg: _HttplibHTTPMessage | None = None, - retries: Retry | None = None, - enforce_content_length: bool = True, - request_method: str | None = None, - request_url: str | None = None, - auto_close: bool = True, - ) -> None: - super().__init__( - headers=headers, - status=status, - version=version, - version_string=version_string, - reason=reason, - decode_content=decode_content, - request_url=request_url, - retries=retries, - ) - - self.enforce_content_length = enforce_content_length - self.auto_close = auto_close - - self._body = None - self._fp: _HttplibHTTPResponse | None = None - self._original_response = original_response - self._fp_bytes_read = 0 - self.msg = msg - - if body and isinstance(body, (str, bytes)): - self._body = body - - self._pool = pool - self._connection = connection - - if hasattr(body, "read"): - self._fp = body # type: ignore[assignment] - - # Are we using the chunked-style of transfer encoding? - self.chunk_left: int | None = None - - # Determine length of response - self.length_remaining = self._init_length(request_method) - - # Used to return the correct amount of bytes for partial read()s - self._decoded_buffer = BytesQueueBuffer() - - # If requested, preload the body. - if preload_content and not self._body: - self._body = self.read(decode_content=decode_content) - - def release_conn(self) -> None: - if not self._pool or not self._connection: - return None - - self._pool._put_conn(self._connection) - self._connection = None - - def drain_conn(self) -> None: - """ - Read and discard any remaining HTTP response data in the response connection. - - Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. - """ - try: - self.read() - except (HTTPError, OSError, BaseSSLError, HTTPException): - pass - - @property - def data(self) -> bytes: - # For backwards-compat with earlier urllib3 0.4 and earlier. - if self._body: - return self._body # type: ignore[return-value] - - if self._fp: - return self.read(cache_content=True) - - return None # type: ignore[return-value] - - @property - def connection(self) -> HTTPConnection | None: - return self._connection - - def isclosed(self) -> bool: - return is_fp_closed(self._fp) - - def tell(self) -> int: - """ - Obtain the number of bytes pulled over the wire so far. May differ from - the amount of content returned by :meth:``urllib3.response.HTTPResponse.read`` - if bytes are encoded on the wire (e.g, compressed). - """ - return self._fp_bytes_read - - def _init_length(self, request_method: str | None) -> int | None: - """ - Set initial length value for Response content if available. - """ - length: int | None - content_length: str | None = self.headers.get("content-length") - - if content_length is not None: - if self.chunked: - # This Response will fail with an IncompleteRead if it can't be - # received as chunked. This method falls back to attempt reading - # the response before raising an exception. - log.warning( - "Received response with both Content-Length and " - "Transfer-Encoding set. This is expressly forbidden " - "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " - "attempting to process response as Transfer-Encoding: " - "chunked." - ) - return None - - try: - # RFC 7230 section 3.3.2 specifies multiple content lengths can - # be sent in a single Content-Length header - # (e.g. Content-Length: 42, 42). This line ensures the values - # are all valid ints and that as long as the `set` length is 1, - # all values are the same. Otherwise, the header is invalid. - lengths = {int(val) for val in content_length.split(",")} - if len(lengths) > 1: - raise InvalidHeader( - "Content-Length contained multiple " - "unmatching values (%s)" % content_length - ) - length = lengths.pop() - except ValueError: - length = None - else: - if length < 0: - length = None - - else: # if content_length is None - length = None - - # Convert status to int for comparison - # In some cases, httplib returns a status of "_UNKNOWN" - try: - status = int(self.status) - except ValueError: - status = 0 - - # Check for responses that shouldn't include a body - if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD": - length = 0 - - return length - - @contextmanager - def _error_catcher(self) -> typing.Generator[None, None, None]: - """ - Catch low-level python exceptions, instead re-raising urllib3 - variants, so that low-level exceptions are not leaked in the - high-level api. - - On exit, release the connection back to the pool. - """ - clean_exit = False - - try: - try: - yield - - except SocketTimeout as e: - # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but - # there is yet no clean way to get at it from this context. - raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] - - except BaseSSLError as e: - # FIXME: Is there a better way to differentiate between SSLErrors? - if "read operation timed out" not in str(e): - # SSL errors related to framing/MAC get wrapped and reraised here - raise SSLError(e) from e - - raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] - - except IncompleteRead as e: - if ( - e.expected is not None - and e.partial is not None - and e.expected == -e.partial - ): - arg = "Response may not contain content." - else: - arg = f"Connection broken: {e!r}" - raise ProtocolError(arg, e) from e - - except (HTTPException, OSError) as e: - raise ProtocolError(f"Connection broken: {e!r}", e) from e - - # If no exception is thrown, we should avoid cleaning up - # unnecessarily. - clean_exit = True - finally: - # If we didn't terminate cleanly, we need to throw away our - # connection. - if not clean_exit: - # The response may not be closed but we're not going to use it - # anymore so close it now to ensure that the connection is - # released back to the pool. - if self._original_response: - self._original_response.close() - - # Closing the response may not actually be sufficient to close - # everything, so if we have a hold of the connection close that - # too. - if self._connection: - self._connection.close() - - # If we hold the original response but it's closed now, we should - # return the connection back to the pool. - if self._original_response and self._original_response.isclosed(): - self.release_conn() - - def _fp_read( - self, - amt: int | None = None, - *, - read1: bool = False, - ) -> bytes: - """ - Read a response with the thought that reading the number of bytes - larger than can fit in a 32-bit int at a time via SSL in some - known cases leads to an overflow error that has to be prevented - if `amt` or `self.length_remaining` indicate that a problem may - happen. - - The known cases: - * 3.8 <= CPython < 3.9.7 because of a bug - https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900. - * urllib3 injected with pyOpenSSL-backed SSL-support. - * CPython < 3.10 only when `amt` does not fit 32-bit int. - """ - assert self._fp - c_int_max = 2**31 - 1 - if ( - (amt and amt > c_int_max) - or ( - amt is None - and self.length_remaining - and self.length_remaining > c_int_max - ) - ) and (util.IS_PYOPENSSL or sys.version_info < (3, 10)): - if read1: - return self._fp.read1(c_int_max) - buffer = io.BytesIO() - # Besides `max_chunk_amt` being a maximum chunk size, it - # affects memory overhead of reading a response by this - # method in CPython. - # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum - # chunk size that does not lead to an overflow error, but - # 256 MiB is a compromise. - max_chunk_amt = 2**28 - while amt is None or amt != 0: - if amt is not None: - chunk_amt = min(amt, max_chunk_amt) - amt -= chunk_amt - else: - chunk_amt = max_chunk_amt - data = self._fp.read(chunk_amt) - if not data: - break - buffer.write(data) - del data # to reduce peak memory usage by `max_chunk_amt`. - return buffer.getvalue() - elif read1: - return self._fp.read1(amt) if amt is not None else self._fp.read1() - else: - # StringIO doesn't like amt=None - return self._fp.read(amt) if amt is not None else self._fp.read() - - def _raw_read( - self, - amt: int | None = None, - *, - read1: bool = False, - ) -> bytes: - """ - Reads `amt` of bytes from the socket. - """ - if self._fp is None: - return None # type: ignore[return-value] - - fp_closed = getattr(self._fp, "closed", False) - - with self._error_catcher(): - data = self._fp_read(amt, read1=read1) if not fp_closed else b"" - if amt is not None and amt != 0 and not data: - # Platform-specific: Buggy versions of Python. - # Close the connection when no data is returned - # - # This is redundant to what httplib/http.client _should_ - # already do. However, versions of python released before - # December 15, 2012 (http://bugs.python.org/issue16298) do - # not properly close the connection in all cases. There is - # no harm in redundantly calling close. - self._fp.close() - if ( - self.enforce_content_length - and self.length_remaining is not None - and self.length_remaining != 0 - ): - # This is an edge case that httplib failed to cover due - # to concerns of backward compatibility. We're - # addressing it here to make sure IncompleteRead is - # raised during streaming, so all calls with incorrect - # Content-Length are caught. - raise IncompleteRead(self._fp_bytes_read, self.length_remaining) - elif read1 and ( - (amt != 0 and not data) or self.length_remaining == len(data) - ): - # All data has been read, but `self._fp.read1` in - # CPython 3.12 and older doesn't always close - # `http.client.HTTPResponse`, so we close it here. - # See https://github.com/python/cpython/issues/113199 - self._fp.close() - - if data: - self._fp_bytes_read += len(data) - if self.length_remaining is not None: - self.length_remaining -= len(data) - return data - - def read( - self, - amt: int | None = None, - decode_content: bool | None = None, - cache_content: bool = False, - ) -> bytes: - """ - Similar to :meth:`http.client.HTTPResponse.read`, but with two additional - parameters: ``decode_content`` and ``cache_content``. - - :param amt: - How much of the content to read. If specified, caching is skipped - because it doesn't make sense to cache partial content as the full - response. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - - :param cache_content: - If True, will save the returned data such that the same result is - returned despite of the state of the underlying file object. This - is useful if you want the ``.data`` property to continue working - after having ``.read()`` the file object. (Overridden if ``amt`` is - set.) - """ - self._init_decoder() - if decode_content is None: - decode_content = self.decode_content - - if amt and amt < 0: - # Negative numbers and `None` should be treated the same. - amt = None - elif amt is not None: - cache_content = False - - if len(self._decoded_buffer) >= amt: - return self._decoded_buffer.get(amt) - - data = self._raw_read(amt) - - flush_decoder = amt is None or (amt != 0 and not data) - - if not data and len(self._decoded_buffer) == 0: - return data - - if amt is None: - data = self._decode(data, decode_content, flush_decoder) - if cache_content: - self._body = data - else: - # do not waste memory on buffer when not decoding - if not decode_content: - if self._has_decoded_content: - raise RuntimeError( - "Calling read(decode_content=False) is not supported after " - "read(decode_content=True) was called." - ) - return data - - decoded_data = self._decode(data, decode_content, flush_decoder) - self._decoded_buffer.put(decoded_data) - - while len(self._decoded_buffer) < amt and data: - # TODO make sure to initially read enough data to get past the headers - # For example, the GZ file header takes 10 bytes, we don't want to read - # it one byte at a time - data = self._raw_read(amt) - decoded_data = self._decode(data, decode_content, flush_decoder) - self._decoded_buffer.put(decoded_data) - data = self._decoded_buffer.get(amt) - - return data - - def read1( - self, - amt: int | None = None, - decode_content: bool | None = None, - ) -> bytes: - """ - Similar to ``http.client.HTTPResponse.read1`` and documented - in :meth:`io.BufferedReader.read1`, but with an additional parameter: - ``decode_content``. - - :param amt: - How much of the content to read. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - if decode_content is None: - decode_content = self.decode_content - if amt and amt < 0: - # Negative numbers and `None` should be treated the same. - amt = None - # try and respond without going to the network - if self._has_decoded_content: - if not decode_content: - raise RuntimeError( - "Calling read1(decode_content=False) is not supported after " - "read1(decode_content=True) was called." - ) - if len(self._decoded_buffer) > 0: - if amt is None: - return self._decoded_buffer.get_all() - return self._decoded_buffer.get(amt) - if amt == 0: - return b"" - - # FIXME, this method's type doesn't say returning None is possible - data = self._raw_read(amt, read1=True) - if not decode_content or data is None: - return data - - self._init_decoder() - while True: - flush_decoder = not data - decoded_data = self._decode(data, decode_content, flush_decoder) - self._decoded_buffer.put(decoded_data) - if decoded_data or flush_decoder: - break - data = self._raw_read(8192, read1=True) - - if amt is None: - return self._decoded_buffer.get_all() - return self._decoded_buffer.get(amt) - - def stream( - self, amt: int | None = 2**16, decode_content: bool | None = None - ) -> typing.Generator[bytes, None, None]: - """ - A generator wrapper for the read() method. A call will block until - ``amt`` bytes have been read from the connection or until the - connection is closed. - - :param amt: - How much of the content to read. The generator will return up to - much data per iteration, but may return less. This is particularly - likely when using compressed data. However, the empty string will - never be returned. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - if self.chunked and self.supports_chunked_reads(): - yield from self.read_chunked(amt, decode_content=decode_content) - else: - while not is_fp_closed(self._fp) or len(self._decoded_buffer) > 0: - data = self.read(amt=amt, decode_content=decode_content) - - if data: - yield data - - # Overrides from io.IOBase - def readable(self) -> bool: - return True - - def close(self) -> None: - if not self.closed and self._fp: - self._fp.close() - - if self._connection: - self._connection.close() - - if not self.auto_close: - io.IOBase.close(self) - - @property - def closed(self) -> bool: - if not self.auto_close: - return io.IOBase.closed.__get__(self) # type: ignore[no-any-return] - elif self._fp is None: - return True - elif hasattr(self._fp, "isclosed"): - return self._fp.isclosed() - elif hasattr(self._fp, "closed"): - return self._fp.closed - else: - return True - - def fileno(self) -> int: - if self._fp is None: - raise OSError("HTTPResponse has no file to get a fileno from") - elif hasattr(self._fp, "fileno"): - return self._fp.fileno() - else: - raise OSError( - "The file-like object this HTTPResponse is wrapped " - "around has no file descriptor" - ) - - def flush(self) -> None: - if ( - self._fp is not None - and hasattr(self._fp, "flush") - and not getattr(self._fp, "closed", False) - ): - return self._fp.flush() - - def supports_chunked_reads(self) -> bool: - """ - Checks if the underlying file-like object looks like a - :class:`http.client.HTTPResponse` object. We do this by testing for - the fp attribute. If it is present we assume it returns raw chunks as - processed by read_chunked(). - """ - return hasattr(self._fp, "fp") - - def _update_chunk_length(self) -> None: - # First, we'll figure out length of a chunk and then - # we'll try to read it from socket. - if self.chunk_left is not None: - return None - line = self._fp.fp.readline() # type: ignore[union-attr] - line = line.split(b";", 1)[0] - try: - self.chunk_left = int(line, 16) - except ValueError: - self.close() - if line: - # Invalid chunked protocol response, abort. - raise InvalidChunkLength(self, line) from None - else: - # Truncated at start of next chunk - raise ProtocolError("Response ended prematurely") from None - - def _handle_chunk(self, amt: int | None) -> bytes: - returned_chunk = None - if amt is None: - chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr] - returned_chunk = chunk - self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. - self.chunk_left = None - elif self.chunk_left is not None and amt < self.chunk_left: - value = self._fp._safe_read(amt) # type: ignore[union-attr] - self.chunk_left = self.chunk_left - amt - returned_chunk = value - elif amt == self.chunk_left: - value = self._fp._safe_read(amt) # type: ignore[union-attr] - self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. - self.chunk_left = None - returned_chunk = value - else: # amt > self.chunk_left - returned_chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr] - self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. - self.chunk_left = None - return returned_chunk # type: ignore[no-any-return] - - def read_chunked( - self, amt: int | None = None, decode_content: bool | None = None - ) -> typing.Generator[bytes, None, None]: - """ - Similar to :meth:`HTTPResponse.read`, but with an additional - parameter: ``decode_content``. - - :param amt: - How much of the content to read. If specified, caching is skipped - because it doesn't make sense to cache partial content as the full - response. - - :param decode_content: - If True, will attempt to decode the body based on the - 'content-encoding' header. - """ - self._init_decoder() - # FIXME: Rewrite this method and make it a class with a better structured logic. - if not self.chunked: - raise ResponseNotChunked( - "Response is not chunked. " - "Header 'transfer-encoding: chunked' is missing." - ) - if not self.supports_chunked_reads(): - raise BodyNotHttplibCompatible( - "Body should be http.client.HTTPResponse like. " - "It should have have an fp attribute which returns raw chunks." - ) - - with self._error_catcher(): - # Don't bother reading the body of a HEAD request. - if self._original_response and is_response_to_head(self._original_response): - self._original_response.close() - return None - - # If a response is already read and closed - # then return immediately. - if self._fp.fp is None: # type: ignore[union-attr] - return None - - if amt and amt < 0: - # Negative numbers and `None` should be treated the same, - # but httplib handles only `None` correctly. - amt = None - - while True: - self._update_chunk_length() - if self.chunk_left == 0: - break - chunk = self._handle_chunk(amt) - decoded = self._decode( - chunk, decode_content=decode_content, flush_decoder=False - ) - if decoded: - yield decoded - - if decode_content: - # On CPython and PyPy, we should never need to flush the - # decoder. However, on Jython we *might* need to, so - # lets defensively do it anyway. - decoded = self._flush_decoder() - if decoded: # Platform-specific: Jython. - yield decoded - - # Chunk content ends with \r\n: discard it. - while self._fp is not None: - line = self._fp.fp.readline() - if not line: - # Some sites may not end with '\r\n'. - break - if line == b"\r\n": - break - - # We read everything; close the "file". - if self._original_response: - self._original_response.close() - - @property - def url(self) -> str | None: - """ - Returns the URL that was the source of this response. - If the request that generated this response redirected, this method - will return the final redirect location. - """ - return self._request_url - - @url.setter - def url(self, url: str) -> None: - self._request_url = url - - def __iter__(self) -> typing.Iterator[bytes]: - buffer: list[bytes] = [] - for chunk in self.stream(decode_content=True): - if b"\n" in chunk: - chunks = chunk.split(b"\n") - yield b"".join(buffer) + chunks[0] + b"\n" - for x in chunks[1:-1]: - yield x + b"\n" - if chunks[-1]: - buffer = [chunks[-1]] - else: - buffer = [] - else: - buffer.append(chunk) - if buffer: - yield b"".join(buffer) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__init__.py b/.venv/lib/python3.12/site-packages/urllib3/util/__init__.py deleted file mode 100644 index 5341260..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# For backwards compatibility, provide imports that used to be here. -from __future__ import annotations - -from .connection import is_connection_dropped -from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers -from .response import is_fp_closed -from .retry import Retry -from .ssl_ import ( - ALPN_PROTOCOLS, - IS_PYOPENSSL, - SSLContext, - assert_fingerprint, - create_urllib3_context, - resolve_cert_reqs, - resolve_ssl_version, - ssl_wrap_socket, -) -from .timeout import Timeout -from .url import Url, parse_url -from .wait import wait_for_read, wait_for_write - -__all__ = ( - "IS_PYOPENSSL", - "SSLContext", - "ALPN_PROTOCOLS", - "Retry", - "Timeout", - "Url", - "assert_fingerprint", - "create_urllib3_context", - "is_connection_dropped", - "is_fp_closed", - "parse_url", - "make_headers", - "resolve_cert_reqs", - "resolve_ssl_version", - "ssl_wrap_socket", - "wait_for_read", - "wait_for_write", - "SKIP_HEADER", - "SKIPPABLE_HEADERS", -) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 84256e01fe877dd503fe7866430671c48e54132e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 999 zcmY+Czi-n(6vxkT66bfCA5B}@f>gxdp|zk3140TV5*49|l7iG*PR718R_|j8Iq%Um{061DbOM;CbmUN zv<%C%0xPr%t8@u2(HgALI;_(MY|tibCg*ZwnYLgnvGZhwuEJHi2GCiMl5kjj`)v##Ql>Dm03bMH4zr&pl;AFXrZ}8BYLiYp6XPh{-Ff=V;cBwmvFn|<$Cx{Jn|3B5APz!7sP=(& z;@NHP1&#a(!7tepb&Y<%{JFxFX&A-@*t!7Q=b-%yw9mlS8CbnZ<&D%0P`K9k(*(xm TIe2sqoU3%RnEGO|$13?Rh}02o diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/connection.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/connection.cpython-312.pyc deleted file mode 100644 index 50d6b5389a7ee2502478288a83d405f672bde05d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4742 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/proxy.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/proxy.cpython-312.pyc deleted file mode 100644 index efb1e4136b0f0d4f55a11f64000d836ff0c1f352..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1211 zcmZ8g&2G~`5Z<-(ZBEVE7Ad!n@>`mihd(GM|1m(~J z?*OlWcoZ&>OM|6Kl@N#CB9#*-X4eUg*eJWRGvEAuv;JDIR}n0I^Gk17K_1Jx8SQmpXjLk!wE7j-RKUAiG4bI*T+AQpOTh6pp;fsVF z?)YdEgpX*5cJTlY6<2w^f)E!n+7`I~ma9tp!VvTN5O0ep|1IG=DCkA|9+Xohur_Iu z@-QYHo>JoVNYbaoVPQxeQ8(fAP3mzP6FVg9oz4bnKQ+y@b_Z~rrCWW8k3<`-s*HC# zvbi0Q=bMj-R9h+cZ%d2FCe$oXGIbK#sm~l6Q(q_qs5y3*2vHtrQ!be$JJJJ4^gn-w;PZjlwQMM*zNqWHQzt`nL#x=e6C zD+ohYl;)1EWTiY%)=Xn+^%#F^bJq*EglI{Su4a{gtYo!Z%-hKtb9tv##^Ox?KYyMX zeHKuIM>OGV$8-9I!(3`S@nXkl?a*+?@V!??G)Vd^ywzI0X~bSaZ$!598W@QU7y;OC z8ENAAhD-`1WfzXeY|X|j%e0ad=I`@Uko^Sw-*P5RmjI`w+wdT0i+^emQYO4nlTU$JW2uN2=)6_Ly2l;&!z5_BOCJQM) zDHIJk+_s$U%&;JlVuU#|D{=W;v%)JFxL(Psmeot)0aMH3f~>5R3?iUBQ=w;G6qfaPGT3F8pAl~ec@efh+MR&8sZa0nb8r=(ZEV6eQ8C8*=+aMA z|AkH;sFgkS%t$@6UswSG&R-l~y*9q0Ps#)@ju)0DDx3$6%abCUz}C{Tv;eYH{apc_ G%=Z`D9ZoU; diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/request.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/request.cpython-312.pyc deleted file mode 100644 index b861ec1382246160116a4c0d37721680bf745a4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8030 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 JTTgM2|1a>}J1+nL diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/response.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/response.cpython-312.pyc deleted file mode 100644 index fdc6f5cd0348a72821c3cd44b63e08536336d087..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2867 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 diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/url.cpython-312.pyc b/.venv/lib/python3.12/site-packages/urllib3/util/__pycache__/url.cpython-312.pyc deleted file mode 100644 index e1ebd1c298e7769ec9577be756ff7ec8481744ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16223 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 bool: # Platform-specific - """ - Returns True if the connection is dropped and should be closed. - :param conn: :class:`urllib3.connection.HTTPConnection` object. - """ - return not conn.is_connected - - -# This function is copied from socket.py in the Python 2.7 standard -# library test suite. Added to its signature is only `socket_options`. -# One additional modification is that we avoid binding to IPv6 servers -# discovered in DNS if the system doesn't have IPv6 functionality. -def create_connection( - address: tuple[str, int], - timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - source_address: tuple[str, int] | None = None, - socket_options: _TYPE_SOCKET_OPTIONS | None = None, -) -> socket.socket: - """Connect to *address* and return the socket object. - - Convenience function. Connect to *address* (a 2-tuple ``(host, - port)``) and return the socket object. Passing the optional - *timeout* parameter will set the timeout on the socket instance - before attempting to connect. If no *timeout* is supplied, the - global default timeout setting returned by :func:`socket.getdefaulttimeout` - is used. If *source_address* is set it must be a tuple of (host, port) - for the socket to bind as a source address before making the connection. - An host of '' or port 0 tells the OS to use the default. - """ - - host, port = address - if host.startswith("["): - host = host.strip("[]") - err = None - - # Using the value from allowed_gai_family() in the context of getaddrinfo lets - # us select whether to work with IPv4 DNS records, IPv6 records, or both. - # The original create_connection function always returns all records. - family = allowed_gai_family() - - try: - host.encode("idna") - except UnicodeError: - raise LocationParseError(f"'{host}', label empty or too long") from None - - for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket.socket(af, socktype, proto) - - # If provided, set socket level options before connecting. - _set_socket_options(sock, socket_options) - - if timeout is not _DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - # Break explicitly a reference cycle - err = None - return sock - - except OSError as _: - err = _ - if sock is not None: - sock.close() - - if err is not None: - try: - raise err - finally: - # Break explicitly a reference cycle - err = None - else: - raise OSError("getaddrinfo returns an empty list") - - -def _set_socket_options( - sock: socket.socket, options: _TYPE_SOCKET_OPTIONS | None -) -> None: - if options is None: - return - - for opt in options: - sock.setsockopt(*opt) - - -def allowed_gai_family() -> socket.AddressFamily: - """This function is designed to work in the context of - getaddrinfo, where family=socket.AF_UNSPEC is the default and - will perform a DNS search for both IPv6 and IPv4 records.""" - - family = socket.AF_INET - if HAS_IPV6: - family = socket.AF_UNSPEC - return family - - -def _has_ipv6(host: str) -> bool: - """Returns True if the system can bind an IPv6 address.""" - sock = None - has_ipv6 = False - - if socket.has_ipv6: - # has_ipv6 returns true if cPython was compiled with IPv6 support. - # It does not tell us if the system has IPv6 support enabled. To - # determine that we must bind to an IPv6 address. - # https://github.com/urllib3/urllib3/pull/611 - # https://bugs.python.org/issue658327 - try: - sock = socket.socket(socket.AF_INET6) - sock.bind((host, 0)) - has_ipv6 = True - except Exception: - pass - - if sock: - sock.close() - return has_ipv6 - - -HAS_IPV6 = _has_ipv6("::1") diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/proxy.py b/.venv/lib/python3.12/site-packages/urllib3/util/proxy.py deleted file mode 100644 index 908fc66..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/proxy.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import annotations - -import typing - -from .url import Url - -if typing.TYPE_CHECKING: - from ..connection import ProxyConfig - - -def connection_requires_http_tunnel( - proxy_url: Url | None = None, - proxy_config: ProxyConfig | None = None, - destination_scheme: str | None = None, -) -> bool: - """ - Returns True if the connection requires an HTTP CONNECT through the proxy. - - :param URL proxy_url: - URL of the proxy. - :param ProxyConfig proxy_config: - Proxy configuration from poolmanager.py - :param str destination_scheme: - The scheme of the destination. (i.e https, http, etc) - """ - # If we're not using a proxy, no way to use a tunnel. - if proxy_url is None: - return False - - # HTTP destinations never require tunneling, we always forward. - if destination_scheme == "http": - return False - - # Support for forwarding with HTTPS proxies and HTTPS destinations. - if ( - proxy_url.scheme == "https" - and proxy_config - and proxy_config.use_forwarding_for_https - ): - return False - - # Otherwise always use a tunnel. - return True diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/request.py b/.venv/lib/python3.12/site-packages/urllib3/util/request.py deleted file mode 100644 index 859597e..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/request.py +++ /dev/null @@ -1,256 +0,0 @@ -from __future__ import annotations - -import io -import typing -from base64 import b64encode -from enum import Enum - -from ..exceptions import UnrewindableBodyError -from .util import to_bytes - -if typing.TYPE_CHECKING: - from typing import Final - -# Pass as a value within ``headers`` to skip -# emitting some HTTP headers that are added automatically. -# The only headers that are supported are ``Accept-Encoding``, -# ``Host``, and ``User-Agent``. -SKIP_HEADER = "@@@SKIP_HEADER@@@" -SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) - -ACCEPT_ENCODING = "gzip,deflate" -try: - try: - import brotlicffi as _unused_module_brotli # type: ignore[import-not-found] # noqa: F401 - except ImportError: - import brotli as _unused_module_brotli # type: ignore[import-not-found] # noqa: F401 -except ImportError: - pass -else: - ACCEPT_ENCODING += ",br" -try: - import zstandard as _unused_module_zstd # noqa: F401 -except ImportError: - pass -else: - ACCEPT_ENCODING += ",zstd" - - -class _TYPE_FAILEDTELL(Enum): - token = 0 - - -_FAILEDTELL: Final[_TYPE_FAILEDTELL] = _TYPE_FAILEDTELL.token - -_TYPE_BODY_POSITION = typing.Union[int, _TYPE_FAILEDTELL] - -# When sending a request with these methods we aren't expecting -# a body so don't need to set an explicit 'Content-Length: 0' -# The reason we do this in the negative instead of tracking methods -# which 'should' have a body is because unknown methods should be -# treated as if they were 'POST' which *does* expect a body. -_METHODS_NOT_EXPECTING_BODY = {"GET", "HEAD", "DELETE", "TRACE", "OPTIONS", "CONNECT"} - - -def make_headers( - keep_alive: bool | None = None, - accept_encoding: bool | list[str] | str | None = None, - user_agent: str | None = None, - basic_auth: str | None = None, - proxy_basic_auth: str | None = None, - disable_cache: bool | None = None, -) -> dict[str, str]: - """ - Shortcuts for generating request headers. - - :param keep_alive: - If ``True``, adds 'connection: keep-alive' header. - - :param accept_encoding: - Can be a boolean, list, or string. - ``True`` translates to 'gzip,deflate'. If either the ``brotli`` or - ``brotlicffi`` package is installed 'gzip,deflate,br' is used instead. - List will get joined by comma. - String will be used as provided. - - :param user_agent: - String representing the user-agent you want, such as - "python-urllib3/0.6" - - :param basic_auth: - Colon-separated username:password string for 'authorization: basic ...' - auth header. - - :param proxy_basic_auth: - Colon-separated username:password string for 'proxy-authorization: basic ...' - auth header. - - :param disable_cache: - If ``True``, adds 'cache-control: no-cache' header. - - Example: - - .. code-block:: python - - import urllib3 - - print(urllib3.util.make_headers(keep_alive=True, user_agent="Batman/1.0")) - # {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} - print(urllib3.util.make_headers(accept_encoding=True)) - # {'accept-encoding': 'gzip,deflate'} - """ - headers: dict[str, str] = {} - if accept_encoding: - if isinstance(accept_encoding, str): - pass - elif isinstance(accept_encoding, list): - accept_encoding = ",".join(accept_encoding) - else: - accept_encoding = ACCEPT_ENCODING - headers["accept-encoding"] = accept_encoding - - if user_agent: - headers["user-agent"] = user_agent - - if keep_alive: - headers["connection"] = "keep-alive" - - if basic_auth: - headers[ - "authorization" - ] = f"Basic {b64encode(basic_auth.encode('latin-1')).decode()}" - - if proxy_basic_auth: - headers[ - "proxy-authorization" - ] = f"Basic {b64encode(proxy_basic_auth.encode('latin-1')).decode()}" - - if disable_cache: - headers["cache-control"] = "no-cache" - - return headers - - -def set_file_position( - body: typing.Any, pos: _TYPE_BODY_POSITION | None -) -> _TYPE_BODY_POSITION | None: - """ - If a position is provided, move file to that point. - Otherwise, we'll attempt to record a position for future use. - """ - if pos is not None: - rewind_body(body, pos) - elif getattr(body, "tell", None) is not None: - try: - pos = body.tell() - except OSError: - # This differentiates from None, allowing us to catch - # a failed `tell()` later when trying to rewind the body. - pos = _FAILEDTELL - - return pos - - -def rewind_body(body: typing.IO[typing.AnyStr], body_pos: _TYPE_BODY_POSITION) -> None: - """ - Attempt to rewind body to a certain position. - Primarily used for request redirects and retries. - - :param body: - File-like object that supports seek. - - :param int pos: - Position to seek to in file. - """ - body_seek = getattr(body, "seek", None) - if body_seek is not None and isinstance(body_pos, int): - try: - body_seek(body_pos) - except OSError as e: - raise UnrewindableBodyError( - "An error occurred when rewinding request body for redirect/retry." - ) from e - elif body_pos is _FAILEDTELL: - raise UnrewindableBodyError( - "Unable to record file position for rewinding " - "request body during a redirect/retry." - ) - else: - raise ValueError( - f"body_pos must be of type integer, instead it was {type(body_pos)}." - ) - - -class ChunksAndContentLength(typing.NamedTuple): - chunks: typing.Iterable[bytes] | None - content_length: int | None - - -def body_to_chunks( - body: typing.Any | None, method: str, blocksize: int -) -> ChunksAndContentLength: - """Takes the HTTP request method, body, and blocksize and - transforms them into an iterable of chunks to pass to - socket.sendall() and an optional 'Content-Length' header. - - A 'Content-Length' of 'None' indicates the length of the body - can't be determined so should use 'Transfer-Encoding: chunked' - for framing instead. - """ - - chunks: typing.Iterable[bytes] | None - content_length: int | None - - # No body, we need to make a recommendation on 'Content-Length' - # based on whether that request method is expected to have - # a body or not. - if body is None: - chunks = None - if method.upper() not in _METHODS_NOT_EXPECTING_BODY: - content_length = 0 - else: - content_length = None - - # Bytes or strings become bytes - elif isinstance(body, (str, bytes)): - chunks = (to_bytes(body),) - content_length = len(chunks[0]) - - # File-like object, TODO: use seek() and tell() for length? - elif hasattr(body, "read"): - - def chunk_readable() -> typing.Iterable[bytes]: - nonlocal body, blocksize - encode = isinstance(body, io.TextIOBase) - while True: - datablock = body.read(blocksize) - if not datablock: - break - if encode: - datablock = datablock.encode("iso-8859-1") - yield datablock - - chunks = chunk_readable() - content_length = None - - # Otherwise we need to start checking via duck-typing. - else: - try: - # Check if the body implements the buffer API. - mv = memoryview(body) - except TypeError: - try: - # Check if the body is an iterable - chunks = iter(body) - content_length = None - except TypeError: - raise TypeError( - f"'body' must be a bytes-like object, file-like " - f"object, or iterable. Instead was {body!r}" - ) from None - else: - # Since it implements the buffer API can be passed directly to socket.sendall() - chunks = (body,) - content_length = mv.nbytes - - return ChunksAndContentLength(chunks=chunks, content_length=content_length) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/response.py b/.venv/lib/python3.12/site-packages/urllib3/util/response.py deleted file mode 100644 index 0f45786..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/response.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -import http.client as httplib -from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect - -from ..exceptions import HeaderParsingError - - -def is_fp_closed(obj: object) -> bool: - """ - Checks whether a given file-like object is closed. - - :param obj: - The file-like object to check. - """ - - try: - # Check `isclosed()` first, in case Python3 doesn't set `closed`. - # GH Issue #928 - return obj.isclosed() # type: ignore[no-any-return, attr-defined] - except AttributeError: - pass - - try: - # Check via the official file-like-object way. - return obj.closed # type: ignore[no-any-return, attr-defined] - except AttributeError: - pass - - try: - # Check if the object is a container for another file-like object that - # gets released on exhaustion (e.g. HTTPResponse). - return obj.fp is None # type: ignore[attr-defined] - except AttributeError: - pass - - raise ValueError("Unable to determine whether fp is closed.") - - -def assert_header_parsing(headers: httplib.HTTPMessage) -> None: - """ - Asserts whether all headers have been successfully parsed. - Extracts encountered errors from the result of parsing headers. - - Only works on Python 3. - - :param http.client.HTTPMessage headers: Headers to verify. - - :raises urllib3.exceptions.HeaderParsingError: - If parsing errors are found. - """ - - # This will fail silently if we pass in the wrong kind of parameter. - # To make debugging easier add an explicit check. - if not isinstance(headers, httplib.HTTPMessage): - raise TypeError(f"expected httplib.Message, got {type(headers)}.") - - unparsed_data = None - - # get_payload is actually email.message.Message.get_payload; - # we're only interested in the result if it's not a multipart message - if not headers.is_multipart(): - payload = headers.get_payload() - - if isinstance(payload, (bytes, str)): - unparsed_data = payload - - # httplib is assuming a response body is available - # when parsing headers even when httplib only sends - # header data to parse_headers() This results in - # defects on multipart responses in particular. - # See: https://github.com/urllib3/urllib3/issues/800 - - # So we ignore the following defects: - # - StartBoundaryNotFoundDefect: - # The claimed start boundary was never found. - # - MultipartInvariantViolationDefect: - # A message claimed to be a multipart but no subparts were found. - defects = [ - defect - for defect in headers.defects - if not isinstance( - defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) - ) - ] - - if defects or unparsed_data: - raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) - - -def is_response_to_head(response: httplib.HTTPResponse) -> bool: - """ - Checks whether the request of a response has been a HEAD-request. - - :param http.client.HTTPResponse response: - Response to check if the originating request - used 'HEAD' as a method. - """ - # FIXME: Can we do this somehow without accessing private httplib _method? - method_str = response._method # type: str # type: ignore[attr-defined] - return method_str.upper() == "HEAD" diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/retry.py b/.venv/lib/python3.12/site-packages/urllib3/util/retry.py deleted file mode 100644 index 0456cce..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/retry.py +++ /dev/null @@ -1,533 +0,0 @@ -from __future__ import annotations - -import email -import logging -import random -import re -import time -import typing -from itertools import takewhile -from types import TracebackType - -from ..exceptions import ( - ConnectTimeoutError, - InvalidHeader, - MaxRetryError, - ProtocolError, - ProxyError, - ReadTimeoutError, - ResponseError, -) -from .util import reraise - -if typing.TYPE_CHECKING: - from typing_extensions import Self - - from ..connectionpool import ConnectionPool - from ..response import BaseHTTPResponse - -log = logging.getLogger(__name__) - - -# Data structure for representing the metadata of requests that result in a retry. -class RequestHistory(typing.NamedTuple): - method: str | None - url: str | None - error: Exception | None - status: int | None - redirect_location: str | None - - -class Retry: - """Retry configuration. - - Each retry attempt will create a new Retry object with updated values, so - they can be safely reused. - - Retries can be defined as a default for a pool: - - .. code-block:: python - - retries = Retry(connect=5, read=2, redirect=5) - http = PoolManager(retries=retries) - response = http.request("GET", "https://example.com/") - - Or per-request (which overrides the default for the pool): - - .. code-block:: python - - response = http.request("GET", "https://example.com/", retries=Retry(10)) - - Retries can be disabled by passing ``False``: - - .. code-block:: python - - response = http.request("GET", "https://example.com/", retries=False) - - Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless - retries are disabled, in which case the causing exception will be raised. - - :param int total: - Total number of retries to allow. Takes precedence over other counts. - - Set to ``None`` to remove this constraint and fall back on other - counts. - - Set to ``0`` to fail on the first retry. - - Set to ``False`` to disable and imply ``raise_on_redirect=False``. - - :param int connect: - How many connection-related errors to retry on. - - These are errors raised before the request is sent to the remote server, - which we assume has not triggered the server to process the request. - - Set to ``0`` to fail on the first retry of this type. - - :param int read: - How many times to retry on read errors. - - These errors are raised after the request was sent to the server, so the - request may have side-effects. - - Set to ``0`` to fail on the first retry of this type. - - :param int redirect: - How many redirects to perform. Limit this to avoid infinite redirect - loops. - - A redirect is a HTTP response with a status code 301, 302, 303, 307 or - 308. - - Set to ``0`` to fail on the first retry of this type. - - Set to ``False`` to disable and imply ``raise_on_redirect=False``. - - :param int status: - How many times to retry on bad status codes. - - These are retries made on responses, where status code matches - ``status_forcelist``. - - Set to ``0`` to fail on the first retry of this type. - - :param int other: - How many times to retry on other errors. - - Other errors are errors that are not connect, read, redirect or status errors. - These errors might be raised after the request was sent to the server, so the - request might have side-effects. - - Set to ``0`` to fail on the first retry of this type. - - If ``total`` is not set, it's a good idea to set this to 0 to account - for unexpected edge cases and avoid infinite retry loops. - - :param Collection allowed_methods: - Set of uppercased HTTP method verbs that we should retry on. - - By default, we only retry on methods which are considered to be - idempotent (multiple requests with the same parameters end with the - same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`. - - Set to a ``None`` value to retry on any verb. - - :param Collection status_forcelist: - A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``allowed_methods`` - and the response status code is in ``status_forcelist``. - - By default, this is disabled with ``None``. - - :param float backoff_factor: - A backoff factor to apply between attempts after the second try - (most errors are resolved immediately by a second try without a - delay). urllib3 will sleep for:: - - {backoff factor} * (2 ** ({number of previous retries})) - - seconds. If `backoff_jitter` is non-zero, this sleep is extended by:: - - random.uniform(0, {backoff jitter}) - - seconds. For example, if the backoff_factor is 0.1, then :func:`Retry.sleep` will - sleep for [0.0s, 0.2s, 0.4s, 0.8s, ...] between retries. No backoff will ever - be longer than `backoff_max`. - - By default, backoff is disabled (factor set to 0). - - :param bool raise_on_redirect: Whether, if the number of redirects is - exhausted, to raise a MaxRetryError, or to return a response with a - response code in the 3xx range. - - :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: - whether we should raise an exception, or return a response, - if status falls in ``status_forcelist`` range and retries have - been exhausted. - - :param tuple history: The history of the request encountered during - each call to :meth:`~Retry.increment`. The list is in the order - the requests occurred. Each list item is of class :class:`RequestHistory`. - - :param bool respect_retry_after_header: - Whether to respect Retry-After header on status codes defined as - :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. - - :param Collection remove_headers_on_redirect: - Sequence of headers to remove from the request when a response - indicating a redirect is returned before firing off the redirected - request. - """ - - #: Default methods to be used for ``allowed_methods`` - DEFAULT_ALLOWED_METHODS = frozenset( - ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] - ) - - #: Default status codes to be used for ``status_forcelist`` - RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) - - #: Default headers to be used for ``remove_headers_on_redirect`` - DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset( - ["Cookie", "Authorization", "Proxy-Authorization"] - ) - - #: Default maximum backoff time. - DEFAULT_BACKOFF_MAX = 120 - - # Backward compatibility; assigned outside of the class. - DEFAULT: typing.ClassVar[Retry] - - def __init__( - self, - total: bool | int | None = 10, - connect: int | None = None, - read: int | None = None, - redirect: bool | int | None = None, - status: int | None = None, - other: int | None = None, - allowed_methods: typing.Collection[str] | None = DEFAULT_ALLOWED_METHODS, - status_forcelist: typing.Collection[int] | None = None, - backoff_factor: float = 0, - backoff_max: float = DEFAULT_BACKOFF_MAX, - raise_on_redirect: bool = True, - raise_on_status: bool = True, - history: tuple[RequestHistory, ...] | None = None, - respect_retry_after_header: bool = True, - remove_headers_on_redirect: typing.Collection[ - str - ] = DEFAULT_REMOVE_HEADERS_ON_REDIRECT, - backoff_jitter: float = 0.0, - ) -> None: - self.total = total - self.connect = connect - self.read = read - self.status = status - self.other = other - - if redirect is False or total is False: - redirect = 0 - raise_on_redirect = False - - self.redirect = redirect - self.status_forcelist = status_forcelist or set() - self.allowed_methods = allowed_methods - self.backoff_factor = backoff_factor - self.backoff_max = backoff_max - self.raise_on_redirect = raise_on_redirect - self.raise_on_status = raise_on_status - self.history = history or () - self.respect_retry_after_header = respect_retry_after_header - self.remove_headers_on_redirect = frozenset( - h.lower() for h in remove_headers_on_redirect - ) - self.backoff_jitter = backoff_jitter - - def new(self, **kw: typing.Any) -> Self: - params = dict( - total=self.total, - connect=self.connect, - read=self.read, - redirect=self.redirect, - status=self.status, - other=self.other, - allowed_methods=self.allowed_methods, - status_forcelist=self.status_forcelist, - backoff_factor=self.backoff_factor, - backoff_max=self.backoff_max, - raise_on_redirect=self.raise_on_redirect, - raise_on_status=self.raise_on_status, - history=self.history, - remove_headers_on_redirect=self.remove_headers_on_redirect, - respect_retry_after_header=self.respect_retry_after_header, - backoff_jitter=self.backoff_jitter, - ) - - params.update(kw) - return type(self)(**params) # type: ignore[arg-type] - - @classmethod - def from_int( - cls, - retries: Retry | bool | int | None, - redirect: bool | int | None = True, - default: Retry | bool | int | None = None, - ) -> Retry: - """Backwards-compatibility for the old retries format.""" - if retries is None: - retries = default if default is not None else cls.DEFAULT - - if isinstance(retries, Retry): - return retries - - redirect = bool(redirect) and None - new_retries = cls(retries, redirect=redirect) - log.debug("Converted retries value: %r -> %r", retries, new_retries) - return new_retries - - def get_backoff_time(self) -> float: - """Formula for computing the current backoff - - :rtype: float - """ - # We want to consider only the last consecutive errors sequence (Ignore redirects). - consecutive_errors_len = len( - list( - takewhile(lambda x: x.redirect_location is None, reversed(self.history)) - ) - ) - if consecutive_errors_len <= 1: - return 0 - - backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) - if self.backoff_jitter != 0.0: - backoff_value += random.random() * self.backoff_jitter - return float(max(0, min(self.backoff_max, backoff_value))) - - def parse_retry_after(self, retry_after: str) -> float: - seconds: float - # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 - if re.match(r"^\s*[0-9]+\s*$", retry_after): - seconds = int(retry_after) - else: - retry_date_tuple = email.utils.parsedate_tz(retry_after) - if retry_date_tuple is None: - raise InvalidHeader(f"Invalid Retry-After header: {retry_after}") - - retry_date = email.utils.mktime_tz(retry_date_tuple) - seconds = retry_date - time.time() - - seconds = max(seconds, 0) - - return seconds - - def get_retry_after(self, response: BaseHTTPResponse) -> float | None: - """Get the value of Retry-After in seconds.""" - - retry_after = response.headers.get("Retry-After") - - if retry_after is None: - return None - - return self.parse_retry_after(retry_after) - - def sleep_for_retry(self, response: BaseHTTPResponse) -> bool: - retry_after = self.get_retry_after(response) - if retry_after: - time.sleep(retry_after) - return True - - return False - - def _sleep_backoff(self) -> None: - backoff = self.get_backoff_time() - if backoff <= 0: - return - time.sleep(backoff) - - def sleep(self, response: BaseHTTPResponse | None = None) -> None: - """Sleep between retry attempts. - - This method will respect a server's ``Retry-After`` response header - and sleep the duration of the time requested. If that is not present, it - will use an exponential backoff. By default, the backoff factor is 0 and - this method will return immediately. - """ - - if self.respect_retry_after_header and response: - slept = self.sleep_for_retry(response) - if slept: - return - - self._sleep_backoff() - - def _is_connection_error(self, err: Exception) -> bool: - """Errors when we're fairly sure that the server did not receive the - request, so it should be safe to retry. - """ - if isinstance(err, ProxyError): - err = err.original_error - return isinstance(err, ConnectTimeoutError) - - def _is_read_error(self, err: Exception) -> bool: - """Errors that occur after the request has been started, so we should - assume that the server began processing it. - """ - return isinstance(err, (ReadTimeoutError, ProtocolError)) - - def _is_method_retryable(self, method: str) -> bool: - """Checks if a given HTTP method should be retried upon, depending if - it is included in the allowed_methods - """ - if self.allowed_methods and method.upper() not in self.allowed_methods: - return False - return True - - def is_retry( - self, method: str, status_code: int, has_retry_after: bool = False - ) -> bool: - """Is this method/status code retryable? (Based on allowlists and control - variables such as the number of total retries to allow, whether to - respect the Retry-After header, whether this header is present, and - whether the returned status code is on the list of status codes to - be retried upon on the presence of the aforementioned header) - """ - if not self._is_method_retryable(method): - return False - - if self.status_forcelist and status_code in self.status_forcelist: - return True - - return bool( - self.total - and self.respect_retry_after_header - and has_retry_after - and (status_code in self.RETRY_AFTER_STATUS_CODES) - ) - - def is_exhausted(self) -> bool: - """Are we out of retries?""" - retry_counts = [ - x - for x in ( - self.total, - self.connect, - self.read, - self.redirect, - self.status, - self.other, - ) - if x - ] - if not retry_counts: - return False - - return min(retry_counts) < 0 - - def increment( - self, - method: str | None = None, - url: str | None = None, - response: BaseHTTPResponse | None = None, - error: Exception | None = None, - _pool: ConnectionPool | None = None, - _stacktrace: TracebackType | None = None, - ) -> Self: - """Return a new Retry object with incremented retry counters. - - :param response: A response object, or None, if the server did not - return a response. - :type response: :class:`~urllib3.response.BaseHTTPResponse` - :param Exception error: An error encountered during the request, or - None if the response was received successfully. - - :return: A new ``Retry`` object. - """ - if self.total is False and error: - # Disabled, indicate to re-raise the error. - raise reraise(type(error), error, _stacktrace) - - total = self.total - if total is not None: - total -= 1 - - connect = self.connect - read = self.read - redirect = self.redirect - status_count = self.status - other = self.other - cause = "unknown" - status = None - redirect_location = None - - if error and self._is_connection_error(error): - # Connect retry? - if connect is False: - raise reraise(type(error), error, _stacktrace) - elif connect is not None: - connect -= 1 - - elif error and self._is_read_error(error): - # Read retry? - if read is False or method is None or not self._is_method_retryable(method): - raise reraise(type(error), error, _stacktrace) - elif read is not None: - read -= 1 - - elif error: - # Other retry? - if other is not None: - other -= 1 - - elif response and response.get_redirect_location(): - # Redirect retry? - if redirect is not None: - redirect -= 1 - cause = "too many redirects" - response_redirect_location = response.get_redirect_location() - if response_redirect_location: - redirect_location = response_redirect_location - status = response.status - - else: - # Incrementing because of a server error like a 500 in - # status_forcelist and the given method is in the allowed_methods - cause = ResponseError.GENERIC_ERROR - if response and response.status: - if status_count is not None: - status_count -= 1 - cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) - status = response.status - - history = self.history + ( - RequestHistory(method, url, error, status, redirect_location), - ) - - new_retry = self.new( - total=total, - connect=connect, - read=read, - redirect=redirect, - status=status_count, - other=other, - history=history, - ) - - if new_retry.is_exhausted(): - reason = error or ResponseError(cause) - raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] - - log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) - - return new_retry - - def __repr__(self) -> str: - return ( - f"{type(self).__name__}(total={self.total}, connect={self.connect}, " - f"read={self.read}, redirect={self.redirect}, status={self.status})" - ) - - -# For backwards compatibility (equivalent to pre-v1.9): -Retry.DEFAULT = Retry(3) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/ssl_.py b/.venv/lib/python3.12/site-packages/urllib3/util/ssl_.py deleted file mode 100644 index c46dd83..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/ssl_.py +++ /dev/null @@ -1,509 +0,0 @@ -from __future__ import annotations - -import hmac -import os -import socket -import sys -import typing -import warnings -from binascii import unhexlify -from hashlib import md5, sha1, sha256 - -from ..exceptions import ProxySchemeUnsupported, SSLError -from .url import _BRACELESS_IPV6_ADDRZ_RE, _IPV4_RE - -SSLContext = None -SSLTransport = None -HAS_NEVER_CHECK_COMMON_NAME = False -IS_PYOPENSSL = False -ALPN_PROTOCOLS = ["http/1.1"] - -_TYPE_VERSION_INFO = typing.Tuple[int, int, int, str, int] - -# Maps the length of a digest to a possible hash function producing this digest -HASHFUNC_MAP = {32: md5, 40: sha1, 64: sha256} - - -def _is_bpo_43522_fixed( - implementation_name: str, - version_info: _TYPE_VERSION_INFO, - pypy_version_info: _TYPE_VERSION_INFO | None, -) -> bool: - """Return True for CPython 3.8.9+, 3.9.3+ or 3.10+ and PyPy 7.3.8+ where - setting SSLContext.hostname_checks_common_name to False works. - - Outside of CPython and PyPy we don't know which implementations work - or not so we conservatively use our hostname matching as we know that works - on all implementations. - - https://github.com/urllib3/urllib3/issues/2192#issuecomment-821832963 - https://foss.heptapod.net/pypy/pypy/-/issues/3539 - """ - if implementation_name == "pypy": - # https://foss.heptapod.net/pypy/pypy/-/issues/3129 - return pypy_version_info >= (7, 3, 8) # type: ignore[operator] - elif implementation_name == "cpython": - major_minor = version_info[:2] - micro = version_info[2] - return ( - (major_minor == (3, 8) and micro >= 9) - or (major_minor == (3, 9) and micro >= 3) - or major_minor >= (3, 10) - ) - else: # Defensive: - return False - - -def _is_has_never_check_common_name_reliable( - openssl_version: str, - openssl_version_number: int, - implementation_name: str, - version_info: _TYPE_VERSION_INFO, - pypy_version_info: _TYPE_VERSION_INFO | None, -) -> bool: - # As of May 2023, all released versions of LibreSSL fail to reject certificates with - # only common names, see https://github.com/urllib3/urllib3/pull/3024 - is_openssl = openssl_version.startswith("OpenSSL ") - # Before fixing OpenSSL issue #14579, the SSL_new() API was not copying hostflags - # like X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, which tripped up CPython. - # https://github.com/openssl/openssl/issues/14579 - # This was released in OpenSSL 1.1.1l+ (>=0x101010cf) - is_openssl_issue_14579_fixed = openssl_version_number >= 0x101010CF - - return is_openssl and ( - is_openssl_issue_14579_fixed - or _is_bpo_43522_fixed(implementation_name, version_info, pypy_version_info) - ) - - -if typing.TYPE_CHECKING: - from ssl import VerifyMode - from typing import TypedDict - - from .ssltransport import SSLTransport as SSLTransportType - - class _TYPE_PEER_CERT_RET_DICT(TypedDict, total=False): - subjectAltName: tuple[tuple[str, str], ...] - subject: tuple[tuple[tuple[str, str], ...], ...] - serialNumber: str - - -# Mapping from 'ssl.PROTOCOL_TLSX' to 'TLSVersion.X' -_SSL_VERSION_TO_TLS_VERSION: dict[int, int] = {} - -try: # Do we have ssl at all? - import ssl - from ssl import ( # type: ignore[assignment] - CERT_REQUIRED, - HAS_NEVER_CHECK_COMMON_NAME, - OP_NO_COMPRESSION, - OP_NO_TICKET, - OPENSSL_VERSION, - OPENSSL_VERSION_NUMBER, - PROTOCOL_TLS, - PROTOCOL_TLS_CLIENT, - OP_NO_SSLv2, - OP_NO_SSLv3, - SSLContext, - TLSVersion, - ) - - PROTOCOL_SSLv23 = PROTOCOL_TLS - - # Setting SSLContext.hostname_checks_common_name = False didn't work before CPython - # 3.8.9, 3.9.3, and 3.10 (but OK on PyPy) or OpenSSL 1.1.1l+ - if HAS_NEVER_CHECK_COMMON_NAME and not _is_has_never_check_common_name_reliable( - OPENSSL_VERSION, - OPENSSL_VERSION_NUMBER, - sys.implementation.name, - sys.version_info, - sys.pypy_version_info if sys.implementation.name == "pypy" else None, # type: ignore[attr-defined] - ): - HAS_NEVER_CHECK_COMMON_NAME = False - - # Need to be careful here in case old TLS versions get - # removed in future 'ssl' module implementations. - for attr in ("TLSv1", "TLSv1_1", "TLSv1_2"): - try: - _SSL_VERSION_TO_TLS_VERSION[getattr(ssl, f"PROTOCOL_{attr}")] = getattr( - TLSVersion, attr - ) - except AttributeError: # Defensive: - continue - - from .ssltransport import SSLTransport # type: ignore[assignment] -except ImportError: - OP_NO_COMPRESSION = 0x20000 # type: ignore[assignment] - OP_NO_TICKET = 0x4000 # type: ignore[assignment] - OP_NO_SSLv2 = 0x1000000 # type: ignore[assignment] - OP_NO_SSLv3 = 0x2000000 # type: ignore[assignment] - PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 # type: ignore[assignment] - PROTOCOL_TLS_CLIENT = 16 # type: ignore[assignment] - - -_TYPE_PEER_CERT_RET = typing.Union["_TYPE_PEER_CERT_RET_DICT", bytes, None] - - -def assert_fingerprint(cert: bytes | None, fingerprint: str) -> None: - """ - Checks if given fingerprint matches the supplied certificate. - - :param cert: - Certificate as bytes object. - :param fingerprint: - Fingerprint as string of hexdigits, can be interspersed by colons. - """ - - if cert is None: - raise SSLError("No certificate for the peer.") - - fingerprint = fingerprint.replace(":", "").lower() - digest_length = len(fingerprint) - hashfunc = HASHFUNC_MAP.get(digest_length) - if not hashfunc: - raise SSLError(f"Fingerprint of invalid length: {fingerprint}") - - # We need encode() here for py32; works on py2 and p33. - fingerprint_bytes = unhexlify(fingerprint.encode()) - - cert_digest = hashfunc(cert).digest() - - if not hmac.compare_digest(cert_digest, fingerprint_bytes): - raise SSLError( - f'Fingerprints did not match. Expected "{fingerprint}", got "{cert_digest.hex()}"' - ) - - -def resolve_cert_reqs(candidate: None | int | str) -> VerifyMode: - """ - Resolves the argument to a numeric constant, which can be passed to - the wrap_socket function/method from the ssl module. - Defaults to :data:`ssl.CERT_REQUIRED`. - If given a string it is assumed to be the name of the constant in the - :mod:`ssl` module or its abbreviation. - (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. - If it's neither `None` nor a string we assume it is already the numeric - constant which can directly be passed to wrap_socket. - """ - if candidate is None: - return CERT_REQUIRED - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, "CERT_" + candidate) - return res # type: ignore[no-any-return] - - return candidate # type: ignore[return-value] - - -def resolve_ssl_version(candidate: None | int | str) -> int: - """ - like resolve_cert_reqs - """ - if candidate is None: - return PROTOCOL_TLS - - if isinstance(candidate, str): - res = getattr(ssl, candidate, None) - if res is None: - res = getattr(ssl, "PROTOCOL_" + candidate) - return typing.cast(int, res) - - return candidate - - -def create_urllib3_context( - ssl_version: int | None = None, - cert_reqs: int | None = None, - options: int | None = None, - ciphers: str | None = None, - ssl_minimum_version: int | None = None, - ssl_maximum_version: int | None = None, -) -> ssl.SSLContext: - """Creates and configures an :class:`ssl.SSLContext` instance for use with urllib3. - - :param ssl_version: - The desired protocol version to use. This will default to - PROTOCOL_SSLv23 which will negotiate the highest protocol that both - the server and your installation of OpenSSL support. - - This parameter is deprecated instead use 'ssl_minimum_version'. - :param ssl_minimum_version: - The minimum version of TLS to be used. Use the 'ssl.TLSVersion' enum for specifying the value. - :param ssl_maximum_version: - The maximum version of TLS to be used. Use the 'ssl.TLSVersion' enum for specifying the value. - Not recommended to set to anything other than 'ssl.TLSVersion.MAXIMUM_SUPPORTED' which is the - default value. - :param cert_reqs: - Whether to require the certificate verification. This defaults to - ``ssl.CERT_REQUIRED``. - :param options: - Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, - ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``, and ``ssl.OP_NO_TICKET``. - :param ciphers: - Which cipher suites to allow the server to select. Defaults to either system configured - ciphers if OpenSSL 1.1.1+, otherwise uses a secure default set of ciphers. - :returns: - Constructed SSLContext object with specified options - :rtype: SSLContext - """ - if SSLContext is None: - raise TypeError("Can't create an SSLContext object without an ssl module") - - # This means 'ssl_version' was specified as an exact value. - if ssl_version not in (None, PROTOCOL_TLS, PROTOCOL_TLS_CLIENT): - # Disallow setting 'ssl_version' and 'ssl_minimum|maximum_version' - # to avoid conflicts. - if ssl_minimum_version is not None or ssl_maximum_version is not None: - raise ValueError( - "Can't specify both 'ssl_version' and either " - "'ssl_minimum_version' or 'ssl_maximum_version'" - ) - - # 'ssl_version' is deprecated and will be removed in the future. - else: - # Use 'ssl_minimum_version' and 'ssl_maximum_version' instead. - ssl_minimum_version = _SSL_VERSION_TO_TLS_VERSION.get( - ssl_version, TLSVersion.MINIMUM_SUPPORTED - ) - ssl_maximum_version = _SSL_VERSION_TO_TLS_VERSION.get( - ssl_version, TLSVersion.MAXIMUM_SUPPORTED - ) - - # This warning message is pushing users to use 'ssl_minimum_version' - # instead of both min/max. Best practice is to only set the minimum version and - # keep the maximum version to be it's default value: 'TLSVersion.MAXIMUM_SUPPORTED' - warnings.warn( - "'ssl_version' option is deprecated and will be " - "removed in urllib3 v2.1.0. Instead use 'ssl_minimum_version'", - category=DeprecationWarning, - stacklevel=2, - ) - - # PROTOCOL_TLS is deprecated in Python 3.10 so we always use PROTOCOL_TLS_CLIENT - context = SSLContext(PROTOCOL_TLS_CLIENT) - - if ssl_minimum_version is not None: - context.minimum_version = ssl_minimum_version - else: # Python <3.10 defaults to 'MINIMUM_SUPPORTED' so explicitly set TLSv1.2 here - context.minimum_version = TLSVersion.TLSv1_2 - - if ssl_maximum_version is not None: - context.maximum_version = ssl_maximum_version - - # Unless we're given ciphers defer to either system ciphers in - # the case of OpenSSL 1.1.1+ or use our own secure default ciphers. - if ciphers: - context.set_ciphers(ciphers) - - # Setting the default here, as we may have no ssl module on import - cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs - - if options is None: - options = 0 - # SSLv2 is easily broken and is considered harmful and dangerous - options |= OP_NO_SSLv2 - # SSLv3 has several problems and is now dangerous - options |= OP_NO_SSLv3 - # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ - # (issue #309) - options |= OP_NO_COMPRESSION - # TLSv1.2 only. Unless set explicitly, do not request tickets. - # This may save some bandwidth on wire, and although the ticket is encrypted, - # there is a risk associated with it being on wire, - # if the server is not rotating its ticketing keys properly. - options |= OP_NO_TICKET - - context.options |= options - - # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is - # necessary for conditional client cert authentication with TLS 1.3. - # The attribute is None for OpenSSL <= 1.1.0 or does not exist when using - # an SSLContext created by pyOpenSSL. - if getattr(context, "post_handshake_auth", None) is not None: - context.post_handshake_auth = True - - # The order of the below lines setting verify_mode and check_hostname - # matter due to safe-guards SSLContext has to prevent an SSLContext with - # check_hostname=True, verify_mode=NONE/OPTIONAL. - # We always set 'check_hostname=False' for pyOpenSSL so we rely on our own - # 'ssl.match_hostname()' implementation. - if cert_reqs == ssl.CERT_REQUIRED and not IS_PYOPENSSL: - context.verify_mode = cert_reqs - context.check_hostname = True - else: - context.check_hostname = False - context.verify_mode = cert_reqs - - try: - context.hostname_checks_common_name = False - except AttributeError: # Defensive: for CPython < 3.8.9 and 3.9.3; for PyPy < 7.3.8 - pass - - # Enable logging of TLS session keys via defacto standard environment variable - # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. - if hasattr(context, "keylog_filename"): - sslkeylogfile = os.environ.get("SSLKEYLOGFILE") - if sslkeylogfile: - context.keylog_filename = sslkeylogfile - - return context - - -@typing.overload -def ssl_wrap_socket( - sock: socket.socket, - keyfile: str | None = ..., - certfile: str | None = ..., - cert_reqs: int | None = ..., - ca_certs: str | None = ..., - server_hostname: str | None = ..., - ssl_version: int | None = ..., - ciphers: str | None = ..., - ssl_context: ssl.SSLContext | None = ..., - ca_cert_dir: str | None = ..., - key_password: str | None = ..., - ca_cert_data: None | str | bytes = ..., - tls_in_tls: typing.Literal[False] = ..., -) -> ssl.SSLSocket: - ... - - -@typing.overload -def ssl_wrap_socket( - sock: socket.socket, - keyfile: str | None = ..., - certfile: str | None = ..., - cert_reqs: int | None = ..., - ca_certs: str | None = ..., - server_hostname: str | None = ..., - ssl_version: int | None = ..., - ciphers: str | None = ..., - ssl_context: ssl.SSLContext | None = ..., - ca_cert_dir: str | None = ..., - key_password: str | None = ..., - ca_cert_data: None | str | bytes = ..., - tls_in_tls: bool = ..., -) -> ssl.SSLSocket | SSLTransportType: - ... - - -def ssl_wrap_socket( - sock: socket.socket, - keyfile: str | None = None, - certfile: str | None = None, - cert_reqs: int | None = None, - ca_certs: str | None = None, - server_hostname: str | None = None, - ssl_version: int | None = None, - ciphers: str | None = None, - ssl_context: ssl.SSLContext | None = None, - ca_cert_dir: str | None = None, - key_password: str | None = None, - ca_cert_data: None | str | bytes = None, - tls_in_tls: bool = False, -) -> ssl.SSLSocket | SSLTransportType: - """ - All arguments except for server_hostname, ssl_context, tls_in_tls, ca_cert_data and - ca_cert_dir have the same meaning as they do when using - :func:`ssl.create_default_context`, :meth:`ssl.SSLContext.load_cert_chain`, - :meth:`ssl.SSLContext.set_ciphers` and :meth:`ssl.SSLContext.wrap_socket`. - - :param server_hostname: - When SNI is supported, the expected hostname of the certificate - :param ssl_context: - A pre-made :class:`SSLContext` object. If none is provided, one will - be created using :func:`create_urllib3_context`. - :param ciphers: - A string of ciphers we wish the client to support. - :param ca_cert_dir: - A directory containing CA certificates in multiple separate files, as - supported by OpenSSL's -CApath flag or the capath argument to - SSLContext.load_verify_locations(). - :param key_password: - Optional password if the keyfile is encrypted. - :param ca_cert_data: - Optional string containing CA certificates in PEM format suitable for - passing as the cadata parameter to SSLContext.load_verify_locations() - :param tls_in_tls: - Use SSLTransport to wrap the existing socket. - """ - context = ssl_context - if context is None: - # Note: This branch of code and all the variables in it are only used in tests. - # We should consider deprecating and removing this code. - context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) - - if ca_certs or ca_cert_dir or ca_cert_data: - try: - context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) - except OSError as e: - raise SSLError(e) from e - - elif ssl_context is None and hasattr(context, "load_default_certs"): - # try to load OS default certs; works well on Windows. - context.load_default_certs() - - # Attempt to detect if we get the goofy behavior of the - # keyfile being encrypted and OpenSSL asking for the - # passphrase via the terminal and instead error out. - if keyfile and key_password is None and _is_key_file_encrypted(keyfile): - raise SSLError("Client private key is encrypted, password is required") - - if certfile: - if key_password is None: - context.load_cert_chain(certfile, keyfile) - else: - context.load_cert_chain(certfile, keyfile, key_password) - - try: - context.set_alpn_protocols(ALPN_PROTOCOLS) - except NotImplementedError: # Defensive: in CI, we always have set_alpn_protocols - pass - - ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls, server_hostname) - return ssl_sock - - -def is_ipaddress(hostname: str | bytes) -> bool: - """Detects whether the hostname given is an IPv4 or IPv6 address. - Also detects IPv6 addresses with Zone IDs. - - :param str hostname: Hostname to examine. - :return: True if the hostname is an IP address, False otherwise. - """ - if isinstance(hostname, bytes): - # IDN A-label bytes are ASCII compatible. - hostname = hostname.decode("ascii") - return bool(_IPV4_RE.match(hostname) or _BRACELESS_IPV6_ADDRZ_RE.match(hostname)) - - -def _is_key_file_encrypted(key_file: str) -> bool: - """Detects if a key file is encrypted or not.""" - with open(key_file) as f: - for line in f: - # Look for Proc-Type: 4,ENCRYPTED - if "ENCRYPTED" in line: - return True - - return False - - -def _ssl_wrap_socket_impl( - sock: socket.socket, - ssl_context: ssl.SSLContext, - tls_in_tls: bool, - server_hostname: str | None = None, -) -> ssl.SSLSocket | SSLTransportType: - if tls_in_tls: - if not SSLTransport: - # Import error, ssl is not available. - raise ProxySchemeUnsupported( - "TLS in TLS requires support for the 'ssl' module" - ) - - SSLTransport._validate_ssl_context_for_tls_in_tls(ssl_context) - return SSLTransport(sock, ssl_context, server_hostname) - - return ssl_context.wrap_socket(sock, server_hostname=server_hostname) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/ssl_match_hostname.py b/.venv/lib/python3.12/site-packages/urllib3/util/ssl_match_hostname.py deleted file mode 100644 index 453cfd4..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/ssl_match_hostname.py +++ /dev/null @@ -1,159 +0,0 @@ -"""The match_hostname() function from Python 3.5, essential when using SSL.""" - -# Note: This file is under the PSF license as the code comes from the python -# stdlib. http://docs.python.org/3/license.html -# It is modified to remove commonName support. - -from __future__ import annotations - -import ipaddress -import re -import typing -from ipaddress import IPv4Address, IPv6Address - -if typing.TYPE_CHECKING: - from .ssl_ import _TYPE_PEER_CERT_RET_DICT - -__version__ = "3.5.0.1" - - -class CertificateError(ValueError): - pass - - -def _dnsname_match( - dn: typing.Any, hostname: str, max_wildcards: int = 1 -) -> typing.Match[str] | None | bool: - """Matching according to RFC 6125, section 6.4.3 - - http://tools.ietf.org/html/rfc6125#section-6.4.3 - """ - pats = [] - if not dn: - return False - - # Ported from python3-syntax: - # leftmost, *remainder = dn.split(r'.') - parts = dn.split(r".") - leftmost = parts[0] - remainder = parts[1:] - - wildcards = leftmost.count("*") - if wildcards > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survey of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn) - ) - - # speed up common case w/o wildcards - if not wildcards: - return bool(dn.lower() == hostname.lower()) - - # RFC 6125, section 6.4.3, subitem 1. - # The client SHOULD NOT attempt to match a presented identifier in which - # the wildcard character comprises a label other than the left-most label. - if leftmost == "*": - # When '*' is a fragment by itself, it matches a non-empty dotless - # fragment. - pats.append("[^.]+") - elif leftmost.startswith("xn--") or hostname.startswith("xn--"): - # RFC 6125, section 6.4.3, subitem 3. - # The client SHOULD NOT attempt to match a presented identifier - # where the wildcard character is embedded within an A-label or - # U-label of an internationalized domain name. - pats.append(re.escape(leftmost)) - else: - # Otherwise, '*' matches any dotless string, e.g. www* - pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) - - # add the remaining fragments, ignore any wildcards - for frag in remainder: - pats.append(re.escape(frag)) - - pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) - return pat.match(hostname) - - -def _ipaddress_match(ipname: str, host_ip: IPv4Address | IPv6Address) -> bool: - """Exact matching of IP addresses. - - RFC 9110 section 4.3.5: "A reference identity of IP-ID contains the decoded - bytes of the IP address. An IP version 4 address is 4 octets, and an IP - version 6 address is 16 octets. [...] A reference identity of type IP-ID - matches if the address is identical to an iPAddress value of the - subjectAltName extension of the certificate." - """ - # OpenSSL may add a trailing newline to a subjectAltName's IP address - # Divergence from upstream: ipaddress can't handle byte str - ip = ipaddress.ip_address(ipname.rstrip()) - return bool(ip.packed == host_ip.packed) - - -def match_hostname( - cert: _TYPE_PEER_CERT_RET_DICT | None, - hostname: str, - hostname_checks_common_name: bool = False, -) -> None: - """Verify that *cert* (in decoded format as returned by - SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 - rules are followed, but IP addresses are not accepted for *hostname*. - - CertificateError is raised on failure. On success, the function - returns nothing. - """ - if not cert: - raise ValueError( - "empty or no certificate, match_hostname needs a " - "SSL socket or SSL context with either " - "CERT_OPTIONAL or CERT_REQUIRED" - ) - try: - # Divergence from upstream: ipaddress can't handle byte str - # - # The ipaddress module shipped with Python < 3.9 does not support - # scoped IPv6 addresses so we unconditionally strip the Zone IDs for - # now. Once we drop support for Python 3.9 we can remove this branch. - if "%" in hostname: - host_ip = ipaddress.ip_address(hostname[: hostname.rfind("%")]) - else: - host_ip = ipaddress.ip_address(hostname) - - except ValueError: - # Not an IP address (common case) - host_ip = None - dnsnames = [] - san: tuple[tuple[str, str], ...] = cert.get("subjectAltName", ()) - key: str - value: str - for key, value in san: - if key == "DNS": - if host_ip is None and _dnsname_match(value, hostname): - return - dnsnames.append(value) - elif key == "IP Address": - if host_ip is not None and _ipaddress_match(value, host_ip): - return - dnsnames.append(value) - - # We only check 'commonName' if it's enabled and we're not verifying - # an IP address. IP addresses aren't valid within 'commonName'. - if hostname_checks_common_name and host_ip is None and not dnsnames: - for sub in cert.get("subject", ()): - for key, value in sub: - if key == "commonName": - if _dnsname_match(value, hostname): - return - dnsnames.append(value) - - if len(dnsnames) > 1: - raise CertificateError( - "hostname %r " - "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) - ) - elif len(dnsnames) == 1: - raise CertificateError(f"hostname {hostname!r} doesn't match {dnsnames[0]!r}") - else: - raise CertificateError("no appropriate subjectAltName fields were found") diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/ssltransport.py b/.venv/lib/python3.12/site-packages/urllib3/util/ssltransport.py deleted file mode 100644 index b52c477..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/ssltransport.py +++ /dev/null @@ -1,279 +0,0 @@ -from __future__ import annotations - -import io -import socket -import ssl -import typing - -from ..exceptions import ProxySchemeUnsupported - -if typing.TYPE_CHECKING: - from typing_extensions import Self - - from .ssl_ import _TYPE_PEER_CERT_RET, _TYPE_PEER_CERT_RET_DICT - - -_WriteBuffer = typing.Union[bytearray, memoryview] -_ReturnValue = typing.TypeVar("_ReturnValue") - -SSL_BLOCKSIZE = 16384 - - -class SSLTransport: - """ - The SSLTransport wraps an existing socket and establishes an SSL connection. - - Contrary to Python's implementation of SSLSocket, it allows you to chain - multiple TLS connections together. It's particularly useful if you need to - implement TLS within TLS. - - The class supports most of the socket API operations. - """ - - @staticmethod - def _validate_ssl_context_for_tls_in_tls(ssl_context: ssl.SSLContext) -> None: - """ - Raises a ProxySchemeUnsupported if the provided ssl_context can't be used - for TLS in TLS. - - The only requirement is that the ssl_context provides the 'wrap_bio' - methods. - """ - - if not hasattr(ssl_context, "wrap_bio"): - raise ProxySchemeUnsupported( - "TLS in TLS requires SSLContext.wrap_bio() which isn't " - "available on non-native SSLContext" - ) - - def __init__( - self, - socket: socket.socket, - ssl_context: ssl.SSLContext, - server_hostname: str | None = None, - suppress_ragged_eofs: bool = True, - ) -> None: - """ - Create an SSLTransport around socket using the provided ssl_context. - """ - self.incoming = ssl.MemoryBIO() - self.outgoing = ssl.MemoryBIO() - - self.suppress_ragged_eofs = suppress_ragged_eofs - self.socket = socket - - self.sslobj = ssl_context.wrap_bio( - self.incoming, self.outgoing, server_hostname=server_hostname - ) - - # Perform initial handshake. - self._ssl_io_loop(self.sslobj.do_handshake) - - def __enter__(self) -> Self: - return self - - def __exit__(self, *_: typing.Any) -> None: - self.close() - - def fileno(self) -> int: - return self.socket.fileno() - - def read(self, len: int = 1024, buffer: typing.Any | None = None) -> int | bytes: - return self._wrap_ssl_read(len, buffer) - - def recv(self, buflen: int = 1024, flags: int = 0) -> int | bytes: - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to recv") - return self._wrap_ssl_read(buflen) - - def recv_into( - self, - buffer: _WriteBuffer, - nbytes: int | None = None, - flags: int = 0, - ) -> None | int | bytes: - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to recv_into") - if nbytes is None: - nbytes = len(buffer) - return self.read(nbytes, buffer) - - def sendall(self, data: bytes, flags: int = 0) -> None: - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to sendall") - count = 0 - with memoryview(data) as view, view.cast("B") as byte_view: - amount = len(byte_view) - while count < amount: - v = self.send(byte_view[count:]) - count += v - - def send(self, data: bytes, flags: int = 0) -> int: - if flags != 0: - raise ValueError("non-zero flags not allowed in calls to send") - return self._ssl_io_loop(self.sslobj.write, data) - - def makefile( - self, - mode: str, - buffering: int | None = None, - *, - encoding: str | None = None, - errors: str | None = None, - newline: str | None = None, - ) -> typing.BinaryIO | typing.TextIO | socket.SocketIO: - """ - Python's httpclient uses makefile and buffered io when reading HTTP - messages and we need to support it. - - This is unfortunately a copy and paste of socket.py makefile with small - changes to point to the socket directly. - """ - if not set(mode) <= {"r", "w", "b"}: - raise ValueError(f"invalid mode {mode!r} (only r, w, b allowed)") - - writing = "w" in mode - reading = "r" in mode or not writing - assert reading or writing - binary = "b" in mode - rawmode = "" - if reading: - rawmode += "r" - if writing: - rawmode += "w" - raw = socket.SocketIO(self, rawmode) # type: ignore[arg-type] - self.socket._io_refs += 1 # type: ignore[attr-defined] - if buffering is None: - buffering = -1 - if buffering < 0: - buffering = io.DEFAULT_BUFFER_SIZE - if buffering == 0: - if not binary: - raise ValueError("unbuffered streams must be binary") - return raw - buffer: typing.BinaryIO - if reading and writing: - buffer = io.BufferedRWPair(raw, raw, buffering) # type: ignore[assignment] - elif reading: - buffer = io.BufferedReader(raw, buffering) - else: - assert writing - buffer = io.BufferedWriter(raw, buffering) - if binary: - return buffer - text = io.TextIOWrapper(buffer, encoding, errors, newline) - text.mode = mode # type: ignore[misc] - return text - - def unwrap(self) -> None: - self._ssl_io_loop(self.sslobj.unwrap) - - def close(self) -> None: - self.socket.close() - - @typing.overload - def getpeercert( - self, binary_form: typing.Literal[False] = ... - ) -> _TYPE_PEER_CERT_RET_DICT | None: - ... - - @typing.overload - def getpeercert(self, binary_form: typing.Literal[True]) -> bytes | None: - ... - - def getpeercert(self, binary_form: bool = False) -> _TYPE_PEER_CERT_RET: - return self.sslobj.getpeercert(binary_form) # type: ignore[return-value] - - def version(self) -> str | None: - return self.sslobj.version() - - def cipher(self) -> tuple[str, str, int] | None: - return self.sslobj.cipher() - - def selected_alpn_protocol(self) -> str | None: - return self.sslobj.selected_alpn_protocol() - - def selected_npn_protocol(self) -> str | None: - return self.sslobj.selected_npn_protocol() - - def shared_ciphers(self) -> list[tuple[str, str, int]] | None: - return self.sslobj.shared_ciphers() - - def compression(self) -> str | None: - return self.sslobj.compression() - - def settimeout(self, value: float | None) -> None: - self.socket.settimeout(value) - - def gettimeout(self) -> float | None: - return self.socket.gettimeout() - - def _decref_socketios(self) -> None: - self.socket._decref_socketios() # type: ignore[attr-defined] - - def _wrap_ssl_read(self, len: int, buffer: bytearray | None = None) -> int | bytes: - try: - return self._ssl_io_loop(self.sslobj.read, len, buffer) - except ssl.SSLError as e: - if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: - return 0 # eof, return 0. - else: - raise - - # func is sslobj.do_handshake or sslobj.unwrap - @typing.overload - def _ssl_io_loop(self, func: typing.Callable[[], None]) -> None: - ... - - # func is sslobj.write, arg1 is data - @typing.overload - def _ssl_io_loop(self, func: typing.Callable[[bytes], int], arg1: bytes) -> int: - ... - - # func is sslobj.read, arg1 is len, arg2 is buffer - @typing.overload - def _ssl_io_loop( - self, - func: typing.Callable[[int, bytearray | None], bytes], - arg1: int, - arg2: bytearray | None, - ) -> bytes: - ... - - def _ssl_io_loop( - self, - func: typing.Callable[..., _ReturnValue], - arg1: None | bytes | int = None, - arg2: bytearray | None = None, - ) -> _ReturnValue: - """Performs an I/O loop between incoming/outgoing and the socket.""" - should_loop = True - ret = None - - while should_loop: - errno = None - try: - if arg1 is None and arg2 is None: - ret = func() - elif arg2 is None: - ret = func(arg1) - else: - ret = func(arg1, arg2) - except ssl.SSLError as e: - if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): - # WANT_READ, and WANT_WRITE are expected, others are not. - raise e - errno = e.errno - - buf = self.outgoing.read() - self.socket.sendall(buf) - - if errno is None: - should_loop = False - elif errno == ssl.SSL_ERROR_WANT_READ: - buf = self.socket.recv(SSL_BLOCKSIZE) - if buf: - self.incoming.write(buf) - else: - self.incoming.write_eof() - return typing.cast(_ReturnValue, ret) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/timeout.py b/.venv/lib/python3.12/site-packages/urllib3/util/timeout.py deleted file mode 100644 index 4bb1be1..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/timeout.py +++ /dev/null @@ -1,275 +0,0 @@ -from __future__ import annotations - -import time -import typing -from enum import Enum -from socket import getdefaulttimeout - -from ..exceptions import TimeoutStateError - -if typing.TYPE_CHECKING: - from typing import Final - - -class _TYPE_DEFAULT(Enum): - # This value should never be passed to socket.settimeout() so for safety we use a -1. - # socket.settimout() raises a ValueError for negative values. - token = -1 - - -_DEFAULT_TIMEOUT: Final[_TYPE_DEFAULT] = _TYPE_DEFAULT.token - -_TYPE_TIMEOUT = typing.Optional[typing.Union[float, _TYPE_DEFAULT]] - - -class Timeout: - """Timeout configuration. - - Timeouts can be defined as a default for a pool: - - .. code-block:: python - - import urllib3 - - timeout = urllib3.util.Timeout(connect=2.0, read=7.0) - - http = urllib3.PoolManager(timeout=timeout) - - resp = http.request("GET", "https://example.com/") - - print(resp.status) - - Or per-request (which overrides the default for the pool): - - .. code-block:: python - - response = http.request("GET", "https://example.com/", timeout=Timeout(10)) - - Timeouts can be disabled by setting all the parameters to ``None``: - - .. code-block:: python - - no_timeout = Timeout(connect=None, read=None) - response = http.request("GET", "https://example.com/", timeout=no_timeout) - - - :param total: - This combines the connect and read timeouts into one; the read timeout - will be set to the time leftover from the connect attempt. In the - event that both a connect timeout and a total are specified, or a read - timeout and a total are specified, the shorter timeout will be applied. - - Defaults to None. - - :type total: int, float, or None - - :param connect: - The maximum amount of time (in seconds) to wait for a connection - attempt to a server to succeed. Omitting the parameter will default the - connect timeout to the system default, probably `the global default - timeout in socket.py - `_. - None will set an infinite timeout for connection attempts. - - :type connect: int, float, or None - - :param read: - The maximum amount of time (in seconds) to wait between consecutive - read operations for a response from the server. Omitting the parameter - will default the read timeout to the system default, probably `the - global default timeout in socket.py - `_. - None will set an infinite timeout. - - :type read: int, float, or None - - .. note:: - - Many factors can affect the total amount of time for urllib3 to return - an HTTP response. - - For example, Python's DNS resolver does not obey the timeout specified - on the socket. Other factors that can affect total request time include - high CPU load, high swap, the program running at a low priority level, - or other behaviors. - - In addition, the read and total timeouts only measure the time between - read operations on the socket connecting the client and the server, - not the total amount of time for the request to return a complete - response. For most requests, the timeout is raised because the server - has not sent the first byte in the specified time. This is not always - the case; if a server streams one byte every fifteen seconds, a timeout - of 20 seconds will not trigger, even though the request will take - several minutes to complete. - """ - - #: A sentinel object representing the default timeout value - DEFAULT_TIMEOUT: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT - - def __init__( - self, - total: _TYPE_TIMEOUT = None, - connect: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - read: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, - ) -> None: - self._connect = self._validate_timeout(connect, "connect") - self._read = self._validate_timeout(read, "read") - self.total = self._validate_timeout(total, "total") - self._start_connect: float | None = None - - def __repr__(self) -> str: - return f"{type(self).__name__}(connect={self._connect!r}, read={self._read!r}, total={self.total!r})" - - # __str__ provided for backwards compatibility - __str__ = __repr__ - - @staticmethod - def resolve_default_timeout(timeout: _TYPE_TIMEOUT) -> float | None: - return getdefaulttimeout() if timeout is _DEFAULT_TIMEOUT else timeout - - @classmethod - def _validate_timeout(cls, value: _TYPE_TIMEOUT, name: str) -> _TYPE_TIMEOUT: - """Check that a timeout attribute is valid. - - :param value: The timeout value to validate - :param name: The name of the timeout attribute to validate. This is - used to specify in error messages. - :return: The validated and casted version of the given value. - :raises ValueError: If it is a numeric value less than or equal to - zero, or the type is not an integer, float, or None. - """ - if value is None or value is _DEFAULT_TIMEOUT: - return value - - if isinstance(value, bool): - raise ValueError( - "Timeout cannot be a boolean value. It must " - "be an int, float or None." - ) - try: - float(value) - except (TypeError, ValueError): - raise ValueError( - "Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value) - ) from None - - try: - if value <= 0: - raise ValueError( - "Attempted to set %s timeout to %s, but the " - "timeout cannot be set to a value less " - "than or equal to 0." % (name, value) - ) - except TypeError: - raise ValueError( - "Timeout value %s was %s, but it must be an " - "int, float or None." % (name, value) - ) from None - - return value - - @classmethod - def from_float(cls, timeout: _TYPE_TIMEOUT) -> Timeout: - """Create a new Timeout from a legacy timeout value. - - The timeout value used by httplib.py sets the same timeout on the - connect(), and recv() socket requests. This creates a :class:`Timeout` - object that sets the individual timeouts to the ``timeout`` value - passed to this function. - - :param timeout: The legacy timeout value. - :type timeout: integer, float, :attr:`urllib3.util.Timeout.DEFAULT_TIMEOUT`, or None - :return: Timeout object - :rtype: :class:`Timeout` - """ - return Timeout(read=timeout, connect=timeout) - - def clone(self) -> Timeout: - """Create a copy of the timeout object - - Timeout properties are stored per-pool but each request needs a fresh - Timeout object to ensure each one has its own start/stop configured. - - :return: a copy of the timeout object - :rtype: :class:`Timeout` - """ - # We can't use copy.deepcopy because that will also create a new object - # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to - # detect the user default. - return Timeout(connect=self._connect, read=self._read, total=self.total) - - def start_connect(self) -> float: - """Start the timeout clock, used during a connect() attempt - - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to start a timer that has been started already. - """ - if self._start_connect is not None: - raise TimeoutStateError("Timeout timer has already been started.") - self._start_connect = time.monotonic() - return self._start_connect - - def get_connect_duration(self) -> float: - """Gets the time elapsed since the call to :meth:`start_connect`. - - :return: Elapsed time in seconds. - :rtype: float - :raises urllib3.exceptions.TimeoutStateError: if you attempt - to get duration for a timer that hasn't been started. - """ - if self._start_connect is None: - raise TimeoutStateError( - "Can't get connect duration for timer that has not started." - ) - return time.monotonic() - self._start_connect - - @property - def connect_timeout(self) -> _TYPE_TIMEOUT: - """Get the value to use when setting a connection timeout. - - This will be a positive float or integer, the value None - (never timeout), or the default system timeout. - - :return: Connect timeout. - :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None - """ - if self.total is None: - return self._connect - - if self._connect is None or self._connect is _DEFAULT_TIMEOUT: - return self.total - - return min(self._connect, self.total) # type: ignore[type-var] - - @property - def read_timeout(self) -> float | None: - """Get the value for the read timeout. - - This assumes some time has elapsed in the connection timeout and - computes the read timeout appropriately. - - If self.total is set, the read timeout is dependent on the amount of - time taken by the connect timeout. If the connection time has not been - established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be - raised. - - :return: Value to use for the read timeout. - :rtype: int, float or None - :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` - has not yet been called on this object. - """ - if ( - self.total is not None - and self.total is not _DEFAULT_TIMEOUT - and self._read is not None - and self._read is not _DEFAULT_TIMEOUT - ): - # In case the connect timeout has not yet been established. - if self._start_connect is None: - return self._read - return max(0, min(self.total - self.get_connect_duration(), self._read)) - elif self.total is not None and self.total is not _DEFAULT_TIMEOUT: - return max(0, self.total - self.get_connect_duration()) - else: - return self.resolve_default_timeout(self._read) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/url.py b/.venv/lib/python3.12/site-packages/urllib3/util/url.py deleted file mode 100644 index d53ea93..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/url.py +++ /dev/null @@ -1,471 +0,0 @@ -from __future__ import annotations - -import re -import typing - -from ..exceptions import LocationParseError -from .util import to_str - -# We only want to normalize urls with an HTTP(S) scheme. -# urllib3 infers URLs without a scheme (None) to be http. -_NORMALIZABLE_SCHEMES = ("http", "https", None) - -# Almost all of these patterns were derived from the -# 'rfc3986' module: https://github.com/python-hyper/rfc3986 -_PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") -_SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") -_URI_RE = re.compile( - r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" - r"(?://([^\\/?#]*))?" - r"([^?#]*)" - r"(?:\?([^#]*))?" - r"(?:#(.*))?$", - re.UNICODE | re.DOTALL, -) - -_IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" -_HEX_PAT = "[0-9A-Fa-f]{1,4}" -_LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=_HEX_PAT, ipv4=_IPV4_PAT) -_subs = {"hex": _HEX_PAT, "ls32": _LS32_PAT} -_variations = [ - # 6( h16 ":" ) ls32 - "(?:%(hex)s:){6}%(ls32)s", - # "::" 5( h16 ":" ) ls32 - "::(?:%(hex)s:){5}%(ls32)s", - # [ h16 ] "::" 4( h16 ":" ) ls32 - "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", - # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 - "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", - # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 - "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", - # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 - "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", - # [ *4( h16 ":" ) h16 ] "::" ls32 - "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", - # [ *5( h16 ":" ) h16 ] "::" h16 - "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", - # [ *6( h16 ":" ) h16 ] "::" - "(?:(?:%(hex)s:){0,6}%(hex)s)?::", -] - -_UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._\-~" -_IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" -_ZONE_ID_PAT = "(?:%25|%)(?:[" + _UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" -_IPV6_ADDRZ_PAT = r"\[" + _IPV6_PAT + r"(?:" + _ZONE_ID_PAT + r")?\]" -_REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*" -_TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$") - -_IPV4_RE = re.compile("^" + _IPV4_PAT + "$") -_IPV6_RE = re.compile("^" + _IPV6_PAT + "$") -_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT + "$") -_BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT[2:-2] + "$") -_ZONE_ID_RE = re.compile("(" + _ZONE_ID_PAT + r")\]$") - -_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % ( - _REG_NAME_PAT, - _IPV4_PAT, - _IPV6_ADDRZ_PAT, -) -_HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL) - -_UNRESERVED_CHARS = set( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" -) -_SUB_DELIM_CHARS = set("!$&'()*+,;=") -_USERINFO_CHARS = _UNRESERVED_CHARS | _SUB_DELIM_CHARS | {":"} -_PATH_CHARS = _USERINFO_CHARS | {"@", "/"} -_QUERY_CHARS = _FRAGMENT_CHARS = _PATH_CHARS | {"?"} - - -class Url( - typing.NamedTuple( - "Url", - [ - ("scheme", typing.Optional[str]), - ("auth", typing.Optional[str]), - ("host", typing.Optional[str]), - ("port", typing.Optional[int]), - ("path", typing.Optional[str]), - ("query", typing.Optional[str]), - ("fragment", typing.Optional[str]), - ], - ) -): - """ - Data structure for representing an HTTP URL. Used as a return value for - :func:`parse_url`. Both the scheme and host are normalized as they are - both case-insensitive according to RFC 3986. - """ - - def __new__( # type: ignore[no-untyped-def] - cls, - scheme: str | None = None, - auth: str | None = None, - host: str | None = None, - port: int | None = None, - path: str | None = None, - query: str | None = None, - fragment: str | None = None, - ): - if path and not path.startswith("/"): - path = "/" + path - if scheme is not None: - scheme = scheme.lower() - return super().__new__(cls, scheme, auth, host, port, path, query, fragment) - - @property - def hostname(self) -> str | None: - """For backwards-compatibility with urlparse. We're nice like that.""" - return self.host - - @property - def request_uri(self) -> str: - """Absolute path including the query string.""" - uri = self.path or "/" - - if self.query is not None: - uri += "?" + self.query - - return uri - - @property - def authority(self) -> str | None: - """ - Authority component as defined in RFC 3986 3.2. - This includes userinfo (auth), host and port. - - i.e. - userinfo@host:port - """ - userinfo = self.auth - netloc = self.netloc - if netloc is None or userinfo is None: - return netloc - else: - return f"{userinfo}@{netloc}" - - @property - def netloc(self) -> str | None: - """ - Network location including host and port. - - If you need the equivalent of urllib.parse's ``netloc``, - use the ``authority`` property instead. - """ - if self.host is None: - return None - if self.port: - return f"{self.host}:{self.port}" - return self.host - - @property - def url(self) -> str: - """ - Convert self into a url - - This function should more or less round-trip with :func:`.parse_url`. The - returned url may not be exactly the same as the url inputted to - :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls - with a blank port will have : removed). - - Example: - - .. code-block:: python - - import urllib3 - - U = urllib3.util.parse_url("https://google.com/mail/") - - print(U.url) - # "https://google.com/mail/" - - print( urllib3.util.Url("https", "username:password", - "host.com", 80, "/path", "query", "fragment" - ).url - ) - # "https://username:password@host.com:80/path?query#fragment" - """ - scheme, auth, host, port, path, query, fragment = self - url = "" - - # We use "is not None" we want things to happen with empty strings (or 0 port) - if scheme is not None: - url += scheme + "://" - if auth is not None: - url += auth + "@" - if host is not None: - url += host - if port is not None: - url += ":" + str(port) - if path is not None: - url += path - if query is not None: - url += "?" + query - if fragment is not None: - url += "#" + fragment - - return url - - def __str__(self) -> str: - return self.url - - -@typing.overload -def _encode_invalid_chars( - component: str, allowed_chars: typing.Container[str] -) -> str: # Abstract - ... - - -@typing.overload -def _encode_invalid_chars( - component: None, allowed_chars: typing.Container[str] -) -> None: # Abstract - ... - - -def _encode_invalid_chars( - component: str | None, allowed_chars: typing.Container[str] -) -> str | None: - """Percent-encodes a URI component without reapplying - onto an already percent-encoded component. - """ - if component is None: - return component - - component = to_str(component) - - # Normalize existing percent-encoded bytes. - # Try to see if the component we're encoding is already percent-encoded - # so we can skip all '%' characters but still encode all others. - component, percent_encodings = _PERCENT_RE.subn( - lambda match: match.group(0).upper(), component - ) - - uri_bytes = component.encode("utf-8", "surrogatepass") - is_percent_encoded = percent_encodings == uri_bytes.count(b"%") - encoded_component = bytearray() - - for i in range(0, len(uri_bytes)): - # Will return a single character bytestring - byte = uri_bytes[i : i + 1] - byte_ord = ord(byte) - if (is_percent_encoded and byte == b"%") or ( - byte_ord < 128 and byte.decode() in allowed_chars - ): - encoded_component += byte - continue - encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper())) - - return encoded_component.decode() - - -def _remove_path_dot_segments(path: str) -> str: - # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code - segments = path.split("/") # Turn the path into a list of segments - output = [] # Initialize the variable to use to store output - - for segment in segments: - # '.' is the current directory, so ignore it, it is superfluous - if segment == ".": - continue - # Anything other than '..', should be appended to the output - if segment != "..": - output.append(segment) - # In this case segment == '..', if we can, we should pop the last - # element - elif output: - output.pop() - - # If the path starts with '/' and the output is empty or the first string - # is non-empty - if path.startswith("/") and (not output or output[0]): - output.insert(0, "") - - # If the path starts with '/.' or '/..' ensure we add one more empty - # string to add a trailing '/' - if path.endswith(("/.", "/..")): - output.append("") - - return "/".join(output) - - -@typing.overload -def _normalize_host(host: None, scheme: str | None) -> None: - ... - - -@typing.overload -def _normalize_host(host: str, scheme: str | None) -> str: - ... - - -def _normalize_host(host: str | None, scheme: str | None) -> str | None: - if host: - if scheme in _NORMALIZABLE_SCHEMES: - is_ipv6 = _IPV6_ADDRZ_RE.match(host) - if is_ipv6: - # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as - # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID - # separator as necessary to return a valid RFC 4007 scoped IP. - match = _ZONE_ID_RE.search(host) - if match: - start, end = match.span(1) - zone_id = host[start:end] - - if zone_id.startswith("%25") and zone_id != "%25": - zone_id = zone_id[3:] - else: - zone_id = zone_id[1:] - zone_id = _encode_invalid_chars(zone_id, _UNRESERVED_CHARS) - return f"{host[:start].lower()}%{zone_id}{host[end:]}" - else: - return host.lower() - elif not _IPV4_RE.match(host): - return to_str( - b".".join([_idna_encode(label) for label in host.split(".")]), - "ascii", - ) - return host - - -def _idna_encode(name: str) -> bytes: - if not name.isascii(): - try: - import idna - except ImportError: - raise LocationParseError( - "Unable to parse URL without the 'idna' module" - ) from None - - try: - return idna.encode(name.lower(), strict=True, std3_rules=True) - except idna.IDNAError: - raise LocationParseError( - f"Name '{name}' is not a valid IDNA label" - ) from None - - return name.lower().encode("ascii") - - -def _encode_target(target: str) -> str: - """Percent-encodes a request target so that there are no invalid characters - - Pre-condition for this function is that 'target' must start with '/'. - If that is the case then _TARGET_RE will always produce a match. - """ - match = _TARGET_RE.match(target) - if not match: # Defensive: - raise LocationParseError(f"{target!r} is not a valid request URI") - - path, query = match.groups() - encoded_target = _encode_invalid_chars(path, _PATH_CHARS) - if query is not None: - query = _encode_invalid_chars(query, _QUERY_CHARS) - encoded_target += "?" + query - return encoded_target - - -def parse_url(url: str) -> Url: - """ - Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is - performed to parse incomplete urls. Fields not provided will be None. - This parser is RFC 3986 and RFC 6874 compliant. - - The parser logic and helper functions are based heavily on - work done in the ``rfc3986`` module. - - :param str url: URL to parse into a :class:`.Url` namedtuple. - - Partly backwards-compatible with :mod:`urllib.parse`. - - Example: - - .. code-block:: python - - import urllib3 - - print( urllib3.util.parse_url('http://google.com/mail/')) - # Url(scheme='http', host='google.com', port=None, path='/mail/', ...) - - print( urllib3.util.parse_url('google.com:80')) - # Url(scheme=None, host='google.com', port=80, path=None, ...) - - print( urllib3.util.parse_url('/foo?bar')) - # Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) - """ - if not url: - # Empty - return Url() - - source_url = url - if not _SCHEME_RE.search(url): - url = "//" + url - - scheme: str | None - authority: str | None - auth: str | None - host: str | None - port: str | None - port_int: int | None - path: str | None - query: str | None - fragment: str | None - - try: - scheme, authority, path, query, fragment = _URI_RE.match(url).groups() # type: ignore[union-attr] - normalize_uri = scheme is None or scheme.lower() in _NORMALIZABLE_SCHEMES - - if scheme: - scheme = scheme.lower() - - if authority: - auth, _, host_port = authority.rpartition("@") - auth = auth or None - host, port = _HOST_PORT_RE.match(host_port).groups() # type: ignore[union-attr] - if auth and normalize_uri: - auth = _encode_invalid_chars(auth, _USERINFO_CHARS) - if port == "": - port = None - else: - auth, host, port = None, None, None - - if port is not None: - port_int = int(port) - if not (0 <= port_int <= 65535): - raise LocationParseError(url) - else: - port_int = None - - host = _normalize_host(host, scheme) - - if normalize_uri and path: - path = _remove_path_dot_segments(path) - path = _encode_invalid_chars(path, _PATH_CHARS) - if normalize_uri and query: - query = _encode_invalid_chars(query, _QUERY_CHARS) - if normalize_uri and fragment: - fragment = _encode_invalid_chars(fragment, _FRAGMENT_CHARS) - - except (ValueError, AttributeError) as e: - raise LocationParseError(source_url) from e - - # For the sake of backwards compatibility we put empty - # string values for path if there are any defined values - # beyond the path in the URL. - # TODO: Remove this when we break backwards compatibility. - if not path: - if query is not None or fragment is not None: - path = "" - else: - path = None - - return Url( - scheme=scheme, - auth=auth, - host=host, - port=port_int, - path=path, - query=query, - fragment=fragment, - ) diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/util.py b/.venv/lib/python3.12/site-packages/urllib3/util/util.py deleted file mode 100644 index 35c77e4..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/util.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -import typing -from types import TracebackType - - -def to_bytes( - x: str | bytes, encoding: str | None = None, errors: str | None = None -) -> bytes: - if isinstance(x, bytes): - return x - elif not isinstance(x, str): - raise TypeError(f"not expecting type {type(x).__name__}") - if encoding or errors: - return x.encode(encoding or "utf-8", errors=errors or "strict") - return x.encode() - - -def to_str( - x: str | bytes, encoding: str | None = None, errors: str | None = None -) -> str: - if isinstance(x, str): - return x - elif not isinstance(x, bytes): - raise TypeError(f"not expecting type {type(x).__name__}") - if encoding or errors: - return x.decode(encoding or "utf-8", errors=errors or "strict") - return x.decode() - - -def reraise( - tp: type[BaseException] | None, - value: BaseException, - tb: TracebackType | None = None, -) -> typing.NoReturn: - try: - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None # type: ignore[assignment] - tb = None diff --git a/.venv/lib/python3.12/site-packages/urllib3/util/wait.py b/.venv/lib/python3.12/site-packages/urllib3/util/wait.py deleted file mode 100644 index aeca0c7..0000000 --- a/.venv/lib/python3.12/site-packages/urllib3/util/wait.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import annotations - -import select -import socket -from functools import partial - -__all__ = ["wait_for_read", "wait_for_write"] - - -# How should we wait on sockets? -# -# There are two types of APIs you can use for waiting on sockets: the fancy -# modern stateful APIs like epoll/kqueue, and the older stateless APIs like -# select/poll. The stateful APIs are more efficient when you have a lots of -# sockets to keep track of, because you can set them up once and then use them -# lots of times. But we only ever want to wait on a single socket at a time -# and don't want to keep track of state, so the stateless APIs are actually -# more efficient. So we want to use select() or poll(). -# -# Now, how do we choose between select() and poll()? On traditional Unixes, -# select() has a strange calling convention that makes it slow, or fail -# altogether, for high-numbered file descriptors. The point of poll() is to fix -# that, so on Unixes, we prefer poll(). -# -# On Windows, there is no poll() (or at least Python doesn't provide a wrapper -# for it), but that's OK, because on Windows, select() doesn't have this -# strange calling convention; plain select() works fine. -# -# So: on Windows we use select(), and everywhere else we use poll(). We also -# fall back to select() in case poll() is somehow broken or missing. - - -def select_wait_for_socket( - sock: socket.socket, - read: bool = False, - write: bool = False, - timeout: float | None = None, -) -> bool: - if not read and not write: - raise RuntimeError("must specify at least one of read=True, write=True") - rcheck = [] - wcheck = [] - if read: - rcheck.append(sock) - if write: - wcheck.append(sock) - # When doing a non-blocking connect, most systems signal success by - # marking the socket writable. Windows, though, signals success by marked - # it as "exceptional". We paper over the difference by checking the write - # sockets for both conditions. (The stdlib selectors module does the same - # thing.) - fn = partial(select.select, rcheck, wcheck, wcheck) - rready, wready, xready = fn(timeout) - return bool(rready or wready or xready) - - -def poll_wait_for_socket( - sock: socket.socket, - read: bool = False, - write: bool = False, - timeout: float | None = None, -) -> bool: - if not read and not write: - raise RuntimeError("must specify at least one of read=True, write=True") - mask = 0 - if read: - mask |= select.POLLIN - if write: - mask |= select.POLLOUT - poll_obj = select.poll() - poll_obj.register(sock, mask) - - # For some reason, poll() takes timeout in milliseconds - def do_poll(t: float | None) -> list[tuple[int, int]]: - if t is not None: - t *= 1000 - return poll_obj.poll(t) - - return bool(do_poll(timeout)) - - -def _have_working_poll() -> bool: - # Apparently some systems have a select.poll that fails as soon as you try - # to use it, either due to strange configuration or broken monkeypatching - # from libraries like eventlet/greenlet. - try: - poll_obj = select.poll() - poll_obj.poll(0) - except (AttributeError, OSError): - return False - else: - return True - - -def wait_for_socket( - sock: socket.socket, - read: bool = False, - write: bool = False, - timeout: float | None = None, -) -> bool: - # We delay choosing which implementation to use until the first time we're - # called. We could do it at import time, but then we might make the wrong - # decision if someone goes wild with monkeypatching select.poll after - # we're imported. - global wait_for_socket - if _have_working_poll(): - wait_for_socket = poll_wait_for_socket - elif hasattr(select, "select"): - wait_for_socket = select_wait_for_socket - return wait_for_socket(sock, read, write, timeout) - - -def wait_for_read(sock: socket.socket, timeout: float | None = None) -> bool: - """Waits for reading to be available on a given socket. - Returns True if the socket is readable, or False if the timeout expired. - """ - return wait_for_socket(sock, read=True, timeout=timeout) - - -def wait_for_write(sock: socket.socket, timeout: float | None = None) -> bool: - """Waits for writing to be available on a given socket. - Returns True if the socket is readable, or False if the timeout expired. - """ - return wait_for_socket(sock, write=True, timeout=timeout) diff --git a/.venv/pyvenv.cfg b/.venv/pyvenv.cfg index 3e51560..d9adafd 100644 --- a/.venv/pyvenv.cfg +++ b/.venv/pyvenv.cfg @@ -1,5 +1,5 @@ -home = /bin +home = /usr/bin include-system-site-packages = false version = 3.12.3 executable = /usr/bin/python3.12 -command = /bin/python -m venv /home/rpetrovich/code/Misc/.venv +command = /usr/bin/python -m venv /home/rpetrovich/code/miscellaneous/.venv diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..70ae8fa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "github-enterprise.uri": "https://github.lmru.tech/" +} \ No newline at end of file diff --git a/blocked_ips.txt b/blocked_ips.txt new file mode 100644 index 0000000..29acab9 --- /dev/null +++ b/blocked_ips.txt @@ -0,0 +1,280 @@ +104.110.240.106 repository.jboss.org +104.110.240.130 repository.jboss.org +104.16.0.35 registry.yarnpkg.com +104.16.0.35 registry.npmjs.org +104.16.1.35 registry.yarnpkg.com +104.16.1.35 registry.npmjs.org +104.16.168.120 dl.yarnpkg.com +104.16.169.120 dl.yarnpkg.com +104.16.2.35 registry.npmjs.org +104.16.2.35 registry.yarnpkg.com +104.16.24.34 registry.yarnpkg.com +104.16.24.34 registry.npmjs.org +104.16.25.34 registry.npmjs.org +104.16.25.34 registry.yarnpkg.com +104.16.26.34 registry.npmjs.org +104.16.26.34 registry.yarnpkg.com +104.16.27.34 registry.yarnpkg.com +104.16.27.34 registry.npmjs.org +104.16.28.34 registry.yarnpkg.com +104.16.28.34 registry.npmjs.org +104.16.29.34 registry.npmjs.org +104.16.29.34 registry.yarnpkg.com +104.16.3.35 registry.yarnpkg.com +104.16.3.35 registry.npmjs.org +104.16.30.34 registry.yarnpkg.com +104.16.30.34 registry.npmjs.org +104.16.31.34 registry.yarnpkg.com +104.16.31.34 registry.npmjs.org +104.16.72.101 services.gradle.org +104.16.72.101 plugins.gradle.org +104.16.73.101 services.gradle.org +104.16.73.101 plugins.gradle.org +104.21.13.85 node-precompiled-binaries.grpc.io +104.21.80.4 cdn.sheetjs.com +107.21.12.206 quay.io +107.22.21.208 oss.sonatype.org +108.138.26.102 hdd48109.live.dynatrace.com +108.138.26.102 wtk16575.live.dynatrace.com +108.138.26.109 hdd48109.live.dynatrace.com +108.138.26.109 wtk16575.live.dynatrace.com +108.138.26.11 hdd48109.live.dynatrace.com +108.138.26.11 wtk16575.live.dynatrace.com +108.138.26.123 hdd48109.live.dynatrace.com +108.138.26.123 wtk16575.live.dynatrace.com +108.156.22.103 binaries.sonarsource.com +108.156.22.35 binaries.sonarsource.com +108.156.22.43 binaries.sonarsource.com +108.156.22.52 binaries.sonarsource.com +108.157.229.17 helm.releases.hashicorp.com +108.157.229.3 helm.releases.hashicorp.com +108.157.229.36 helm.releases.hashicorp.com +108.157.229.86 helm.releases.hashicorp.com +116.203.44.5 registry-gitlab.smarthead.ru +13.107.246.44 www.powershellgallery.com +13.107.246.67 www.powershellgallery.com +13.107.246.67 packages.microsoft.com +13.107.253.72 repo.adobe.com +13.33.187.52 releases.hashicorp.com +13.33.187.85 releases.hashicorp.com +13.33.187.87 releases.hashicorp.com +13.33.187.98 releases.hashicorp.com +13.90.137.153 dist.apache.org +140.82.121.3 github.com +140.82.121.33 ghcr.io +142.250.150.82 gcr.io +146.59.69.202 packages.sury.org +146.75.120.209 repo1.maven.org +146.75.54.132 deb.debian.org +147.75.85.69 apt.postgresql.org +150.171.69.10 mcr.microsoft.com +150.171.70.10 mcr.microsoft.com +151.101.1.227 rubygems.org +151.101.129.227 rubygems.org +151.101.130.132 security.debian.org +151.101.193.227 rubygems.org +151.101.194.132 security.debian.org +151.101.2.132 dlcdn.apache.org +151.101.2.132 security.debian.org +151.101.65.227 rubygems.org +151.101.66.132 security.debian.org +151.101.84.223 files.pythonhosted.org +151.101.84.223 pypi.python.org +157.90.160.34 dl.qameta.io +158.160.130.222 art.platformeco.cloud +172.67.132.164 node-precompiled-binaries.grpc.io +172.67.172.85 cdn.sheetjs.com +173.194.221.82 eu.gcr.io +173.194.221.82 k8s.gcr.io +178.128.6.101 repo.zabbix.com +18.165.140.100 hdd48109.live.dynatrace.com +18.165.140.100 wtk16575.live.dynatrace.com +18.165.140.122 hdd48109.live.dynatrace.com +18.165.140.122 wtk16575.live.dynatrace.com +18.165.140.2 hdd48109.live.dynatrace.com +18.165.140.2 wtk16575.live.dynatrace.com +18.165.140.61 hdd48109.live.dynatrace.com +18.165.140.61 wtk16575.live.dynatrace.com +18.172.112.106 fastdl.mongodb.org +18.172.112.108 fastdl.mongodb.org +18.172.112.122 fastdl.mongodb.org +18.172.112.53 fastdl.mongodb.org +18.193.107.15 app.camunda.com +18.198.112.44 plus-pkgs.nginx.com +18.198.212.80 plus-pkgs.nginx.com +18.214.194.113 jcenter.bintray.com +18.214.194.113 dl.bintray.com +18.232.172.199 jcenter.bintray.com +18.232.172.199 dl.bintray.com +185.12.28.11 hub.verme.ru +185.125.190.81 archive.ubuntu.com +185.125.190.81 security.ubuntu.com +185.125.190.82 archive.ubuntu.com +185.125.190.82 security.ubuntu.com +185.125.190.83 archive.ubuntu.com +185.125.190.83 security.ubuntu.com +185.199.108.133 raw.githubusercontent.com +185.199.108.153 helm.tricksterproxy.io +185.199.108.153 provectus.github.io +185.199.108.153 victoriametrics.github.io +185.199.108.153 ansible-community.github.io +185.199.108.153 grafana.github.io +185.199.108.153 prometheus-community.github.io +185.199.108.153 open-telemetry.github.io +185.199.109.133 raw.githubusercontent.com +185.199.109.153 provectus.github.io +185.199.109.153 victoriametrics.github.io +185.199.109.153 helm.tricksterproxy.io +185.199.109.153 ansible-community.github.io +185.199.109.153 grafana.github.io +185.199.109.153 prometheus-community.github.io +185.199.109.153 open-telemetry.github.io +185.199.110.133 raw.githubusercontent.com +185.199.110.153 prometheus-community.github.io +185.199.110.153 helm.tricksterproxy.io +185.199.110.153 provectus.github.io +185.199.110.153 victoriametrics.github.io +185.199.110.153 ansible-community.github.io +185.199.110.153 open-telemetry.github.io +185.199.110.153 grafana.github.io +185.199.111.133 raw.githubusercontent.com +185.199.111.153 helm.tricksterproxy.io +185.199.111.153 provectus.github.io +185.199.111.153 victoriametrics.github.io +185.199.111.153 ansible-community.github.io +185.199.111.153 open-telemetry.github.io +185.199.111.153 grafana.github.io +185.199.111.153 prometheus-community.github.io +188.114.96.1 node-precompiled-binaries.grpc.io +188.114.97.1 node-precompiled-binaries.grpc.io +188.114.98.229 jitpack.io +188.114.98.229 charts.jetstack.io +188.114.98.229 api.cypress.io +188.114.98.233 api.cypress.io +188.114.99.229 jitpack.io +188.114.99.229 charts.jetstack.io +188.114.99.229 api.cypress.io +188.114.99.233 api.cypress.io +199.232.192.209 repo1.maven.org +199.232.196.209 repo1.maven.org +2.19.204.49 developer.huawei.com +2.19.204.85 developer.huawei.com +213.180.204.183 mirror.yandex.ru +216.239.32.21 go.dev +216.239.34.21 go.dev +216.239.36.21 go.dev +216.239.38.21 go.dev +216.58.210.142 maven.google.com +217.196.149.55 apt.postgresql.org +3.123.220.237 adeo-docker.jfrog.io +3.123.220.237 adeo-docker-adeo-habitation-social-platform-community-release.jfrog.io +3.123.220.237 adeo-docker-public.jfrog.io +3.123.220.237 adeo.jfrog.io +3.123.220.237 adeo-docker-adeo-supplier-portal-dev.jfrog.io +3.123.220.237 artifacts.camunda.com +3.164.206.102 download.docker.com +3.164.206.39 download.docker.com +3.164.206.5 download.docker.com +3.164.206.93 download.docker.com +3.164.230.119 packages.confluent.io +3.164.230.21 packages.confluent.io +3.164.230.3 packages.confluent.io +3.164.230.95 packages.confluent.io +3.164.68.105 fastdl.mongodb.org +3.164.68.120 fastdl.mongodb.org +3.164.68.120 packages.atlassian.com +3.164.68.125 packages.atlassian.com +3.164.68.13 charts.bitnami.com +3.164.68.18 charts.bitnami.com +3.164.68.34 fastdl.mongodb.org +3.164.68.49 fastdl.mongodb.org +3.164.68.51 charts.bitnami.com +3.164.68.55 packages.atlassian.com +3.164.68.60 packages.atlassian.com +3.164.68.70 charts.bitnami.com +3.219.239.5 registry-1.docker.io +3.221.179.99 releases.jfrog.io +3.221.179.99 docker.bintray.io +3.224.227.198 docker.io +3.64.156.118 adeo.jfrog.io +3.64.156.118 adeo-docker.jfrog.io +3.64.156.118 adeo-docker-adeo-habitation-social-platform-community-release.jfrog.io +3.64.156.118 adeo-docker-public.jfrog.io +3.64.156.118 adeo-docker-adeo-supplier-portal-dev.jfrog.io +3.64.156.118 artifacts.camunda.com +3.95.117.170 dl.bintray.com +3.95.117.170 jcenter.bintray.com +34.120.127.130 helm.elastic.co +34.120.127.130 artifacts.elastic.co +34.120.251.192 maven.fabric.io +34.194.247.182 quay.io +34.204.25.116 quay.io +34.225.228.28 repo.spring.io +34.225.228.28 repo.jenkins-ci.org +34.225.228.28 jaspersoft.jfrog.io +34.226.69.105 registry-1.docker.io +34.234.119.86 registry.aquasec.com +34.236.131.237 quay.io +34.68.230.202 docker.elastic.co +34.96.108.209 registry.k8s.io +44.193.181.103 docker.io +44.199.98.27 registry.aquasec.com +44.209.124.32 jaspersoft.jfrog.io +44.209.124.32 repo.spring.io +44.209.124.32 repo.jenkins-ci.org +44.217.227.31 oss.sonatype.org +44.219.3.189 docker.io +46.148.230.218 registry.deckhouse.io +46.148.230.218 tuf.trdl.dev +46.148.230.218 tuf.werf.io +5.182.227.150 registry.flant.com +50.17.28.229 releases.jfrog.io +50.17.28.229 docker.bintray.io +51.250.108.143 reg.qatools.ru +51.250.42.133 nexus.uxfeedback.ru +52.22.177.50 jaspersoft.jfrog.io +52.22.177.50 repo.jenkins-ci.org +52.22.177.50 repo.spring.io +52.240.159.111 www.nuget.org +52.52.69.196 packagecloud.io +52.59.103.36 adeo-docker.jfrog.io +52.59.103.36 adeo-docker-adeo-habitation-social-platform-community-release.jfrog.io +52.59.103.36 adeo-docker-public.jfrog.io +52.59.103.36 adeo.jfrog.io +52.59.103.36 adeo-docker-adeo-supplier-portal-dev.jfrog.io +52.59.103.36 artifacts.camunda.com +52.85.49.108 packages.confluent.io +52.85.49.128 packages.confluent.io +52.85.49.20 packages.confluent.io +52.85.49.85 packages.confluent.io +52.86.230.159 registry.aquasec.com +54.183.9.100 airbyte.mycloudrepo.io +54.196.99.49 registry-1.docker.io +54.205.219.235 quay.io +54.219.142.32 airbyte.mycloudrepo.io +54.219.193.67 packagecloud.io +54.221.82.220 releases.jfrog.io +54.221.82.220 docker.bintray.io +54.85.190.97 quay.io +62.84.127.94 hub.elma365.tech +64.233.164.141 proxy.golang.org +64.233.165.82 eu.gcr.io +65.109.119.155 repository.apache.org +72.32.157.246 apt.postgresql.org +77.223.118.150 registry.sbdagroup.com +77.223.120.232 registry.deckhouse.io +77.223.120.232 tuf.trdl.dev +77.223.120.232 tuf.werf.io +82.202.247.250 gitlab-registry-leroy.agora.ru +84.201.144.250 registry.infra.platformeco.tech +84.201.171.239 cr.yandex +84.201.185.129 hashicorp-releases.yandexcloud.net +87.238.57.227 apt.postgresql.org +91.134.164.156 maven.jahia.org +91.189.91.81 security.ubuntu.com +91.189.91.81 archive.ubuntu.com +91.189.91.82 security.ubuntu.com +91.189.91.82 archive.ubuntu.com +95.213.31.200 artifactory-external.vkpartner.ru +96.16.54.184 repository.jboss.org +96.16.54.200 repository.jboss.org diff --git a/ng_labels_with_aqua.py b/ng_labels_with_aqua.py new file mode 100644 index 0000000..e6d3390 --- /dev/null +++ b/ng_labels_with_aqua.py @@ -0,0 +1,186 @@ +from ruamel.yaml import YAML, comments +from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq +import os + + +base_dir = "/home/rpetrovich/code/Repos/lmru--devops--argocd-apps/" + +yaml = YAML() +yaml.preserve_quotes = True +yaml.explicit_start = True +yaml.indent(mapping=2, sequence=4, offset=2) +yaml.allow_duplicate_keys = True + +def load_yaml(file_path): + with open(file_path, 'r') as file: + data = list(yaml.load_all(file)) + return data + +def save_yaml(data, file_path): + with open(file_path, 'w') as file: + yaml.dump_all(data, file) + +def extract_cluster_info(node_path): + parts = node_path.split('/') + nodes_index = parts.index('nodes') + env = parts[nodes_index - 2] + cluster_index = parts.index('clusters') + 1 + cluster_name = parts[cluster_index] + subpath = '-'.join(parts[cluster_index + 2:nodes_index - 1]) if nodes_index - cluster_index > 2 else '' + return cluster_name, env, subpath + +def add_label_if_needed(node_group): + excluded_labels = [ + "d8-", "opendistro", "opensearch", "otel-", "system", + "mimir-", "prometheus", "frontend", "elastic", "master", + "loadbalancer", "deckhouse", "router", "moon-", "sentry", + "odfe-", "obs-grafana", "devops-core-argocd", "victoria-", + "clickhouse", "redis", "rabbit", "loki","cassandra", "jaeger", "vm-", + "memcached", "kafka" + ] + + name = node_group['metadata']['name'] + print(f"Processing NodeGroup: {name}") + + if any(excluded in name for excluded in excluded_labels): + print(f"Excluded by name: {name}") + return False # Пропускаем обработку, если имя в списке исключений + + node_template = node_group['spec'].setdefault('nodeTemplate', comments.CommentedMap()) + labels = node_template.setdefault('labels', comments.CommentedMap()) + filtered_labels = {k: v for k, v in labels.items() if not k.startswith("threshold.")} + + if any(excluded in label for label in filtered_labels for excluded in excluded_labels): + print(f"Excluded by existing labels: {filtered_labels}") + return False # Пропускаем обработку, если метки в списке исключений + + new_label_key = 'node-role.k8s.lmru.tech/application' + + # Добавляем логику: проверяем метку, но продолжаем проверку тейнтов + label_already_present = new_label_key in labels + if label_already_present: + print(f"Label already present for {name}, but checking taints...") + else: + labels[new_label_key] = "" + print(f"Label added for {name}: {new_label_key}") + + # Возвращаем True для продолжения обработки, даже если метка уже есть + return True + +def find_cluster_file(base_dir, cluster_name, env): + target_directory = f"{base_dir}/common/aqua-kube-enforcer/{env}/values/" + + if not os.path.exists(target_directory): + print(f"Directory does not exist: {target_directory}") + return None + + for file in os.listdir(target_directory): + if cluster_name in file and env in file and file.endswith('cluster.yaml') and '-elk-' not in file: + return os.path.join(target_directory, file) + + print(f"No matching cluster file found that contains both '{cluster_name}' and '{env}' in {target_directory}") + return None + +def process_yaml_file(node_file): + if '/elk/' in node_file: + print(f"Skipping: {node_file} due to '/elk/'") + return + + cluster_name, env, _ = extract_cluster_info(node_file) + data = load_yaml(node_file) + + if not data: + print(f"No data found in {node_file}") + return + + changed = False + tolerations = [] + + for doc in data: + if doc is None: + print(f"Skipping invalid or empty YAML document in {node_file}") + continue + + # Проверяем метки и продолжаем даже если метка уже есть + if doc.get('kind') == "NodeGroup" and add_label_if_needed(doc): + changed = True + taints = doc.get('spec', {}).get('nodeTemplate', {}).get('taints', []) + if taints: + print(f"Taints found in NodeGroup {doc['metadata']['name']}: {taints}") + for taint in taints: + if 'key' in taint and 'value' in taint: + tolerations.append({ + "key": taint['key'], + "operator": "Equal", + "effect": dq(taint.get('effect', '')), + "value": taint['value'] + }) + print(f"Adding toleration for {doc['metadata']['name']}: {taint}") + + if changed: + save_yaml(data, node_file) + print(f"Changes saved to {node_file}") + + if tolerations: + cluster_file = find_cluster_file(base_dir, cluster_name, env) + if cluster_file: + print(f"Found cluster file for {cluster_name} in {env}: {cluster_file}") + update_cluster_file(cluster_file, tolerations) + else: + print(f"No cluster file found for {cluster_name} in {env}") + else: + print(f"No tolerations found to add for {node_file}") + + +def update_cluster_file(cluster_file, new_tolerations): + if not cluster_file: + print("No matching cluster file found.") + return + + print(f"Attempting to update cluster file: {cluster_file} with tolerations: {new_tolerations}") + + try: + data = load_yaml(cluster_file) + updated = False # Флаг для отслеживания изменений + + for doc in data: + # Убедимся, что раздел enforcer присутствует + enforcer = doc.setdefault('enforcer', comments.CommentedMap()) + + # Добавляем nodeSelector, если отсутствует + enforcer.setdefault('nodeSelector', {})['node-role.k8s.lmru.tech/application'] = '' + + # Проверяем и добавляем новые tolerations + existing_tolerations = enforcer.setdefault('tolerations', []) + for new_tol in new_tolerations: + # Проверяем, если такая же toleration уже присутствует + if not any( + tol['key'] == new_tol['key'] and + tol['operator'] == new_tol['operator'] and + tol['effect'] == new_tol['effect'] and + tol['value'] == new_tol['value'] + for tol in existing_tolerations + ): + existing_tolerations.append(new_tol) + updated = True # Устанавливаем флаг, что произошли изменения + + if updated: + save_yaml(data, cluster_file) + print(f"Updated cluster file successfully: {cluster_file}") + else: + print(f"No updates were needed for {cluster_file}") + + except Exception as e: + print(f"Failed to update cluster file {cluster_file}: {e}") + +def process_directory(directory): + for root, _, files in os.walk(directory, topdown=True): + if "stage" not in root: # comment if you don't want to restrict "stage" only + continue + for file in files: + if file == 'node_groups.yaml': + full_path = os.path.join(root, file) + print(f"Processing: {full_path}") + process_yaml_file(full_path) + +process_directory(base_dir) diff --git a/salt/init.sls b/salt/init.sls index 7aec692..eff98dd 100644 --- a/salt/init.sls +++ b/salt/init.sls @@ -1,44 +1,20 @@ -{% for ip in [ - "54.67.28.63", - "13.56.23.197", - "213.180.204.183", - "151.101.130.132", - "151.101.194.132", - "151.101.2.132", - "151.101.246.132", - "151.101.66.132", - "147.75.85.69", - "217.196.149.55", - "72.32.157.246", - "87.238.57.227", - "104.16.168.120", - "104.16.169.120", - "52.85.49.108", - "52.85.49.128", - "52.85.49.20", - "52.85.49.85", - "18.198.112.44", - "18.198.212.80", - "146.59.69.202" -] %} -remove_ip_{{ ip.replace('.', '_') }}_output: +remove_ip_64.233.162.82_output: iptables.delete: - table: filter - chain: OUTPUT - - destination: {{ ip }} + - destination: 64.233.162.82 - jump: REJECT -remove_ip_{{ ip.replace('.', '_') }}_forward: +remove_ip_64.233.162.82_forward: iptables.delete: - table: filter - chain: FORWARD - - destination: {{ ip }} + - destination: 64.233.162.82 - jump: REJECT -remove_ip_{{ ip.replace('.', '_') }}_docker_user: +remove_ip_64.233.162.82_docker_user: iptables.delete: - table: filter - chain: DOCKER-USER - - destination: {{ ip }} + - destination: 64.233.162.82 - jump: REJECT -{% endfor %} \ No newline at end of file