From c125c4c81bbadd6606e6f8f5614cd51fedcdb446 Mon Sep 17 00:00:00 2001 From: 81123 Dolev Dror Date: Wed, 15 Oct 2025 19:56:35 -0700 Subject: [PATCH 1/3] ahsahhaha --- main.py | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..7704d87 --- /dev/null +++ b/main.py @@ -0,0 +1,63 @@ +#If you are reading this you know what this does (Alision forced me to write comments) +import json + +#Load tim data json file +with open("example_tim_data.json", 'r') as tim_json: + tim_data = json.load(tim_json) + +#Class for team data +class team_data: + #Initilizes team number and other variables + def __init__(self, team_number) -> None: + self.team_number = team_number + self.match_numbers = [] + self.times_climbed = 0 + self.balls_per_match = [] + + #Adds data for a tim to the total team data + def add_data(self, match_num, climbed, num_balls) -> None: + self.match_numbers.append(match_num) + self.times_climbed += (1 * climbed) + self.balls_per_match.append(num_balls) + + #Calculates output team data + def calculate_team_data(self) -> dict: + number_matches_played = len(self.match_numbers) + average_balls_scored = sum(self.balls_per_match)/number_matches_played + least_balls_scored = min(self.balls_per_match) + most_balls_scored = max(self.balls_per_match) + percent_climb_success = self.times_climbed/number_matches_played + + #Creates and returns dict with output team data + data = { + 'team_number': self.team_number, + 'average_balls_scored': average_balls_scored, + 'least_balls_scored': least_balls_scored, + 'most_balls_scored': most_balls_scored, + 'number_matches_played': number_matches_played, + 'percent_climb_success': percent_climb_success + } + + return data + +#Initilizes variable to hold all teams data +loaded_teams = {} + +#Loops through tim data +for tim in tim_data: + #Checks if team is already in team data. If not it makes a new team_data object to hold it + if tim['team_num'] not in loaded_teams.keys(): + loaded_teams[tim['team_num']] = team_data(tim['team_num']) + + #Adds tim data to team data + loaded_teams[tim['team_num']].add_data(tim['match_num'], tim['climbed'], tim['num_balls']) + +#Initilizes variable for calculated team data +calculated_teams_data = [] + +#Calculates team data and adds it all to culculated_teams_data +for team in loaded_teams.values(): + calculated_teams_data.append(team.calculate_team_data()) + +#No MongoDB so just prints out the data +print(calculated_teams_data) \ No newline at end of file From 7b6337ab96df1421d49f1d074584c6637e574dd2 Mon Sep 17 00:00:00 2001 From: 81123 Dolev Dror Date: Thu, 16 Oct 2025 19:46:13 -0700 Subject: [PATCH 2/3] Add schema --- main.py | 29 +++++++++++++++++++++++++---- tim_data_schema.yaml | 4 ++++ 2 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 tim_data_schema.yaml diff --git a/main.py b/main.py index 7704d87..b11caa6 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,27 @@ #If you are reading this you know what this does (Alision forced me to write comments) import json +import yaml #Load tim data json file with open("example_tim_data.json", 'r') as tim_json: tim_data = json.load(tim_json) +#Load schema yaml file +with open("tim_data_schema.yaml", "r") as tim_schema_yaml: + tim_schema = yaml.load(tim_schema_yaml, yaml.Loader) + +class_types = { + 'int': int, + 'float': float, + 'str': str, + 'bool': bool, + 'set': set, + 'list': list, + 'dict': dict +} + #Class for team data -class team_data: +class TeamData(): #Initilizes team number and other variables def __init__(self, team_number) -> None: self.team_number = team_number @@ -23,10 +38,10 @@ def add_data(self, match_num, climbed, num_balls) -> None: #Calculates output team data def calculate_team_data(self) -> dict: number_matches_played = len(self.match_numbers) - average_balls_scored = sum(self.balls_per_match)/number_matches_played + average_balls_scored = sum(self.balls_per_match) / number_matches_played least_balls_scored = min(self.balls_per_match) most_balls_scored = max(self.balls_per_match) - percent_climb_success = self.times_climbed/number_matches_played + percent_climb_success = self.times_climbed / number_matches_played #Creates and returns dict with output team data data = { @@ -45,9 +60,15 @@ def calculate_team_data(self) -> dict: #Loops through tim data for tim in tim_data: + #Validate tim + for data in tim: + if type(tim[data]) != class_types[tim_schema[data]]: + print("Tim data is invalid") + exit() + #Checks if team is already in team data. If not it makes a new team_data object to hold it if tim['team_num'] not in loaded_teams.keys(): - loaded_teams[tim['team_num']] = team_data(tim['team_num']) + loaded_teams[tim['team_num']] = TeamData(tim['team_num']) #Adds tim data to team data loaded_teams[tim['team_num']].add_data(tim['match_num'], tim['climbed'], tim['num_balls']) diff --git a/tim_data_schema.yaml b/tim_data_schema.yaml new file mode 100644 index 0000000..378eacf --- /dev/null +++ b/tim_data_schema.yaml @@ -0,0 +1,4 @@ +team_num: int +match_num: int +climbed: bool +num_balls: int \ No newline at end of file From 58078ed07f8e6a7819c747ce6975f84fd8913994 Mon Sep 17 00:00:00 2001 From: DolevD Date: Wed, 29 Oct 2025 19:39:45 -0700 Subject: [PATCH 3/3] Add mongodb --- .venv/bin/Activate.ps1 | 247 + .venv/bin/activate | 70 + .venv/bin/activate.csh | 27 + .venv/bin/activate.fish | 69 + .venv/bin/pip | 8 + .venv/bin/pip3 | 8 + .venv/bin/pip3.12 | 8 + .venv/bin/python | 1 + .venv/bin/python3 | 1 + .venv/bin/python3.12 | 1 + .../site-packages/_yaml/__init__.py | 33 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 881 bytes .../python3.12/site-packages/bson/__init__.py | 1484 +++ .../bson/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 59646 bytes .../bson/__pycache__/_helpers.cpython-312.pyc | Bin 0 -> 1501 bytes .../bson/__pycache__/binary.cpython-312.pyc | Bin 0 -> 19272 bytes .../bson/__pycache__/code.cpython-312.pyc | Bin 0 -> 4074 bytes .../__pycache__/codec_options.cpython-312.pyc | Bin 0 -> 22805 bytes .../__pycache__/datetime_ms.cpython-312.pyc | Bin 0 -> 9251 bytes .../bson/__pycache__/dbref.cpython-312.pyc | Bin 0 -> 6489 bytes .../__pycache__/decimal128.cpython-312.pyc | Bin 0 -> 14816 bytes .../bson/__pycache__/errors.cpython-312.pyc | Bin 0 -> 1417 bytes .../bson/__pycache__/int64.cpython-312.pyc | Bin 0 -> 1199 bytes .../__pycache__/json_util.cpython-312.pyc | Bin 0 -> 46525 bytes .../bson/__pycache__/max_key.cpython-312.pyc | Bin 0 -> 2167 bytes .../bson/__pycache__/min_key.cpython-312.pyc | Bin 0 -> 2167 bytes .../bson/__pycache__/objectid.cpython-312.pyc | Bin 0 -> 11636 bytes .../bson/__pycache__/raw_bson.cpython-312.pyc | Bin 0 -> 8554 bytes .../bson/__pycache__/regex.cpython-312.pyc | Bin 0 -> 5650 bytes .../bson/__pycache__/son.cpython-312.pyc | Bin 0 -> 9453 bytes .../__pycache__/timestamp.cpython-312.pyc | Bin 0 -> 5928 bytes .../bson/__pycache__/typings.cpython-312.pyc | Bin 0 -> 877 bytes .../bson/__pycache__/tz_util.cpython-312.pyc | Bin 0 -> 2510 bytes .../_cbson.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 336696 bytes .../_cbson.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 340448 bytes .../_cbson.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 371640 bytes .../_cbson.cpython-39-x86_64-linux-gnu.so | Bin 0 -> 334600 bytes .../site-packages/bson/_cbsonmodule.c | 3276 +++++++ .../site-packages/bson/_cbsonmodule.h | 181 + .../python3.12/site-packages/bson/_helpers.py | 43 + .../python3.12/site-packages/bson/binary.py | 577 ++ .../site-packages/bson/bson-endian.h | 233 + .../python3.12/site-packages/bson/buffer.c | 157 + .../python3.12/site-packages/bson/buffer.h | 51 + .../lib/python3.12/site-packages/bson/code.py | 100 + .../site-packages/bson/codec_options.py | 521 + .../site-packages/bson/datetime_ms.py | 182 + .../python3.12/site-packages/bson/dbref.py | 133 + .../site-packages/bson/decimal128.py | 351 + .../python3.12/site-packages/bson/errors.py | 36 + .../python3.12/site-packages/bson/int64.py | 39 + .../site-packages/bson/json_util.py | 1164 +++ .../python3.12/site-packages/bson/max_key.py | 56 + .../python3.12/site-packages/bson/min_key.py | 56 + .../python3.12/site-packages/bson/objectid.py | 274 + .../python3.12/site-packages/bson/py.typed | 2 + .../python3.12/site-packages/bson/raw_bson.py | 200 + .../python3.12/site-packages/bson/regex.py | 133 + .../lib/python3.12/site-packages/bson/son.py | 211 + .../python3.12/site-packages/bson/time64.c | 781 ++ .../python3.12/site-packages/bson/time64.h | 67 + .../site-packages/bson/time64_config.h | 78 + .../site-packages/bson/time64_limits.h | 95 + .../site-packages/bson/timestamp.py | 123 + .../python3.12/site-packages/bson/typings.py | 31 + .../python3.12/site-packages/bson/tz_util.py | 56 + .../python3.12/site-packages/dns/__init__.py | 72 + .../dns/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 757 bytes .../__pycache__/_asyncbackend.cpython-312.pyc | Bin 0 -> 4862 bytes .../_asyncio_backend.cpython-312.pyc | Bin 0 -> 14368 bytes .../dns/__pycache__/_ddr.cpython-312.pyc | Bin 0 -> 7856 bytes .../dns/__pycache__/_features.cpython-312.pyc | Bin 0 -> 3326 bytes .../_immutable_ctx.cpython-312.pyc | Bin 0 -> 3252 bytes .../dns/__pycache__/_no_ssl.cpython-312.pyc | Bin 0 -> 3173 bytes .../dns/__pycache__/_tls_util.cpython-312.pyc | Bin 0 -> 910 bytes .../__pycache__/_trio_backend.cpython-312.pyc | Bin 0 -> 13642 bytes .../__pycache__/asyncbackend.cpython-312.pyc | Bin 0 -> 3435 bytes .../__pycache__/asyncquery.cpython-312.pyc | Bin 0 -> 38614 bytes .../__pycache__/asyncresolver.cpython-312.pyc | Bin 0 -> 21163 bytes .../dns/__pycache__/btree.cpython-312.pyc | Bin 0 -> 40912 bytes .../dns/__pycache__/btreezone.cpython-312.pyc | Bin 0 -> 21045 bytes .../dns/__pycache__/dnssec.cpython-312.pyc | Bin 0 -> 51265 bytes .../__pycache__/dnssectypes.cpython-312.pyc | Bin 0 -> 1993 bytes .../dns/__pycache__/e164.cpython-312.pyc | Bin 0 -> 4823 bytes .../dns/__pycache__/edns.cpython-312.pyc | Bin 0 -> 26141 bytes .../dns/__pycache__/entropy.cpython-312.pyc | Bin 0 -> 5991 bytes .../dns/__pycache__/enum.cpython-312.pyc | Bin 0 -> 4888 bytes .../dns/__pycache__/exception.cpython-312.pyc | Bin 0 -> 7224 bytes .../dns/__pycache__/flags.cpython-312.pyc | Bin 0 -> 3095 bytes .../dns/__pycache__/grange.cpython-312.pyc | Bin 0 -> 1810 bytes .../dns/__pycache__/immutable.cpython-312.pyc | Bin 0 -> 3805 bytes .../dns/__pycache__/inet.cpython-312.pyc | Bin 0 -> 6687 bytes .../dns/__pycache__/ipv4.cpython-312.pyc | Bin 0 -> 2585 bytes .../dns/__pycache__/ipv6.cpython-312.pyc | Bin 0 -> 6711 bytes .../dns/__pycache__/message.cpython-312.pyc | Bin 0 -> 88789 bytes .../dns/__pycache__/name.cpython-312.pyc | Bin 0 -> 49198 bytes .../dns/__pycache__/namedict.cpython-312.pyc | Bin 0 -> 4379 bytes .../__pycache__/nameserver.cpython-312.pyc | Bin 0 -> 14218 bytes .../dns/__pycache__/node.cpython-312.pyc | Bin 0 -> 16605 bytes .../dns/__pycache__/opcode.cpython-312.pyc | Bin 0 -> 3233 bytes .../dns/__pycache__/query.cpython-312.pyc | Bin 0 -> 69436 bytes .../dns/__pycache__/rcode.cpython-312.pyc | Bin 0 -> 4509 bytes .../dns/__pycache__/rdata.cpython-312.pyc | Bin 0 -> 39819 bytes .../__pycache__/rdataclass.cpython-312.pyc | Bin 0 -> 3541 bytes .../dns/__pycache__/rdataset.cpython-312.pyc | Bin 0 -> 22955 bytes .../dns/__pycache__/rdatatype.cpython-312.pyc | Bin 0 -> 10321 bytes .../dns/__pycache__/renderer.cpython-312.pyc | Bin 0 -> 16335 bytes .../dns/__pycache__/resolver.cpython-312.pyc | Bin 0 -> 88448 bytes .../__pycache__/reversename.cpython-312.pyc | Bin 0 -> 4793 bytes .../dns/__pycache__/rrset.cpython-312.pyc | Bin 0 -> 12487 bytes .../dns/__pycache__/serial.cpython-312.pyc | Bin 0 -> 5205 bytes .../dns/__pycache__/set.cpython-312.pyc | Bin 0 -> 12293 bytes .../dns/__pycache__/tokenizer.cpython-312.pyc | Bin 0 -> 26560 bytes .../__pycache__/transaction.cpython-312.pyc | Bin 0 -> 29318 bytes .../dns/__pycache__/tsig.cpython-312.pyc | Bin 0 -> 17040 bytes .../__pycache__/tsigkeyring.cpython-312.pyc | Bin 0 -> 2875 bytes .../dns/__pycache__/ttl.cpython-312.pyc | Bin 0 -> 2366 bytes .../dns/__pycache__/update.cpython-312.pyc | Bin 0 -> 16298 bytes .../dns/__pycache__/version.cpython-312.pyc | Bin 0 -> 786 bytes .../dns/__pycache__/versioned.cpython-312.pyc | Bin 0 -> 14828 bytes .../dns/__pycache__/win32util.cpython-312.pyc | Bin 0 -> 19800 bytes .../dns/__pycache__/wire.cpython-312.pyc | Bin 0 -> 5798 bytes .../dns/__pycache__/xfr.cpython-312.pyc | Bin 0 -> 15066 bytes .../dns/__pycache__/zone.cpython-312.pyc | Bin 0 -> 68636 bytes .../dns/__pycache__/zonefile.cpython-312.pyc | Bin 0 -> 34543 bytes .../dns/__pycache__/zonetypes.cpython-312.pyc | Bin 0 -> 1367 bytes .../site-packages/dns/_asyncbackend.py | 100 + .../site-packages/dns/_asyncio_backend.py | 276 + .../lib/python3.12/site-packages/dns/_ddr.py | 154 + .../python3.12/site-packages/dns/_features.py | 95 + .../site-packages/dns/_immutable_ctx.py | 76 + .../python3.12/site-packages/dns/_no_ssl.py | 61 + .../python3.12/site-packages/dns/_tls_util.py | 19 + .../site-packages/dns/_trio_backend.py | 255 + .../site-packages/dns/asyncbackend.py | 101 + .../site-packages/dns/asyncquery.py | 953 ++ .../site-packages/dns/asyncresolver.py | 478 + .../lib/python3.12/site-packages/dns/btree.py | 850 ++ .../python3.12/site-packages/dns/btreezone.py | 367 + .../python3.12/site-packages/dns/dnssec.py | 1242 +++ .../site-packages/dns/dnssecalgs/__init__.py | 124 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5506 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 4611 bytes .../__pycache__/cryptography.cpython-312.pyc | Bin 0 -> 3821 bytes .../__pycache__/dsa.cpython-312.pyc | Bin 0 -> 6250 bytes .../__pycache__/ecdsa.cpython-312.pyc | Bin 0 -> 6115 bytes .../__pycache__/eddsa.cpython-312.pyc | Bin 0 -> 4220 bytes .../__pycache__/rsa.cpython-312.pyc | Bin 0 -> 7281 bytes .../site-packages/dns/dnssecalgs/base.py | 89 + .../dns/dnssecalgs/cryptography.py | 68 + .../site-packages/dns/dnssecalgs/dsa.py | 108 + .../site-packages/dns/dnssecalgs/ecdsa.py | 100 + .../site-packages/dns/dnssecalgs/eddsa.py | 70 + .../site-packages/dns/dnssecalgs/rsa.py | 126 + .../site-packages/dns/dnssectypes.py | 71 + .../lib/python3.12/site-packages/dns/e164.py | 116 + .../lib/python3.12/site-packages/dns/edns.py | 591 ++ .../python3.12/site-packages/dns/entropy.py | 130 + .../lib/python3.12/site-packages/dns/enum.py | 113 + .../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 | 195 + .../lib/python3.12/site-packages/dns/ipv4.py | 76 + .../lib/python3.12/site-packages/dns/ipv6.py | 217 + .../python3.12/site-packages/dns/message.py | 1954 ++++ .../lib/python3.12/site-packages/dns/name.py | 1289 +++ .../python3.12/site-packages/dns/namedict.py | 109 + .../site-packages/dns/nameserver.py | 361 + .../lib/python3.12/site-packages/dns/node.py | 358 + .../python3.12/site-packages/dns/opcode.py | 119 + .../lib/python3.12/site-packages/dns/py.typed | 0 .../lib/python3.12/site-packages/dns/query.py | 1786 ++++ .../site-packages/dns/quic/__init__.py | 78 + .../quic/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3325 bytes .../quic/__pycache__/_asyncio.cpython-312.pyc | Bin 0 -> 18897 bytes .../quic/__pycache__/_common.cpython-312.pyc | Bin 0 -> 15073 bytes .../quic/__pycache__/_sync.cpython-312.pyc | Bin 0 -> 19818 bytes .../quic/__pycache__/_trio.cpython-312.pyc | Bin 0 -> 16159 bytes .../site-packages/dns/quic/_asyncio.py | 276 + .../site-packages/dns/quic/_common.py | 344 + .../site-packages/dns/quic/_sync.py | 306 + .../site-packages/dns/quic/_trio.py | 250 + .../lib/python3.12/site-packages/dns/rcode.py | 168 + .../lib/python3.12/site-packages/dns/rdata.py | 935 ++ .../site-packages/dns/rdataclass.py | 118 + .../python3.12/site-packages/dns/rdataset.py | 508 + .../python3.12/site-packages/dns/rdatatype.py | 338 + .../site-packages/dns/rdtypes/ANY/AFSDB.py | 45 + .../site-packages/dns/rdtypes/ANY/AMTRELAY.py | 89 + .../site-packages/dns/rdtypes/ANY/AVC.py | 26 + .../site-packages/dns/rdtypes/ANY/CAA.py | 67 + .../site-packages/dns/rdtypes/ANY/CDNSKEY.py | 33 + .../site-packages/dns/rdtypes/ANY/CDS.py | 29 + .../site-packages/dns/rdtypes/ANY/CERT.py | 113 + .../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/DSYNC.py | 72 + .../site-packages/dns/rdtypes/ANY/EUI48.py | 30 + .../site-packages/dns/rdtypes/ANY/EUI64.py | 30 + .../site-packages/dns/rdtypes/ANY/GPOS.py | 126 + .../site-packages/dns/rdtypes/ANY/HINFO.py | 64 + .../site-packages/dns/rdtypes/ANY/HIP.py | 85 + .../site-packages/dns/rdtypes/ANY/ISDN.py | 78 + .../site-packages/dns/rdtypes/ANY/L32.py | 42 + .../site-packages/dns/rdtypes/ANY/L64.py | 48 + .../site-packages/dns/rdtypes/ANY/LOC.py | 347 + .../site-packages/dns/rdtypes/ANY/LP.py | 42 + .../site-packages/dns/rdtypes/ANY/MX.py | 24 + .../site-packages/dns/rdtypes/ANY/NID.py | 48 + .../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 | 120 + .../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/RESINFO.py | 24 + .../site-packages/dns/rdtypes/ANY/RP.py | 58 + .../site-packages/dns/rdtypes/ANY/RRSIG.py | 155 + .../site-packages/dns/rdtypes/ANY/RT.py | 24 + .../site-packages/dns/rdtypes/ANY/SMIMEA.py | 9 + .../site-packages/dns/rdtypes/ANY/SOA.py | 78 + .../site-packages/dns/rdtypes/ANY/SPF.py | 26 + .../site-packages/dns/rdtypes/ANY/SSHFP.py | 67 + .../site-packages/dns/rdtypes/ANY/TKEY.py | 135 + .../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/WALLET.py | 9 + .../site-packages/dns/rdtypes/ANY/X25.py | 57 + .../site-packages/dns/rdtypes/ANY/ZONEMD.py | 64 + .../site-packages/dns/rdtypes/ANY/__init__.py | 71 + .../ANY/__pycache__/AFSDB.cpython-312.pyc | Bin 0 -> 1084 bytes .../ANY/__pycache__/AMTRELAY.cpython-312.pyc | Bin 0 -> 4235 bytes .../ANY/__pycache__/AVC.cpython-312.pyc | Bin 0 -> 657 bytes .../ANY/__pycache__/CAA.cpython-312.pyc | Bin 0 -> 3393 bytes .../ANY/__pycache__/CDNSKEY.cpython-312.pyc | Bin 0 -> 744 bytes .../ANY/__pycache__/CDS.cpython-312.pyc | Bin 0 -> 852 bytes .../ANY/__pycache__/CERT.cpython-312.pyc | Bin 0 -> 4501 bytes .../ANY/__pycache__/CNAME.cpython-312.pyc | Bin 0 -> 867 bytes .../ANY/__pycache__/CSYNC.cpython-312.pyc | Bin 0 -> 3352 bytes .../ANY/__pycache__/DLV.cpython-312.pyc | Bin 0 -> 654 bytes .../ANY/__pycache__/DNAME.cpython-312.pyc | Bin 0 -> 930 bytes .../ANY/__pycache__/DNSKEY.cpython-312.pyc | Bin 0 -> 741 bytes .../ANY/__pycache__/DS.cpython-312.pyc | Bin 0 -> 651 bytes .../ANY/__pycache__/DSYNC.cpython-312.pyc | Bin 0 -> 4327 bytes .../ANY/__pycache__/EUI48.cpython-312.pyc | Bin 0 -> 739 bytes .../ANY/__pycache__/EUI64.cpython-312.pyc | Bin 0 -> 739 bytes .../ANY/__pycache__/GPOS.cpython-312.pyc | Bin 0 -> 6121 bytes .../ANY/__pycache__/HINFO.cpython-312.pyc | Bin 0 -> 2986 bytes .../ANY/__pycache__/HIP.cpython-312.pyc | Bin 0 -> 4852 bytes .../ANY/__pycache__/ISDN.cpython-312.pyc | Bin 0 -> 3453 bytes .../ANY/__pycache__/L32.cpython-312.pyc | Bin 0 -> 2540 bytes .../ANY/__pycache__/L64.cpython-312.pyc | Bin 0 -> 2998 bytes .../ANY/__pycache__/LOC.cpython-312.pyc | Bin 0 -> 14147 bytes .../ANY/__pycache__/LP.cpython-312.pyc | Bin 0 -> 2489 bytes .../ANY/__pycache__/MX.cpython-312.pyc | Bin 0 -> 651 bytes .../ANY/__pycache__/NID.cpython-312.pyc | Bin 0 -> 2991 bytes .../ANY/__pycache__/NINFO.cpython-312.pyc | Bin 0 -> 663 bytes .../ANY/__pycache__/NS.cpython-312.pyc | Bin 0 -> 651 bytes .../ANY/__pycache__/NSEC.cpython-312.pyc | Bin 0 -> 3095 bytes .../ANY/__pycache__/NSEC3.cpython-312.pyc | Bin 0 -> 6399 bytes .../__pycache__/NSEC3PARAM.cpython-312.pyc | Bin 0 -> 3414 bytes .../__pycache__/OPENPGPKEY.cpython-312.pyc | Bin 0 -> 2329 bytes .../ANY/__pycache__/OPT.cpython-312.pyc | Bin 0 -> 3543 bytes .../ANY/__pycache__/PTR.cpython-312.pyc | Bin 0 -> 654 bytes .../ANY/__pycache__/RESINFO.cpython-312.pyc | Bin 0 -> 669 bytes .../ANY/__pycache__/RP.cpython-312.pyc | Bin 0 -> 2549 bytes .../ANY/__pycache__/RRSIG.cpython-312.pyc | Bin 0 -> 6616 bytes .../ANY/__pycache__/RT.cpython-312.pyc | Bin 0 -> 669 bytes .../ANY/__pycache__/SMIMEA.cpython-312.pyc | Bin 0 -> 669 bytes .../ANY/__pycache__/SOA.cpython-312.pyc | Bin 0 -> 3826 bytes .../ANY/__pycache__/SPF.cpython-312.pyc | Bin 0 -> 657 bytes .../ANY/__pycache__/SSHFP.cpython-312.pyc | Bin 0 -> 3159 bytes .../ANY/__pycache__/TKEY.cpython-312.pyc | Bin 0 -> 5103 bytes .../ANY/__pycache__/TLSA.cpython-312.pyc | Bin 0 -> 663 bytes .../ANY/__pycache__/TSIG.cpython-312.pyc | Bin 0 -> 5923 bytes .../ANY/__pycache__/TXT.cpython-312.pyc | Bin 0 -> 657 bytes .../ANY/__pycache__/URI.cpython-312.pyc | Bin 0 -> 4182 bytes .../ANY/__pycache__/WALLET.cpython-312.pyc | Bin 0 -> 666 bytes .../ANY/__pycache__/X25.cpython-312.pyc | Bin 0 -> 2371 bytes .../ANY/__pycache__/ZONEMD.cpython-312.pyc | Bin 0 -> 4212 bytes .../ANY/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 605 bytes .../site-packages/dns/rdtypes/CH/A.py | 60 + .../site-packages/dns/rdtypes/CH/__init__.py | 22 + .../rdtypes/CH/__pycache__/A.cpython-312.pyc | Bin 0 -> 2564 bytes .../CH/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 283 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 | 87 + .../site-packages/dns/rdtypes/IN/KX.py | 24 + .../site-packages/dns/rdtypes/IN/NAPTR.py | 109 + .../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 0 -> 2131 bytes .../IN/__pycache__/AAAA.cpython-312.pyc | Bin 0 -> 2155 bytes .../IN/__pycache__/APL.cpython-312.pyc | Bin 0 -> 6941 bytes .../IN/__pycache__/DHCID.cpython-312.pyc | Bin 0 -> 2275 bytes .../IN/__pycache__/HTTPS.cpython-312.pyc | Bin 0 -> 665 bytes .../IN/__pycache__/IPSECKEY.cpython-312.pyc | Bin 0 -> 4263 bytes .../rdtypes/IN/__pycache__/KX.cpython-312.pyc | Bin 0 -> 668 bytes .../IN/__pycache__/NAPTR.cpython-312.pyc | Bin 0 -> 5077 bytes .../IN/__pycache__/NSAP.cpython-312.pyc | Bin 0 -> 2721 bytes .../IN/__pycache__/NSAP_PTR.cpython-312.pyc | Bin 0 -> 676 bytes .../rdtypes/IN/__pycache__/PX.cpython-312.pyc | Bin 0 -> 3471 bytes .../IN/__pycache__/SRV.cpython-312.pyc | Bin 0 -> 3724 bytes .../IN/__pycache__/SVCB.cpython-312.pyc | Bin 0 -> 662 bytes .../IN/__pycache__/WKS.cpython-312.pyc | Bin 0 -> 4625 bytes .../IN/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 364 bytes .../site-packages/dns/rdtypes/__init__.py | 33 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 363 bytes .../__pycache__/dnskeybase.cpython-312.pyc | Bin 0 -> 3901 bytes .../__pycache__/dsbase.cpython-312.pyc | Bin 0 -> 4212 bytes .../__pycache__/euibase.cpython-312.pyc | Bin 0 -> 3622 bytes .../__pycache__/mxbase.cpython-312.pyc | Bin 0 -> 4415 bytes .../__pycache__/nsbase.cpython-312.pyc | Bin 0 -> 2848 bytes .../__pycache__/svcbbase.cpython-312.pyc | Bin 0 -> 29901 bytes .../__pycache__/tlsabase.cpython-312.pyc | Bin 0 -> 3408 bytes .../__pycache__/txtbase.cpython-312.pyc | Bin 0 -> 5211 bytes .../rdtypes/__pycache__/util.cpython-312.pyc | Bin 0 -> 13896 bytes .../site-packages/dns/rdtypes/dnskeybase.py | 83 + .../site-packages/dns/rdtypes/dsbase.py | 83 + .../site-packages/dns/rdtypes/euibase.py | 73 + .../site-packages/dns/rdtypes/mxbase.py | 87 + .../site-packages/dns/rdtypes/nsbase.py | 63 + .../site-packages/dns/rdtypes/svcbbase.py | 587 ++ .../site-packages/dns/rdtypes/tlsabase.py | 69 + .../site-packages/dns/rdtypes/txtbase.py | 109 + .../site-packages/dns/rdtypes/util.py | 269 + .../python3.12/site-packages/dns/renderer.py | 355 + .../python3.12/site-packages/dns/resolver.py | 2068 ++++ .../site-packages/dns/reversename.py | 106 + .../lib/python3.12/site-packages/dns/rrset.py | 287 + .../python3.12/site-packages/dns/serial.py | 118 + .venv/lib/python3.12/site-packages/dns/set.py | 308 + .../python3.12/site-packages/dns/tokenizer.py | 706 ++ .../site-packages/dns/transaction.py | 651 ++ .../lib/python3.12/site-packages/dns/tsig.py | 359 + .../site-packages/dns/tsigkeyring.py | 68 + .venv/lib/python3.12/site-packages/dns/ttl.py | 90 + .../python3.12/site-packages/dns/update.py | 389 + .../python3.12/site-packages/dns/version.py | 42 + .../python3.12/site-packages/dns/versioned.py | 320 + .../python3.12/site-packages/dns/win32util.py | 438 + .../lib/python3.12/site-packages/dns/wire.py | 98 + .venv/lib/python3.12/site-packages/dns/xfr.py | 356 + .../lib/python3.12/site-packages/dns/zone.py | 1462 +++ .../python3.12/site-packages/dns/zonefile.py | 756 ++ .../python3.12/site-packages/dns/zonetypes.py | 37 + .../dnspython-2.8.0.dist-info/INSTALLER | 1 + .../dnspython-2.8.0.dist-info/METADATA | 149 + .../dnspython-2.8.0.dist-info/RECORD | 304 + .../dnspython-2.8.0.dist-info/WHEEL | 4 + .../licenses/LICENSE | 35 + .../site-packages/gridfs/__init__.py | 54 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1071 bytes .../gridfs/__pycache__/errors.cpython-312.pyc | Bin 0 -> 1221 bytes .../__pycache__/grid_file.cpython-312.pyc | Bin 0 -> 372 bytes .../grid_file_shared.cpython-312.pyc | Bin 0 -> 6604 bytes .../gridfs/asynchronous/__init__.py | 42 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 910 bytes .../__pycache__/grid_file.cpython-312.pyc | Bin 0 -> 93046 bytes .../gridfs/asynchronous/grid_file.py | 2008 ++++ .../python3.12/site-packages/gridfs/errors.py | 34 + .../site-packages/gridfs/grid_file.py | 18 + .../site-packages/gridfs/grid_file_shared.py | 168 + .../python3.12/site-packages/gridfs/py.typed | 2 + .../gridfs/synchronous/__init__.py | 42 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 881 bytes .../__pycache__/grid_file.cpython-312.pyc | Bin 0 -> 86896 bytes .../gridfs/synchronous/grid_file.py | 1994 ++++ .../pip-24.0.dist-info/AUTHORS.txt | 760 ++ .../pip-24.0.dist-info/INSTALLER | 1 + .../pip-24.0.dist-info/LICENSE.txt | 20 + .../site-packages/pip-24.0.dist-info/METADATA | 88 + .../site-packages/pip-24.0.dist-info/RECORD | 1005 ++ .../pip-24.0.dist-info/REQUESTED | 0 .../site-packages/pip-24.0.dist-info/WHEEL | 5 + .../pip-24.0.dist-info/entry_points.txt | 4 + .../pip-24.0.dist-info/top_level.txt | 1 + .../python3.12/site-packages/pip/__init__.py | 13 + .../python3.12/site-packages/pip/__main__.py | 24 + .../site-packages/pip/__pip-runner__.py | 50 + .../pip/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 706 bytes .../pip/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 862 bytes .../__pip-runner__.cpython-312.pyc | Bin 0 -> 2225 bytes .../site-packages/pip/_internal/__init__.py | 18 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 808 bytes .../__pycache__/build_env.cpython-312.pyc | Bin 0 -> 14315 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 12686 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 0 -> 17687 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 33305 bytes .../__pycache__/main.cpython-312.pyc | Bin 0 -> 691 bytes .../__pycache__/pyproject.cpython-312.pyc | Bin 0 -> 4992 bytes .../self_outdated_check.cpython-312.pyc | Bin 0 -> 10573 bytes .../__pycache__/wheel_builder.cpython-312.pyc | Bin 0 -> 13670 bytes .../site-packages/pip/_internal/build_env.py | 311 + .../site-packages/pip/_internal/cache.py | 290 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 299 bytes .../autocompletion.cpython-312.pyc | Bin 0 -> 8486 bytes .../__pycache__/base_command.cpython-312.pyc | Bin 0 -> 10476 bytes .../__pycache__/cmdoptions.cpython-312.pyc | Bin 0 -> 30395 bytes .../command_context.cpython-312.pyc | Bin 0 -> 1802 bytes .../cli/__pycache__/main.cpython-312.pyc | Bin 0 -> 2319 bytes .../__pycache__/main_parser.cpython-312.pyc | Bin 0 -> 4926 bytes .../cli/__pycache__/parser.cpython-312.pyc | Bin 0 -> 15043 bytes .../__pycache__/progress_bars.cpython-312.pyc | Bin 0 -> 2641 bytes .../__pycache__/req_command.cpython-312.pyc | Bin 0 -> 18873 bytes .../cli/__pycache__/spinners.cpython-312.pyc | Bin 0 -> 7861 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 0 -> 396 bytes .../pip/_internal/cli/autocompletion.py | 172 + .../pip/_internal/cli/base_command.py | 236 + .../pip/_internal/cli/cmdoptions.py | 1074 ++ .../pip/_internal/cli/command_context.py | 27 + .../site-packages/pip/_internal/cli/main.py | 79 + .../pip/_internal/cli/main_parser.py | 134 + .../site-packages/pip/_internal/cli/parser.py | 294 + .../pip/_internal/cli/progress_bars.py | 68 + .../pip/_internal/cli/req_command.py | 505 + .../pip/_internal/cli/spinners.py | 159 + .../pip/_internal/cli/status_codes.py | 6 + .../pip/_internal/commands/__init__.py | 132 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4023 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 9732 bytes .../__pycache__/check.cpython-312.pyc | Bin 0 -> 2111 bytes .../__pycache__/completion.cpython-312.pyc | Bin 0 -> 5213 bytes .../__pycache__/configuration.cpython-312.pyc | Bin 0 -> 13233 bytes .../__pycache__/debug.cpython-312.pyc | Bin 0 -> 10182 bytes .../__pycache__/download.cpython-312.pyc | Bin 0 -> 7610 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 0 -> 4437 bytes .../commands/__pycache__/hash.cpython-312.pyc | Bin 0 -> 3004 bytes .../commands/__pycache__/help.cpython-312.pyc | Bin 0 -> 1694 bytes .../__pycache__/index.cpython-312.pyc | Bin 0 -> 6741 bytes .../__pycache__/inspect.cpython-312.pyc | Bin 0 -> 3996 bytes .../__pycache__/install.cpython-312.pyc | Bin 0 -> 28934 bytes .../commands/__pycache__/list.cpython-312.pyc | Bin 0 -> 15677 bytes .../__pycache__/search.cpython-312.pyc | Bin 0 -> 7642 bytes .../commands/__pycache__/show.cpython-312.pyc | Bin 0 -> 9749 bytes .../__pycache__/uninstall.cpython-312.pyc | Bin 0 -> 4747 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 8977 bytes .../pip/_internal/commands/cache.py | 225 + .../pip/_internal/commands/check.py | 54 + .../pip/_internal/commands/completion.py | 130 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 201 + .../pip/_internal/commands/download.py | 147 + .../pip/_internal/commands/freeze.py | 109 + .../pip/_internal/commands/hash.py | 59 + .../pip/_internal/commands/help.py | 41 + .../pip/_internal/commands/index.py | 139 + .../pip/_internal/commands/inspect.py | 92 + .../pip/_internal/commands/install.py | 774 ++ .../pip/_internal/commands/list.py | 370 + .../pip/_internal/commands/search.py | 174 + .../pip/_internal/commands/show.py | 189 + .../pip/_internal/commands/uninstall.py | 113 + .../pip/_internal/commands/wheel.py | 183 + .../pip/_internal/configuration.py | 383 + .../pip/_internal/distributions/__init__.py | 21 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 962 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 2883 bytes .../__pycache__/installed.cpython-312.pyc | Bin 0 -> 1721 bytes .../__pycache__/sdist.cpython-312.pyc | Bin 0 -> 8509 bytes .../__pycache__/wheel.cpython-312.pyc | Bin 0 -> 2269 bytes .../pip/_internal/distributions/base.py | 51 + .../pip/_internal/distributions/installed.py | 29 + .../pip/_internal/distributions/sdist.py | 156 + .../pip/_internal/distributions/wheel.py | 40 + .../site-packages/pip/_internal/exceptions.py | 728 ++ .../pip/_internal/index/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 253 bytes .../__pycache__/collector.cpython-312.pyc | Bin 0 -> 21907 bytes .../package_finder.cpython-312.pyc | Bin 0 -> 40756 bytes .../index/__pycache__/sources.cpython-312.pyc | Bin 0 -> 12625 bytes .../pip/_internal/index/collector.py | 507 + .../pip/_internal/index/package_finder.py | 1027 ++ .../pip/_internal/index/sources.py | 285 + .../pip/_internal/locations/__init__.py | 467 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 16797 bytes .../__pycache__/_distutils.cpython-312.pyc | Bin 0 -> 6877 bytes .../__pycache__/_sysconfig.cpython-312.pyc | Bin 0 -> 8032 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 3802 bytes .../pip/_internal/locations/_distutils.py | 172 + .../pip/_internal/locations/_sysconfig.py | 213 + .../pip/_internal/locations/base.py | 81 + .../site-packages/pip/_internal/main.py | 12 + .../pip/_internal/metadata/__init__.py | 128 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5903 bytes .../__pycache__/_json.cpython-312.pyc | Bin 0 -> 2896 bytes .../metadata/__pycache__/base.cpython-312.pyc | Bin 0 -> 35733 bytes .../__pycache__/pkg_resources.cpython-312.pyc | Bin 0 -> 15811 bytes .../pip/_internal/metadata/_json.py | 84 + .../pip/_internal/metadata/base.py | 702 ++ .../_internal/metadata/importlib/__init__.py | 6 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 379 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 3354 bytes .../__pycache__/_dists.cpython-312.pyc | Bin 0 -> 13446 bytes .../__pycache__/_envs.cpython-312.pyc | Bin 0 -> 11201 bytes .../_internal/metadata/importlib/_compat.py | 55 + .../_internal/metadata/importlib/_dists.py | 227 + .../pip/_internal/metadata/importlib/_envs.py | 189 + .../pip/_internal/metadata/pkg_resources.py | 278 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 287 bytes .../__pycache__/candidate.cpython-312.pyc | Bin 0 -> 1926 bytes .../__pycache__/direct_url.cpython-312.pyc | Bin 0 -> 11220 bytes .../format_control.cpython-312.pyc | Bin 0 -> 4248 bytes .../models/__pycache__/index.cpython-312.pyc | Bin 0 -> 1715 bytes .../installation_report.cpython-312.pyc | Bin 0 -> 2293 bytes .../models/__pycache__/link.cpython-312.pyc | Bin 0 -> 26023 bytes .../models/__pycache__/scheme.cpython-312.pyc | Bin 0 -> 1190 bytes .../__pycache__/search_scope.cpython-312.pyc | Bin 0 -> 5109 bytes .../selection_prefs.cpython-312.pyc | Bin 0 -> 1872 bytes .../__pycache__/target_python.cpython-312.pyc | Bin 0 -> 4975 bytes .../models/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 5801 bytes .../pip/_internal/models/candidate.py | 30 + .../pip/_internal/models/direct_url.py | 235 + .../pip/_internal/models/format_control.py | 78 + .../pip/_internal/models/index.py | 28 + .../_internal/models/installation_report.py | 56 + .../pip/_internal/models/link.py | 579 ++ .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 132 + .../pip/_internal/models/selection_prefs.py | 51 + .../pip/_internal/models/target_python.py | 122 + .../pip/_internal/models/wheel.py | 92 + .../pip/_internal/network/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 275 bytes .../network/__pycache__/auth.cpython-312.pyc | Bin 0 -> 22017 bytes .../network/__pycache__/cache.cpython-312.pyc | Bin 0 -> 6539 bytes .../__pycache__/download.cpython-312.pyc | Bin 0 -> 8574 bytes .../__pycache__/lazy_wheel.cpython-312.pyc | Bin 0 -> 11684 bytes .../__pycache__/session.cpython-312.pyc | Bin 0 -> 18795 bytes .../network/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2274 bytes .../__pycache__/xmlrpc.cpython-312.pyc | Bin 0 -> 2970 bytes .../pip/_internal/network/auth.py | 561 ++ .../pip/_internal/network/cache.py | 106 + .../pip/_internal/network/download.py | 186 + .../pip/_internal/network/lazy_wheel.py | 210 + .../pip/_internal/network/session.py | 520 + .../pip/_internal/network/utils.py | 96 + .../pip/_internal/network/xmlrpc.py | 62 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 218 bytes .../__pycache__/check.cpython-312.pyc | Bin 0 -> 7600 bytes .../__pycache__/freeze.cpython-312.pyc | Bin 0 -> 10138 bytes .../__pycache__/prepare.cpython-312.pyc | Bin 0 -> 25768 bytes .../_internal/operations/build/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 224 bytes .../__pycache__/build_tracker.cpython-312.pyc | Bin 0 -> 7844 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 1901 bytes .../metadata_editable.cpython-312.pyc | Bin 0 -> 1935 bytes .../metadata_legacy.cpython-312.pyc | Bin 0 -> 3086 bytes .../build/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 1705 bytes .../wheel_editable.cpython-312.pyc | Bin 0 -> 2046 bytes .../__pycache__/wheel_legacy.cpython-312.pyc | Bin 0 -> 3950 bytes .../operations/build/build_tracker.py | 139 + .../_internal/operations/build/metadata.py | 39 + .../operations/build/metadata_editable.py | 41 + .../operations/build/metadata_legacy.py | 74 + .../pip/_internal/operations/build/wheel.py | 37 + .../operations/build/wheel_editable.py | 46 + .../operations/build/wheel_legacy.py | 102 + .../pip/_internal/operations/check.py | 187 + .../pip/_internal/operations/freeze.py | 255 + .../_internal/operations/install/__init__.py | 2 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 287 bytes .../editable_legacy.cpython-312.pyc | Bin 0 -> 1838 bytes .../install/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 33880 bytes .../operations/install/editable_legacy.py | 46 + .../pip/_internal/operations/install/wheel.py | 734 ++ .../pip/_internal/operations/prepare.py | 730 ++ .../site-packages/pip/_internal/pyproject.py | 179 + .../pip/_internal/req/__init__.py | 92 + .../req/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3764 bytes .../__pycache__/constructors.cpython-312.pyc | Bin 0 -> 21603 bytes .../req/__pycache__/req_file.cpython-312.pyc | Bin 0 -> 21482 bytes .../__pycache__/req_install.cpython-312.pyc | Bin 0 -> 38435 bytes .../req/__pycache__/req_set.cpython-312.pyc | Bin 0 -> 7239 bytes .../__pycache__/req_uninstall.cpython-312.pyc | Bin 0 -> 32998 bytes .../pip/_internal/req/constructors.py | 576 ++ .../pip/_internal/req/req_file.py | 554 ++ .../pip/_internal/req/req_install.py | 923 ++ .../pip/_internal/req/req_set.py | 119 + .../pip/_internal/req/req_uninstall.py | 649 ++ .../pip/_internal/resolution/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 218 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 1206 bytes .../pip/_internal/resolution/base.py | 20 + .../_internal/resolution/legacy/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 225 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 0 -> 22460 bytes .../_internal/resolution/legacy/resolver.py | 598 ++ .../resolution/resolvelib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 229 bytes .../__pycache__/base.cpython-312.pyc | Bin 0 -> 8358 bytes .../__pycache__/candidates.cpython-312.pyc | Bin 0 -> 30419 bytes .../__pycache__/factory.cpython-312.pyc | Bin 0 -> 32135 bytes .../found_candidates.cpython-312.pyc | Bin 0 -> 6229 bytes .../__pycache__/provider.cpython-312.pyc | Bin 0 -> 10399 bytes .../__pycache__/reporter.cpython-312.pyc | Bin 0 -> 4956 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 11450 bytes .../__pycache__/resolver.cpython-312.pyc | Bin 0 -> 12372 bytes .../_internal/resolution/resolvelib/base.py | 141 + .../resolution/resolvelib/candidates.py | 597 ++ .../resolution/resolvelib/factory.py | 812 ++ .../resolution/resolvelib/found_candidates.py | 155 + .../resolution/resolvelib/provider.py | 255 + .../resolution/resolvelib/reporter.py | 80 + .../resolution/resolvelib/requirements.py | 166 + .../resolution/resolvelib/resolver.py | 317 + .../pip/_internal/self_outdated_check.py | 248 + .../pip/_internal/utils/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 213 bytes .../__pycache__/_jaraco_text.cpython-312.pyc | Bin 0 -> 4554 bytes .../utils/__pycache__/_log.cpython-312.pyc | Bin 0 -> 1884 bytes .../utils/__pycache__/appdirs.cpython-312.pyc | Bin 0 -> 2428 bytes .../utils/__pycache__/compat.cpython-312.pyc | Bin 0 -> 2231 bytes .../compatibility_tags.cpython-312.pyc | Bin 0 -> 5579 bytes .../__pycache__/datetime.cpython-312.pyc | Bin 0 -> 702 bytes .../__pycache__/deprecation.cpython-312.pyc | Bin 0 -> 4204 bytes .../direct_url_helpers.cpython-312.pyc | Bin 0 -> 3581 bytes .../__pycache__/egg_link.cpython-312.pyc | Bin 0 -> 3244 bytes .../__pycache__/encoding.cpython-312.pyc | Bin 0 -> 2176 bytes .../__pycache__/entrypoints.cpython-312.pyc | Bin 0 -> 4011 bytes .../__pycache__/filesystem.cpython-312.pyc | Bin 0 -> 7476 bytes .../__pycache__/filetypes.cpython-312.pyc | Bin 0 -> 1182 bytes .../utils/__pycache__/glibc.cpython-312.pyc | Bin 0 -> 2360 bytes .../utils/__pycache__/hashes.cpython-312.pyc | Bin 0 -> 7572 bytes .../utils/__pycache__/logging.cpython-312.pyc | Bin 0 -> 13575 bytes .../utils/__pycache__/misc.cpython-312.pyc | Bin 0 -> 34139 bytes .../utils/__pycache__/models.cpython-312.pyc | Bin 0 -> 2730 bytes .../__pycache__/packaging.cpython-312.pyc | Bin 0 -> 2601 bytes .../setuptools_build.cpython-312.pyc | Bin 0 -> 4568 bytes .../__pycache__/subprocess.cpython-312.pyc | Bin 0 -> 8736 bytes .../__pycache__/temp_dir.cpython-312.pyc | Bin 0 -> 12080 bytes .../__pycache__/unpacking.cpython-312.pyc | Bin 0 -> 11126 bytes .../utils/__pycache__/urls.cpython-312.pyc | Bin 0 -> 2423 bytes .../__pycache__/virtualenv.cpython-312.pyc | Bin 0 -> 4498 bytes .../utils/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 5944 bytes .../pip/_internal/utils/_jaraco_text.py | 109 + .../site-packages/pip/_internal/utils/_log.py | 38 + .../pip/_internal/utils/appdirs.py | 52 + .../pip/_internal/utils/compat.py | 63 + .../pip/_internal/utils/compatibility_tags.py | 165 + .../pip/_internal/utils/datetime.py | 11 + .../pip/_internal/utils/deprecation.py | 120 + .../pip/_internal/utils/direct_url_helpers.py | 87 + .../pip/_internal/utils/egg_link.py | 80 + .../pip/_internal/utils/encoding.py | 36 + .../pip/_internal/utils/entrypoints.py | 84 + .../pip/_internal/utils/filesystem.py | 153 + .../pip/_internal/utils/filetypes.py | 27 + .../pip/_internal/utils/glibc.py | 88 + .../pip/_internal/utils/hashes.py | 151 + .../pip/_internal/utils/logging.py | 348 + .../site-packages/pip/_internal/utils/misc.py | 783 ++ .../pip/_internal/utils/models.py | 39 + .../pip/_internal/utils/packaging.py | 57 + .../pip/_internal/utils/setuptools_build.py | 146 + .../pip/_internal/utils/subprocess.py | 260 + .../pip/_internal/utils/temp_dir.py | 296 + .../pip/_internal/utils/unpacking.py | 257 + .../site-packages/pip/_internal/utils/urls.py | 62 + .../pip/_internal/utils/virtualenv.py | 104 + .../pip/_internal/utils/wheel.py | 134 + .../pip/_internal/vcs/__init__.py | 15 + .../vcs/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 552 bytes .../vcs/__pycache__/bazaar.cpython-312.pyc | Bin 0 -> 5044 bytes .../vcs/__pycache__/git.cpython-312.pyc | Bin 0 -> 19013 bytes .../vcs/__pycache__/mercurial.cpython-312.pyc | Bin 0 -> 7633 bytes .../__pycache__/subversion.cpython-312.pyc | Bin 0 -> 12505 bytes .../versioncontrol.cpython-312.pyc | Bin 0 -> 29031 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 112 + .../site-packages/pip/_internal/vcs/git.py | 526 + .../pip/_internal/vcs/mercurial.py | 163 + .../pip/_internal/vcs/subversion.py | 324 + .../pip/_internal/vcs/versioncontrol.py | 705 ++ .../pip/_internal/wheel_builder.py | 354 + .../site-packages/pip/_vendor/__init__.py | 121 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4714 bytes .../_vendor/__pycache__/six.cpython-312.pyc | Bin 0 -> 41291 bytes .../typing_extensions.cpython-312.pyc | Bin 0 -> 122071 bytes .../pip/_vendor/cachecontrol/__init__.py | 28 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 924 bytes .../__pycache__/_cmd.cpython-312.pyc | Bin 0 -> 2668 bytes .../__pycache__/adapter.cpython-312.pyc | Bin 0 -> 6486 bytes .../__pycache__/cache.cpython-312.pyc | Bin 0 -> 3831 bytes .../__pycache__/controller.cpython-312.pyc | Bin 0 -> 16189 bytes .../__pycache__/filewrapper.cpython-312.pyc | Bin 0 -> 4369 bytes .../__pycache__/heuristics.cpython-312.pyc | Bin 0 -> 6716 bytes .../__pycache__/serialize.cpython-312.pyc | Bin 0 -> 6427 bytes .../__pycache__/wrapper.cpython-312.pyc | Bin 0 -> 1696 bytes .../pip/_vendor/cachecontrol/_cmd.py | 70 + .../pip/_vendor/cachecontrol/adapter.py | 161 + .../pip/_vendor/cachecontrol/cache.py | 74 + .../_vendor/cachecontrol/caches/__init__.py | 8 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 457 bytes .../__pycache__/file_cache.cpython-312.pyc | Bin 0 -> 7732 bytes .../__pycache__/redis_cache.cpython-312.pyc | Bin 0 -> 2760 bytes .../_vendor/cachecontrol/caches/file_cache.py | 181 + .../cachecontrol/caches/redis_cache.py | 48 + .../pip/_vendor/cachecontrol/controller.py | 494 + .../pip/_vendor/cachecontrol/filewrapper.py | 119 + .../pip/_vendor/cachecontrol/heuristics.py | 154 + .../pip/_vendor/cachecontrol/serialize.py | 206 + .../pip/_vendor/cachecontrol/wrapper.py | 43 + .../pip/_vendor/certifi/__init__.py | 4 + .../pip/_vendor/certifi/__main__.py | 12 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 340 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 667 bytes .../certifi/__pycache__/core.cpython-312.pyc | Bin 0 -> 3349 bytes .../pip/_vendor/certifi/cacert.pem | 4635 +++++++++ .../site-packages/pip/_vendor/certifi/core.py | 119 + .../pip/_vendor/chardet/__init__.py | 115 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4590 bytes .../__pycache__/big5freq.cpython-312.pyc | Bin 0 -> 27221 bytes .../__pycache__/big5prober.cpython-312.pyc | Bin 0 -> 1409 bytes .../chardistribution.cpython-312.pyc | Bin 0 -> 9660 bytes .../charsetgroupprober.cpython-312.pyc | Bin 0 -> 4144 bytes .../__pycache__/charsetprober.cpython-312.pyc | Bin 0 -> 5040 bytes .../codingstatemachine.cpython-312.pyc | Bin 0 -> 3900 bytes .../codingstatemachinedict.cpython-312.pyc | Bin 0 -> 811 bytes .../__pycache__/cp949prober.cpython-312.pyc | Bin 0 -> 1418 bytes .../chardet/__pycache__/enums.cpython-312.pyc | Bin 0 -> 3018 bytes .../__pycache__/escprober.cpython-312.pyc | Bin 0 -> 4588 bytes .../chardet/__pycache__/escsm.cpython-312.pyc | Bin 0 -> 15332 bytes .../__pycache__/eucjpprober.cpython-312.pyc | Bin 0 -> 4405 bytes .../__pycache__/euckrfreq.cpython-312.pyc | Bin 0 -> 12104 bytes .../__pycache__/euckrprober.cpython-312.pyc | Bin 0 -> 1412 bytes .../__pycache__/euctwfreq.cpython-312.pyc | Bin 0 -> 27226 bytes .../__pycache__/euctwprober.cpython-312.pyc | Bin 0 -> 1412 bytes .../__pycache__/gb2312freq.cpython-312.pyc | Bin 0 -> 19148 bytes .../__pycache__/gb2312prober.cpython-312.pyc | Bin 0 -> 1425 bytes .../__pycache__/hebrewprober.cpython-312.pyc | Bin 0 -> 5844 bytes .../__pycache__/jisfreq.cpython-312.pyc | Bin 0 -> 22177 bytes .../__pycache__/johabfreq.cpython-312.pyc | Bin 0 -> 83025 bytes .../__pycache__/johabprober.cpython-312.pyc | Bin 0 -> 1416 bytes .../__pycache__/jpcntx.cpython-312.pyc | Bin 0 -> 39571 bytes .../langbulgarianmodel.cpython-312.pyc | Bin 0 -> 83144 bytes .../langgreekmodel.cpython-312.pyc | Bin 0 -> 77010 bytes .../langhebrewmodel.cpython-312.pyc | Bin 0 -> 77521 bytes .../langhungarianmodel.cpython-312.pyc | Bin 0 -> 83098 bytes .../langrussianmodel.cpython-312.pyc | Bin 0 -> 105273 bytes .../__pycache__/langthaimodel.cpython-312.pyc | Bin 0 -> 77699 bytes .../langturkishmodel.cpython-312.pyc | Bin 0 -> 77538 bytes .../__pycache__/latin1prober.cpython-312.pyc | Bin 0 -> 7024 bytes .../macromanprober.cpython-312.pyc | Bin 0 -> 7204 bytes .../mbcharsetprober.cpython-312.pyc | Bin 0 -> 3925 bytes .../mbcsgroupprober.cpython-312.pyc | Bin 0 -> 1610 bytes .../__pycache__/mbcssm.cpython-312.pyc | Bin 0 -> 38667 bytes .../__pycache__/resultdict.cpython-312.pyc | Bin 0 -> 654 bytes .../sbcharsetprober.cpython-312.pyc | Bin 0 -> 6409 bytes .../sbcsgroupprober.cpython-312.pyc | Bin 0 -> 2379 bytes .../__pycache__/sjisprober.cpython-312.pyc | Bin 0 -> 4517 bytes .../universaldetector.cpython-312.pyc | Bin 0 -> 12291 bytes .../__pycache__/utf1632prober.cpython-312.pyc | Bin 0 -> 10001 bytes .../__pycache__/utf8prober.cpython-312.pyc | Bin 0 -> 3197 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 510 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 261 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 147 + .../pip/_vendor/chardet/cli/__init__.py | 0 .../cli/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 217 bytes .../__pycache__/chardetect.cpython-312.pyc | Bin 0 -> 4034 bytes .../pip/_vendor/chardet/cli/chardetect.py | 112 + .../pip/_vendor/chardet/codingstatemachine.py | 90 + .../_vendor/chardet/codingstatemachinedict.py | 19 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 85 + .../pip/_vendor/chardet/escprober.py | 102 + .../pip/_vendor/chardet/escsm.py | 261 + .../pip/_vendor/chardet/eucjpprober.py | 102 + .../pip/_vendor/chardet/euckrfreq.py | 196 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 388 + .../pip/_vendor/chardet/euctwprober.py | 47 + .../pip/_vendor/chardet/gb2312freq.py | 284 + .../pip/_vendor/chardet/gb2312prober.py | 47 + .../pip/_vendor/chardet/hebrewprober.py | 316 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/johabfreq.py | 2382 +++++ .../pip/_vendor/chardet/johabprober.py | 47 + .../pip/_vendor/chardet/jpcntx.py | 238 + .../pip/_vendor/chardet/langbulgarianmodel.py | 4649 +++++++++ .../pip/_vendor/chardet/langgreekmodel.py | 4397 +++++++++ .../pip/_vendor/chardet/langhebrewmodel.py | 4380 +++++++++ .../pip/_vendor/chardet/langhungarianmodel.py | 4649 +++++++++ .../pip/_vendor/chardet/langrussianmodel.py | 5725 +++++++++++ .../pip/_vendor/chardet/langthaimodel.py | 4380 +++++++++ .../pip/_vendor/chardet/langturkishmodel.py | 4380 +++++++++ .../pip/_vendor/chardet/latin1prober.py | 147 + .../pip/_vendor/chardet/macromanprober.py | 162 + .../pip/_vendor/chardet/mbcharsetprober.py | 95 + .../pip/_vendor/chardet/mbcsgroupprober.py | 57 + .../pip/_vendor/chardet/mbcssm.py | 661 ++ .../pip/_vendor/chardet/metadata/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 222 bytes .../__pycache__/languages.cpython-312.pyc | Bin 0 -> 9777 bytes .../pip/_vendor/chardet/metadata/languages.py | 352 + .../pip/_vendor/chardet/resultdict.py | 16 + .../pip/_vendor/chardet/sbcharsetprober.py | 162 + .../pip/_vendor/chardet/sbcsgroupprober.py | 88 + .../pip/_vendor/chardet/sjisprober.py | 105 + .../pip/_vendor/chardet/universaldetector.py | 362 + .../pip/_vendor/chardet/utf1632prober.py | 225 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 7 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 514 bytes .../colorama/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 3972 bytes .../__pycache__/ansitowin32.cpython-312.pyc | Bin 0 -> 16443 bytes .../__pycache__/initialise.cpython-312.pyc | Bin 0 -> 3572 bytes .../__pycache__/win32.cpython-312.pyc | Bin 0 -> 8148 bytes .../__pycache__/winterm.cpython-312.pyc | Bin 0 -> 9110 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 277 + .../pip/_vendor/colorama/initialise.py | 121 + .../pip/_vendor/colorama/tests/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 220 bytes .../__pycache__/ansi_test.cpython-312.pyc | Bin 0 -> 5489 bytes .../ansitowin32_test.cpython-312.pyc | Bin 0 -> 18125 bytes .../initialise_test.cpython-312.pyc | Bin 0 -> 11770 bytes .../__pycache__/isatty_test.cpython-312.pyc | Bin 0 -> 4926 bytes .../tests/__pycache__/utils.cpython-312.pyc | Bin 0 -> 2510 bytes .../__pycache__/winterm_test.cpython-312.pyc | Bin 0 -> 6634 bytes .../pip/_vendor/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../_vendor/colorama/tests/initialise_test.py | 189 + .../pip/_vendor/colorama/tests/isatty_test.py | 57 + .../pip/_vendor/colorama/tests/utils.py | 49 + .../_vendor/colorama/tests/winterm_test.py | 131 + .../pip/_vendor/colorama/win32.py | 180 + .../pip/_vendor/colorama/winterm.py | 195 + .../pip/_vendor/distlib/__init__.py | 33 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1291 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 45627 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 66049 bytes .../distlib/__pycache__/index.cpython-312.pyc | Bin 0 -> 24388 bytes .../__pycache__/locators.cpython-312.pyc | Bin 0 -> 60180 bytes .../__pycache__/manifest.cpython-312.pyc | Bin 0 -> 15147 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 7704 bytes .../__pycache__/metadata.cpython-312.pyc | Bin 0 -> 41821 bytes .../__pycache__/resources.cpython-312.pyc | Bin 0 -> 17347 bytes .../__pycache__/scripts.cpython-312.pyc | Bin 0 -> 19602 bytes .../distlib/__pycache__/util.cpython-312.pyc | Bin 0 -> 88278 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 30388 bytes .../distlib/__pycache__/wheel.cpython-312.pyc | Bin 0 -> 51883 bytes .../pip/_vendor/distlib/compat.py | 1138 +++ .../pip/_vendor/distlib/database.py | 1359 +++ .../pip/_vendor/distlib/index.py | 508 + .../pip/_vendor/distlib/locators.py | 1303 +++ .../pip/_vendor/distlib/manifest.py | 384 + .../pip/_vendor/distlib/markers.py | 167 + .../pip/_vendor/distlib/metadata.py | 1068 ++ .../pip/_vendor/distlib/resources.py | 358 + .../pip/_vendor/distlib/scripts.py | 452 + .../site-packages/pip/_vendor/distlib/util.py | 2025 ++++ .../pip/_vendor/distlib/version.py | 751 ++ .../pip/_vendor/distlib/wheel.py | 1099 +++ .../pip/_vendor/distro/__init__.py | 54 + .../pip/_vendor/distro/__main__.py | 4 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 982 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 314 bytes .../distro/__pycache__/distro.cpython-312.pyc | Bin 0 -> 53776 bytes .../pip/_vendor/distro/distro.py | 1399 +++ .../pip/_vendor/idna/__init__.py | 44 + .../idna/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 903 bytes .../idna/__pycache__/codec.cpython-312.pyc | Bin 0 -> 4655 bytes .../idna/__pycache__/compat.cpython-312.pyc | Bin 0 -> 909 bytes .../idna/__pycache__/core.cpython-312.pyc | Bin 0 -> 16045 bytes .../idna/__pycache__/idnadata.cpython-312.pyc | Bin 0 -> 99519 bytes .../__pycache__/intranges.cpython-312.pyc | Bin 0 -> 2660 bytes .../__pycache__/package_data.cpython-312.pyc | Bin 0 -> 238 bytes .../__pycache__/uts46data.cpython-312.pyc | Bin 0 -> 158892 bytes .../site-packages/pip/_vendor/idna/codec.py | 112 + .../site-packages/pip/_vendor/idna/compat.py | 13 + .../site-packages/pip/_vendor/idna/core.py | 400 + .../pip/_vendor/idna/idnadata.py | 4246 ++++++++ .../pip/_vendor/idna/intranges.py | 54 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8600 +++++++++++++++++ .../pip/_vendor/msgpack/__init__.py | 57 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1853 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 2047 bytes .../msgpack/__pycache__/ext.cpython-312.pyc | Bin 0 -> 8690 bytes .../__pycache__/fallback.cpython-312.pyc | Bin 0 -> 43598 bytes .../pip/_vendor/msgpack/exceptions.py | 48 + .../site-packages/pip/_vendor/msgpack/ext.py | 193 + .../pip/_vendor/msgpack/fallback.py | 1010 ++ .../pip/_vendor/packaging/__about__.py | 26 + .../pip/_vendor/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-312.pyc | Bin 0 -> 652 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 488 bytes .../__pycache__/_manylinux.cpython-312.pyc | Bin 0 -> 12098 bytes .../__pycache__/_musllinux.cpython-312.pyc | Bin 0 -> 6932 bytes .../__pycache__/_structures.cpython-312.pyc | Bin 0 -> 3263 bytes .../__pycache__/markers.cpython-312.pyc | Bin 0 -> 14080 bytes .../__pycache__/requirements.cpython-312.pyc | Bin 0 -> 6968 bytes .../__pycache__/specifiers.cpython-312.pyc | Bin 0 -> 31269 bytes .../__pycache__/tags.cpython-312.pyc | Bin 0 -> 18978 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 5890 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 19961 bytes .../pip/_vendor/packaging/_manylinux.py | 301 + .../pip/_vendor/packaging/_musllinux.py | 136 + .../pip/_vendor/packaging/_structures.py | 61 + .../pip/_vendor/packaging/markers.py | 304 + .../pip/_vendor/packaging/requirements.py | 146 + .../pip/_vendor/packaging/specifiers.py | 802 ++ .../pip/_vendor/packaging/tags.py | 487 + .../pip/_vendor/packaging/utils.py | 136 + .../pip/_vendor/packaging/version.py | 504 + .../pip/_vendor/pkg_resources/__init__.py | 3361 +++++++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 146496 bytes .../pip/_vendor/platformdirs/__init__.py | 566 ++ .../pip/_vendor/platformdirs/__main__.py | 53 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 18051 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 1968 bytes .../__pycache__/android.cpython-312.pyc | Bin 0 -> 9466 bytes .../__pycache__/api.cpython-312.pyc | Bin 0 -> 9694 bytes .../__pycache__/macos.cpython-312.pyc | Bin 0 -> 5659 bytes .../__pycache__/unix.cpython-312.pyc | Bin 0 -> 12463 bytes .../__pycache__/version.cpython-312.pyc | Bin 0 -> 333 bytes .../__pycache__/windows.cpython-312.pyc | Bin 0 -> 13021 bytes .../pip/_vendor/platformdirs/android.py | 210 + .../pip/_vendor/platformdirs/api.py | 223 + .../pip/_vendor/platformdirs/macos.py | 91 + .../pip/_vendor/platformdirs/unix.py | 223 + .../pip/_vendor/platformdirs/version.py | 4 + .../pip/_vendor/platformdirs/windows.py | 255 + .../pip/_vendor/pygments/__init__.py | 82 + .../pip/_vendor/pygments/__main__.py | 17 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3511 bytes .../__pycache__/__main__.cpython-312.pyc | Bin 0 -> 757 bytes .../__pycache__/cmdline.cpython-312.pyc | Bin 0 -> 26628 bytes .../__pycache__/console.cpython-312.pyc | Bin 0 -> 2649 bytes .../__pycache__/filter.cpython-312.pyc | Bin 0 -> 3255 bytes .../__pycache__/formatter.cpython-312.pyc | Bin 0 -> 4592 bytes .../__pycache__/lexer.cpython-312.pyc | Bin 0 -> 38352 bytes .../__pycache__/modeline.cpython-312.pyc | Bin 0 -> 1591 bytes .../__pycache__/plugin.cpython-312.pyc | Bin 0 -> 3419 bytes .../__pycache__/regexopt.cpython-312.pyc | Bin 0 -> 4104 bytes .../__pycache__/scanner.cpython-312.pyc | Bin 0 -> 4779 bytes .../__pycache__/sphinxext.cpython-312.pyc | Bin 0 -> 11069 bytes .../__pycache__/style.cpython-312.pyc | Bin 0 -> 6697 bytes .../__pycache__/token.cpython-312.pyc | Bin 0 -> 8165 bytes .../__pycache__/unistring.cpython-312.pyc | Bin 0 -> 33011 bytes .../pygments/__pycache__/util.cpython-312.pyc | Bin 0 -> 14004 bytes .../pip/_vendor/pygments/cmdline.py | 668 ++ .../pip/_vendor/pygments/console.py | 70 + .../pip/_vendor/pygments/filter.py | 71 + .../pip/_vendor/pygments/filters/__init__.py | 940 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 37959 bytes .../pip/_vendor/pygments/formatter.py | 124 + .../_vendor/pygments/formatters/__init__.py | 158 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 6949 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 4238 bytes .../__pycache__/bbcode.cpython-312.pyc | Bin 0 -> 4217 bytes .../__pycache__/groff.cpython-312.pyc | Bin 0 -> 7287 bytes .../__pycache__/html.cpython-312.pyc | Bin 0 -> 40595 bytes .../__pycache__/img.cpython-312.pyc | Bin 0 -> 27066 bytes .../__pycache__/irc.cpython-312.pyc | Bin 0 -> 6088 bytes .../__pycache__/latex.cpython-312.pyc | Bin 0 -> 19977 bytes .../__pycache__/other.cpython-312.pyc | Bin 0 -> 6907 bytes .../__pycache__/pangomarkup.cpython-312.pyc | Bin 0 -> 2953 bytes .../__pycache__/rtf.cpython-312.pyc | Bin 0 -> 6149 bytes .../__pycache__/svg.cpython-312.pyc | Bin 0 -> 9089 bytes .../__pycache__/terminal.cpython-312.pyc | Bin 0 -> 5852 bytes .../__pycache__/terminal256.cpython-312.pyc | Bin 0 -> 15180 bytes .../_vendor/pygments/formatters/_mapping.py | 23 + .../pip/_vendor/pygments/formatters/bbcode.py | 108 + .../pip/_vendor/pygments/formatters/groff.py | 170 + .../pip/_vendor/pygments/formatters/html.py | 989 ++ .../pip/_vendor/pygments/formatters/img.py | 645 ++ .../pip/_vendor/pygments/formatters/irc.py | 154 + .../pip/_vendor/pygments/formatters/latex.py | 521 + .../pip/_vendor/pygments/formatters/other.py | 161 + .../pygments/formatters/pangomarkup.py | 83 + .../pip/_vendor/pygments/formatters/rtf.py | 146 + .../pip/_vendor/pygments/formatters/svg.py | 188 + .../_vendor/pygments/formatters/terminal.py | 127 + .../pygments/formatters/terminal256.py | 338 + .../pip/_vendor/pygments/lexer.py | 943 ++ .../pip/_vendor/pygments/lexers/__init__.py | 362 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 14675 bytes .../__pycache__/_mapping.cpython-312.pyc | Bin 0 -> 64427 bytes .../lexers/__pycache__/python.cpython-312.pyc | Bin 0 -> 42662 bytes .../pip/_vendor/pygments/lexers/_mapping.py | 559 ++ .../pip/_vendor/pygments/lexers/python.py | 1198 +++ .../pip/_vendor/pygments/modeline.py | 43 + .../pip/_vendor/pygments/plugin.py | 88 + .../pip/_vendor/pygments/regexopt.py | 91 + .../pip/_vendor/pygments/scanner.py | 104 + .../pip/_vendor/pygments/sphinxext.py | 217 + .../pip/_vendor/pygments/style.py | 197 + .../pip/_vendor/pygments/styles/__init__.py | 103 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4471 bytes .../pip/_vendor/pygments/token.py | 213 + .../pip/_vendor/pygments/unistring.py | 153 + .../pip/_vendor/pygments/util.py | 330 + .../pip/_vendor/pyparsing/__init__.py | 322 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 7934 bytes .../__pycache__/actions.cpython-312.pyc | Bin 0 -> 8418 bytes .../__pycache__/common.cpython-312.pyc | Bin 0 -> 13437 bytes .../__pycache__/core.cpython-312.pyc | Bin 0 -> 267731 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 13017 bytes .../__pycache__/helpers.cpython-312.pyc | Bin 0 -> 48524 bytes .../__pycache__/results.cpython-312.pyc | Bin 0 -> 34133 bytes .../__pycache__/testing.cpython-312.pyc | Bin 0 -> 17211 bytes .../__pycache__/unicode.cpython-312.pyc | Bin 0 -> 13207 bytes .../__pycache__/util.cpython-312.pyc | Bin 0 -> 14927 bytes .../pip/_vendor/pyparsing/actions.py | 217 + .../pip/_vendor/pyparsing/common.py | 432 + .../pip/_vendor/pyparsing/core.py | 6115 ++++++++++++ .../pip/_vendor/pyparsing/diagram/__init__.py | 656 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 26836 bytes .../pip/_vendor/pyparsing/exceptions.py | 299 + .../pip/_vendor/pyparsing/helpers.py | 1100 +++ .../pip/_vendor/pyparsing/results.py | 796 ++ .../pip/_vendor/pyparsing/testing.py | 331 + .../pip/_vendor/pyparsing/unicode.py | 361 + .../pip/_vendor/pyparsing/util.py | 284 + .../pip/_vendor/pyproject_hooks/__init__.py | 23 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 636 bytes .../__pycache__/_compat.cpython-312.pyc | Bin 0 -> 397 bytes .../__pycache__/_impl.cpython-312.pyc | Bin 0 -> 14748 bytes .../pip/_vendor/pyproject_hooks/_compat.py | 8 + .../pip/_vendor/pyproject_hooks/_impl.py | 330 + .../pyproject_hooks/_in_process/__init__.py | 18 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1103 bytes .../__pycache__/_in_process.cpython-312.pyc | Bin 0 -> 14420 bytes .../_in_process/_in_process.py | 353 + .../pip/_vendor/requests/__init__.py | 182 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 5476 bytes .../__pycache__/__version__.cpython-312.pyc | Bin 0 -> 607 bytes .../_internal_utils.cpython-312.pyc | Bin 0 -> 2047 bytes .../__pycache__/adapters.cpython-312.pyc | Bin 0 -> 21303 bytes .../requests/__pycache__/api.cpython-312.pyc | Bin 0 -> 7227 bytes .../requests/__pycache__/auth.cpython-312.pyc | Bin 0 -> 13946 bytes .../__pycache__/certs.cpython-312.pyc | Bin 0 -> 945 bytes .../__pycache__/compat.cpython-312.pyc | Bin 0 -> 1530 bytes .../__pycache__/cookies.cpython-312.pyc | Bin 0 -> 25269 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 7070 bytes .../requests/__pycache__/help.cpython-312.pyc | Bin 0 -> 4335 bytes .../__pycache__/hooks.cpython-312.pyc | Bin 0 -> 1075 bytes .../__pycache__/models.cpython-312.pyc | Bin 0 -> 35471 bytes .../__pycache__/packages.cpython-312.pyc | Bin 0 -> 795 bytes .../__pycache__/sessions.cpython-312.pyc | Bin 0 -> 27780 bytes .../__pycache__/status_codes.cpython-312.pyc | Bin 0 -> 5982 bytes .../__pycache__/structures.cpython-312.pyc | Bin 0 -> 5640 bytes .../__pycache__/utils.cpython-312.pyc | Bin 0 -> 36098 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 50 + .../pip/_vendor/requests/adapters.py | 538 ++ .../site-packages/pip/_vendor/requests/api.py | 157 + .../pip/_vendor/requests/auth.py | 315 + .../pip/_vendor/requests/certs.py | 24 + .../pip/_vendor/requests/compat.py | 67 + .../pip/_vendor/requests/cookies.py | 561 ++ .../pip/_vendor/requests/exceptions.py | 141 + .../pip/_vendor/requests/help.py | 131 + .../pip/_vendor/requests/hooks.py | 33 + .../pip/_vendor/requests/models.py | 1034 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 833 ++ .../pip/_vendor/requests/status_codes.py | 128 + .../pip/_vendor/requests/structures.py | 99 + .../pip/_vendor/requests/utils.py | 1088 +++ .../pip/_vendor/resolvelib/__init__.py | 26 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 657 bytes .../__pycache__/providers.cpython-312.pyc | Bin 0 -> 6874 bytes .../__pycache__/reporters.cpython-312.pyc | Bin 0 -> 2677 bytes .../__pycache__/resolvers.cpython-312.pyc | Bin 0 -> 25920 bytes .../__pycache__/structs.cpython-312.pyc | Bin 0 -> 10529 bytes .../pip/_vendor/resolvelib/compat/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 223 bytes .../collections_abc.cpython-312.pyc | Bin 0 -> 443 bytes .../resolvelib/compat/collections_abc.py | 6 + .../pip/_vendor/resolvelib/providers.py | 133 + .../pip/_vendor/resolvelib/reporters.py | 43 + .../pip/_vendor/resolvelib/resolvers.py | 547 ++ .../pip/_vendor/resolvelib/structs.py | 170 + .../pip/_vendor/rich/__init__.py | 177 + .../pip/_vendor/rich/__main__.py | 274 + .../rich/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 7038 bytes .../rich/__pycache__/__main__.cpython-312.pyc | Bin 0 -> 10327 bytes .../__pycache__/_cell_widths.cpython-312.pyc | Bin 0 -> 7844 bytes .../__pycache__/_emoji_codes.cpython-312.pyc | Bin 0 -> 205999 bytes .../_emoji_replace.cpython-312.pyc | Bin 0 -> 1752 bytes .../_export_format.cpython-312.pyc | Bin 0 -> 2344 bytes .../__pycache__/_extension.cpython-312.pyc | Bin 0 -> 560 bytes .../rich/__pycache__/_fileno.cpython-312.pyc | Bin 0 -> 878 bytes .../rich/__pycache__/_inspect.cpython-312.pyc | Bin 0 -> 12100 bytes .../__pycache__/_log_render.cpython-312.pyc | Bin 0 -> 4170 bytes .../rich/__pycache__/_loop.cpython-312.pyc | Bin 0 -> 1908 bytes .../__pycache__/_null_file.cpython-312.pyc | Bin 0 -> 3643 bytes .../__pycache__/_palettes.cpython-312.pyc | Bin 0 -> 5183 bytes .../rich/__pycache__/_pick.cpython-312.pyc | Bin 0 -> 749 bytes .../rich/__pycache__/_ratio.cpython-312.pyc | Bin 0 -> 6602 bytes .../__pycache__/_spinners.cpython-312.pyc | Bin 0 -> 13202 bytes .../rich/__pycache__/_stack.cpython-312.pyc | Bin 0 -> 988 bytes .../rich/__pycache__/_timer.cpython-312.pyc | Bin 0 -> 888 bytes .../_win32_console.cpython-312.pyc | Bin 0 -> 28999 bytes .../rich/__pycache__/_windows.cpython-312.pyc | Bin 0 -> 2513 bytes .../_windows_renderer.cpython-312.pyc | Bin 0 -> 3596 bytes .../rich/__pycache__/_wrap.cpython-312.pyc | Bin 0 -> 2383 bytes .../rich/__pycache__/abc.cpython-312.pyc | Bin 0 -> 1631 bytes .../rich/__pycache__/align.cpython-312.pyc | Bin 0 -> 12345 bytes .../rich/__pycache__/ansi.cpython-312.pyc | Bin 0 -> 9129 bytes .../rich/__pycache__/bar.cpython-312.pyc | Bin 0 -> 4295 bytes .../rich/__pycache__/box.cpython-312.pyc | Bin 0 -> 11881 bytes .../rich/__pycache__/cells.cpython-312.pyc | Bin 0 -> 5641 bytes .../rich/__pycache__/color.cpython-312.pyc | Bin 0 -> 26593 bytes .../__pycache__/color_triplet.cpython-312.pyc | Bin 0 -> 1724 bytes .../rich/__pycache__/columns.cpython-312.pyc | Bin 0 -> 8610 bytes .../rich/__pycache__/console.cpython-312.pyc | Bin 0 -> 113816 bytes .../__pycache__/constrain.cpython-312.pyc | Bin 0 -> 2281 bytes .../__pycache__/containers.cpython-312.pyc | Bin 0 -> 9249 bytes .../rich/__pycache__/control.cpython-312.pyc | Bin 0 -> 10952 bytes .../default_styles.cpython-312.pyc | Bin 0 -> 10396 bytes .../rich/__pycache__/diagnose.cpython-312.pyc | Bin 0 -> 1510 bytes .../rich/__pycache__/emoji.cpython-312.pyc | Bin 0 -> 4232 bytes .../rich/__pycache__/errors.cpython-312.pyc | Bin 0 -> 1868 bytes .../__pycache__/file_proxy.cpython-312.pyc | Bin 0 -> 3600 bytes .../rich/__pycache__/filesize.cpython-312.pyc | Bin 0 -> 3105 bytes .../__pycache__/highlighter.cpython-312.pyc | Bin 0 -> 9921 bytes .../rich/__pycache__/json.cpython-312.pyc | Bin 0 -> 6058 bytes .../rich/__pycache__/jupyter.cpython-312.pyc | Bin 0 -> 5232 bytes .../rich/__pycache__/layout.cpython-312.pyc | Bin 0 -> 20243 bytes .../rich/__pycache__/live.cpython-312.pyc | Bin 0 -> 19166 bytes .../__pycache__/live_render.cpython-312.pyc | Bin 0 -> 4917 bytes .../rich/__pycache__/logging.cpython-312.pyc | Bin 0 -> 13577 bytes .../rich/__pycache__/markup.cpython-312.pyc | Bin 0 -> 9321 bytes .../rich/__pycache__/measure.cpython-312.pyc | Bin 0 -> 6399 bytes .../rich/__pycache__/padding.cpython-312.pyc | Bin 0 -> 7157 bytes .../rich/__pycache__/pager.cpython-312.pyc | Bin 0 -> 1843 bytes .../rich/__pycache__/palette.cpython-312.pyc | Bin 0 -> 5337 bytes .../rich/__pycache__/panel.cpython-312.pyc | Bin 0 -> 12120 bytes .../rich/__pycache__/pretty.cpython-312.pyc | Bin 0 -> 40079 bytes .../rich/__pycache__/progress.cpython-312.pyc | Bin 0 -> 75101 bytes .../__pycache__/progress_bar.cpython-312.pyc | Bin 0 -> 10412 bytes .../rich/__pycache__/prompt.cpython-312.pyc | Bin 0 -> 14804 bytes .../rich/__pycache__/protocol.cpython-312.pyc | Bin 0 -> 1815 bytes .../rich/__pycache__/region.cpython-312.pyc | Bin 0 -> 590 bytes .../rich/__pycache__/repr.cpython-312.pyc | Bin 0 -> 6649 bytes .../rich/__pycache__/rule.cpython-312.pyc | Bin 0 -> 6591 bytes .../rich/__pycache__/scope.cpython-312.pyc | Bin 0 -> 3853 bytes .../rich/__pycache__/screen.cpython-312.pyc | Bin 0 -> 2507 bytes .../rich/__pycache__/segment.cpython-312.pyc | Bin 0 -> 28184 bytes .../rich/__pycache__/spinner.cpython-312.pyc | Bin 0 -> 6087 bytes .../rich/__pycache__/status.cpython-312.pyc | Bin 0 -> 6091 bytes .../rich/__pycache__/style.cpython-312.pyc | Bin 0 -> 33537 bytes .../rich/__pycache__/styled.cpython-312.pyc | Bin 0 -> 2162 bytes .../rich/__pycache__/syntax.cpython-312.pyc | Bin 0 -> 39635 bytes .../rich/__pycache__/table.cpython-312.pyc | Bin 0 -> 43607 bytes .../terminal_theme.cpython-312.pyc | Bin 0 -> 3371 bytes .../rich/__pycache__/text.cpython-312.pyc | Bin 0 -> 58986 bytes .../rich/__pycache__/theme.cpython-312.pyc | Bin 0 -> 6363 bytes .../rich/__pycache__/themes.cpython-312.pyc | Bin 0 -> 337 bytes .../__pycache__/traceback.cpython-312.pyc | Bin 0 -> 31571 bytes .../rich/__pycache__/tree.cpython-312.pyc | Bin 0 -> 11462 bytes .../pip/_vendor/rich/_cell_widths.py | 451 + .../pip/_vendor/rich/_emoji_codes.py | 3610 +++++++ .../pip/_vendor/rich/_emoji_replace.py | 32 + .../pip/_vendor/rich/_export_format.py | 76 + .../pip/_vendor/rich/_extension.py | 10 + .../site-packages/pip/_vendor/rich/_fileno.py | 24 + .../pip/_vendor/rich/_inspect.py | 270 + .../pip/_vendor/rich/_log_render.py | 94 + .../site-packages/pip/_vendor/rich/_loop.py | 43 + .../pip/_vendor/rich/_null_file.py | 69 + .../pip/_vendor/rich/_palettes.py | 309 + .../site-packages/pip/_vendor/rich/_pick.py | 17 + .../site-packages/pip/_vendor/rich/_ratio.py | 160 + .../pip/_vendor/rich/_spinners.py | 482 + .../site-packages/pip/_vendor/rich/_stack.py | 16 + .../site-packages/pip/_vendor/rich/_timer.py | 19 + .../pip/_vendor/rich/_win32_console.py | 662 ++ .../pip/_vendor/rich/_windows.py | 72 + .../pip/_vendor/rich/_windows_renderer.py | 56 + .../site-packages/pip/_vendor/rich/_wrap.py | 56 + .../site-packages/pip/_vendor/rich/abc.py | 33 + .../site-packages/pip/_vendor/rich/align.py | 311 + .../site-packages/pip/_vendor/rich/ansi.py | 240 + .../site-packages/pip/_vendor/rich/bar.py | 94 + .../site-packages/pip/_vendor/rich/box.py | 517 + .../site-packages/pip/_vendor/rich/cells.py | 154 + .../site-packages/pip/_vendor/rich/color.py | 622 ++ .../pip/_vendor/rich/color_triplet.py | 38 + .../site-packages/pip/_vendor/rich/columns.py | 187 + .../site-packages/pip/_vendor/rich/console.py | 2633 +++++ .../pip/_vendor/rich/constrain.py | 37 + .../pip/_vendor/rich/containers.py | 167 + .../site-packages/pip/_vendor/rich/control.py | 225 + .../pip/_vendor/rich/default_styles.py | 190 + .../pip/_vendor/rich/diagnose.py | 37 + .../site-packages/pip/_vendor/rich/emoji.py | 96 + .../site-packages/pip/_vendor/rich/errors.py | 34 + .../pip/_vendor/rich/file_proxy.py | 57 + .../pip/_vendor/rich/filesize.py | 89 + .../pip/_vendor/rich/highlighter.py | 232 + .../site-packages/pip/_vendor/rich/json.py | 140 + .../site-packages/pip/_vendor/rich/jupyter.py | 101 + .../site-packages/pip/_vendor/rich/layout.py | 443 + .../site-packages/pip/_vendor/rich/live.py | 375 + .../pip/_vendor/rich/live_render.py | 113 + .../site-packages/pip/_vendor/rich/logging.py | 289 + .../site-packages/pip/_vendor/rich/markup.py | 246 + .../site-packages/pip/_vendor/rich/measure.py | 151 + .../site-packages/pip/_vendor/rich/padding.py | 141 + .../site-packages/pip/_vendor/rich/pager.py | 34 + .../site-packages/pip/_vendor/rich/palette.py | 100 + .../site-packages/pip/_vendor/rich/panel.py | 308 + .../site-packages/pip/_vendor/rich/pretty.py | 994 ++ .../pip/_vendor/rich/progress.py | 1702 ++++ .../pip/_vendor/rich/progress_bar.py | 224 + .../site-packages/pip/_vendor/rich/prompt.py | 376 + .../pip/_vendor/rich/protocol.py | 42 + .../site-packages/pip/_vendor/rich/region.py | 10 + .../site-packages/pip/_vendor/rich/repr.py | 149 + .../site-packages/pip/_vendor/rich/rule.py | 130 + .../site-packages/pip/_vendor/rich/scope.py | 86 + .../site-packages/pip/_vendor/rich/screen.py | 54 + .../site-packages/pip/_vendor/rich/segment.py | 739 ++ .../site-packages/pip/_vendor/rich/spinner.py | 137 + .../site-packages/pip/_vendor/rich/status.py | 132 + .../site-packages/pip/_vendor/rich/style.py | 796 ++ .../site-packages/pip/_vendor/rich/styled.py | 42 + .../site-packages/pip/_vendor/rich/syntax.py | 948 ++ .../site-packages/pip/_vendor/rich/table.py | 1002 ++ .../pip/_vendor/rich/terminal_theme.py | 153 + .../site-packages/pip/_vendor/rich/text.py | 1307 +++ .../site-packages/pip/_vendor/rich/theme.py | 115 + .../site-packages/pip/_vendor/rich/themes.py | 5 + .../pip/_vendor/rich/traceback.py | 756 ++ .../site-packages/pip/_vendor/rich/tree.py | 251 + .../site-packages/pip/_vendor/six.py | 998 ++ .../pip/_vendor/tenacity/__init__.py | 608 ++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 27109 bytes .../__pycache__/_asyncio.cpython-312.pyc | Bin 0 -> 4829 bytes .../__pycache__/_utils.cpython-312.pyc | Bin 0 -> 2338 bytes .../__pycache__/after.cpython-312.pyc | Bin 0 -> 1647 bytes .../__pycache__/before.cpython-312.pyc | Bin 0 -> 1487 bytes .../__pycache__/before_sleep.cpython-312.pyc | Bin 0 -> 2325 bytes .../tenacity/__pycache__/nap.cpython-312.pyc | Bin 0 -> 1435 bytes .../__pycache__/retry.cpython-312.pyc | Bin 0 -> 14304 bytes .../tenacity/__pycache__/stop.cpython-312.pyc | Bin 0 -> 5591 bytes .../__pycache__/tornadoweb.cpython-312.pyc | Bin 0 -> 2609 bytes .../tenacity/__pycache__/wait.cpython-312.pyc | Bin 0 -> 12436 bytes .../pip/_vendor/tenacity/_asyncio.py | 94 + .../pip/_vendor/tenacity/_utils.py | 76 + .../pip/_vendor/tenacity/after.py | 51 + .../pip/_vendor/tenacity/before.py | 46 + .../pip/_vendor/tenacity/before_sleep.py | 71 + .../site-packages/pip/_vendor/tenacity/nap.py | 43 + .../pip/_vendor/tenacity/retry.py | 272 + .../pip/_vendor/tenacity/stop.py | 103 + .../pip/_vendor/tenacity/tornadoweb.py | 59 + .../pip/_vendor/tenacity/wait.py | 228 + .../pip/_vendor/tomli/__init__.py | 11 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 407 bytes .../tomli/__pycache__/_parser.cpython-312.pyc | Bin 0 -> 26950 bytes .../tomli/__pycache__/_re.cpython-312.pyc | Bin 0 -> 3931 bytes .../tomli/__pycache__/_types.cpython-312.pyc | Bin 0 -> 389 bytes .../pip/_vendor/tomli/_parser.py | 691 ++ .../site-packages/pip/_vendor/tomli/_re.py | 107 + .../site-packages/pip/_vendor/tomli/_types.py | 10 + .../pip/_vendor/truststore/__init__.py | 13 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 641 bytes .../__pycache__/_api.cpython-312.pyc | Bin 0 -> 15820 bytes .../__pycache__/_macos.cpython-312.pyc | Bin 0 -> 16685 bytes .../__pycache__/_openssl.cpython-312.pyc | Bin 0 -> 2238 bytes .../_ssl_constants.cpython-312.pyc | Bin 0 -> 1122 bytes .../__pycache__/_windows.cpython-312.pyc | Bin 0 -> 15529 bytes .../pip/_vendor/truststore/_api.py | 302 + .../pip/_vendor/truststore/_macos.py | 501 + .../pip/_vendor/truststore/_openssl.py | 66 + .../pip/_vendor/truststore/_ssl_constants.py | 31 + .../pip/_vendor/truststore/_windows.py | 554 ++ .../pip/_vendor/typing_extensions.py | 3072 ++++++ .../pip/_vendor/urllib3/__init__.py | 102 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 3428 bytes .../__pycache__/_collections.cpython-312.pyc | Bin 0 -> 16511 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 241 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 20430 bytes .../connectionpool.cpython-312.pyc | Bin 0 -> 36465 bytes .../__pycache__/exceptions.cpython-312.pyc | Bin 0 -> 13516 bytes .../__pycache__/fields.cpython-312.pyc | Bin 0 -> 10436 bytes .../__pycache__/filepost.cpython-312.pyc | Bin 0 -> 4041 bytes .../__pycache__/poolmanager.cpython-312.pyc | Bin 0 -> 20823 bytes .../__pycache__/request.cpython-312.pyc | Bin 0 -> 7317 bytes .../__pycache__/response.cpython-312.pyc | Bin 0 -> 33991 bytes .../pip/_vendor/urllib3/_collections.py | 355 + .../pip/_vendor/urllib3/_version.py | 2 + .../pip/_vendor/urllib3/connection.py | 572 ++ .../pip/_vendor/urllib3/connectionpool.py | 1137 +++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 221 bytes .../_appengine_environ.cpython-312.pyc | Bin 0 -> 1871 bytes .../__pycache__/appengine.cpython-312.pyc | Bin 0 -> 11587 bytes .../__pycache__/ntlmpool.cpython-312.pyc | Bin 0 -> 5742 bytes .../__pycache__/pyopenssl.cpython-312.pyc | Bin 0 -> 24473 bytes .../securetransport.cpython-312.pyc | Bin 0 -> 35579 bytes .../contrib/__pycache__/socks.cpython-312.pyc | Bin 0 -> 7534 bytes .../urllib3/contrib/_appengine_environ.py | 36 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 238 bytes .../__pycache__/bindings.cpython-312.pyc | Bin 0 -> 17450 bytes .../__pycache__/low_level.cpython-312.pyc | Bin 0 -> 14824 bytes .../contrib/_securetransport/bindings.py | 519 + .../contrib/_securetransport/low_level.py | 397 + .../pip/_vendor/urllib3/contrib/appengine.py | 314 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 130 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 518 + .../urllib3/contrib/securetransport.py | 921 ++ .../pip/_vendor/urllib3/contrib/socks.py | 216 + .../pip/_vendor/urllib3/exceptions.py | 323 + .../pip/_vendor/urllib3/fields.py | 274 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 222 bytes .../packages/__pycache__/six.cpython-312.pyc | Bin 0 -> 41342 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 232 bytes .../__pycache__/makefile.cpython-312.pyc | Bin 0 -> 1848 bytes .../weakref_finalize.cpython-312.pyc | Bin 0 -> 7354 bytes .../urllib3/packages/backports/makefile.py | 51 + .../packages/backports/weakref_finalize.py | 155 + .../pip/_vendor/urllib3/packages/six.py | 1076 +++ .../pip/_vendor/urllib3/poolmanager.py | 556 ++ .../pip/_vendor/urllib3/request.py | 191 + .../pip/_vendor/urllib3/response.py | 879 ++ .../pip/_vendor/urllib3/util/__init__.py | 49 + .../util/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 1169 bytes .../__pycache__/connection.cpython-312.pyc | Bin 0 -> 4779 bytes .../util/__pycache__/proxy.cpython-312.pyc | Bin 0 -> 1575 bytes .../util/__pycache__/queue.cpython-312.pyc | Bin 0 -> 1375 bytes .../util/__pycache__/request.cpython-312.pyc | Bin 0 -> 4206 bytes .../util/__pycache__/response.cpython-312.pyc | Bin 0 -> 3012 bytes .../util/__pycache__/retry.cpython-312.pyc | Bin 0 -> 21741 bytes .../util/__pycache__/ssl_.cpython-312.pyc | Bin 0 -> 15126 bytes .../ssl_match_hostname.cpython-312.pyc | Bin 0 -> 5094 bytes .../__pycache__/ssltransport.cpython-312.pyc | Bin 0 -> 10795 bytes .../util/__pycache__/timeout.cpython-312.pyc | Bin 0 -> 11162 bytes .../util/__pycache__/url.cpython-312.pyc | Bin 0 -> 15818 bytes .../util/__pycache__/wait.cpython-312.pyc | Bin 0 -> 4426 bytes .../pip/_vendor/urllib3/util/connection.py | 149 + .../pip/_vendor/urllib3/util/proxy.py | 57 + .../pip/_vendor/urllib3/util/queue.py | 22 + .../pip/_vendor/urllib3/util/request.py | 137 + .../pip/_vendor/urllib3/util/response.py | 107 + .../pip/_vendor/urllib3/util/retry.py | 622 ++ .../pip/_vendor/urllib3/util/ssl_.py | 495 + .../urllib3/util/ssl_match_hostname.py | 159 + .../pip/_vendor/urllib3/util/ssltransport.py | 221 + .../pip/_vendor/urllib3/util/timeout.py | 271 + .../pip/_vendor/urllib3/util/url.py | 435 + .../pip/_vendor/urllib3/util/wait.py | 152 + .../site-packages/pip/_vendor/vendor.txt | 24 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 12024 bytes .../__pycache__/labels.cpython-312.pyc | Bin 0 -> 7155 bytes .../__pycache__/mklabels.cpython-312.pyc | Bin 0 -> 2722 bytes .../__pycache__/tests.cpython-312.pyc | Bin 0 -> 9274 bytes .../x_user_defined.cpython-312.pyc | Bin 0 -> 3318 bytes .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../lib/python3.12/site-packages/pip/py.typed | 4 + .../pymongo-4.15.3.dist-info/INSTALLER | 1 + .../pymongo-4.15.3.dist-info/METADATA | 479 + .../pymongo-4.15.3.dist-info/RECORD | 309 + .../pymongo-4.15.3.dist-info/REQUESTED | 0 .../pymongo-4.15.3.dist-info/WHEEL | 7 + .../pymongo-4.15.3.dist-info/licenses/LICENSE | 201 + .../site-packages/pymongo/__init__.py | 178 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 4488 bytes .../__pycache__/_asyncio_lock.cpython-312.pyc | Bin 0 -> 13134 bytes .../__pycache__/_asyncio_task.cpython-312.pyc | Bin 0 -> 2326 bytes .../_azure_helpers.cpython-312.pyc | Bin 0 -> 2128 bytes .../_client_bulk_shared.cpython-312.pyc | Bin 0 -> 3282 bytes .../pymongo/__pycache__/_csot.cpython-312.pyc | Bin 0 -> 8041 bytes .../__pycache__/_gcp_helpers.cpython-312.pyc | Bin 0 -> 1490 bytes .../__pycache__/_version.cpython-312.pyc | Bin 0 -> 1559 bytes .../pymongo/__pycache__/auth.cpython-312.pyc | Bin 0 -> 441 bytes .../__pycache__/auth_oidc.cpython-312.pyc | Bin 0 -> 552 bytes .../auth_oidc_shared.cpython-312.pyc | Bin 0 -> 6968 bytes .../__pycache__/auth_shared.cpython-312.pyc | Bin 0 -> 9220 bytes .../__pycache__/bulk_shared.cpython-312.pyc | Bin 0 -> 5149 bytes .../__pycache__/change_stream.cpython-312.pyc | Bin 0 -> 539 bytes .../client_options.cpython-312.pyc | Bin 0 -> 16085 bytes .../client_session.cpython-312.pyc | Bin 0 -> 512 bytes .../__pycache__/collation.cpython-312.pyc | Bin 0 -> 8583 bytes .../__pycache__/collection.cpython-312.pyc | Bin 0 -> 484 bytes .../command_cursor.cpython-312.pyc | Bin 0 -> 503 bytes .../__pycache__/common.cpython-312.pyc | Bin 0 -> 42000 bytes .../compression_support.cpython-312.pyc | Bin 0 -> 6473 bytes .../__pycache__/cursor.cpython-312.pyc | Bin 0 -> 509 bytes .../__pycache__/cursor_shared.cpython-312.pyc | Bin 0 -> 1295 bytes .../__pycache__/daemon.cpython-312.pyc | Bin 0 -> 4570 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 451 bytes .../database_shared.cpython-312.pyc | Bin 0 -> 996 bytes .../__pycache__/driver_info.cpython-312.pyc | Bin 0 -> 1944 bytes .../__pycache__/encryption.cpython-312.pyc | Bin 0 -> 515 bytes .../encryption_options.cpython-312.pyc | Bin 0 -> 18082 bytes .../__pycache__/errors.cpython-312.pyc | Bin 0 -> 20494 bytes .../__pycache__/event_loggers.cpython-312.pyc | Bin 0 -> 13621 bytes .../pymongo/__pycache__/hello.cpython-312.pyc | Bin 0 -> 11317 bytes .../helpers_shared.cpython-312.pyc | Bin 0 -> 11882 bytes .../pymongo/__pycache__/lock.cpython-312.pyc | Bin 0 -> 3228 bytes .../__pycache__/logger.cpython-312.pyc | Bin 0 -> 9383 bytes .../max_staleness_selectors.cpython-312.pyc | Bin 0 -> 4009 bytes .../__pycache__/message.cpython-312.pyc | Bin 0 -> 66365 bytes .../__pycache__/mongo_client.cpython-312.pyc | Bin 0 -> 466 bytes .../__pycache__/monitoring.cpython-312.pyc | Bin 0 -> 80220 bytes .../__pycache__/network_layer.cpython-312.pyc | Bin 0 -> 35720 bytes .../__pycache__/ocsp_cache.cpython-312.pyc | Bin 0 -> 4862 bytes .../__pycache__/ocsp_support.cpython-312.pyc | Bin 0 -> 17403 bytes .../__pycache__/operations.cpython-312.pyc | Bin 0 -> 35130 bytes .../periodic_executor.cpython-312.pyc | Bin 0 -> 11536 bytes .../pymongo/__pycache__/pool.cpython-312.pyc | Bin 0 -> 442 bytes .../__pycache__/pool_options.cpython-312.pyc | Bin 0 -> 21321 bytes .../__pycache__/pool_shared.cpython-312.pyc | Bin 0 -> 19072 bytes .../pyopenssl_context.cpython-312.pyc | Bin 0 -> 18544 bytes .../__pycache__/read_concern.cpython-312.pyc | Bin 0 -> 3139 bytes .../read_preferences.cpython-312.pyc | Bin 0 -> 26566 bytes .../__pycache__/response.cpython-312.pyc | Bin 0 -> 5204 bytes .../__pycache__/results.cpython-312.pyc | Bin 0 -> 18762 bytes .../__pycache__/saslprep.cpython-312.pyc | Bin 0 -> 3466 bytes .../__pycache__/server_api.cpython-312.pyc | Bin 0 -> 6480 bytes .../server_description.cpython-312.pyc | Bin 0 -> 13839 bytes .../server_selectors.cpython-312.pyc | Bin 0 -> 8039 bytes .../__pycache__/server_type.cpython-312.pyc | Bin 0 -> 843 bytes .../socket_checker.cpython-312.pyc | Bin 0 -> 3713 bytes .../__pycache__/ssl_context.cpython-312.pyc | Bin 0 -> 1161 bytes .../__pycache__/ssl_support.cpython-312.pyc | Bin 0 -> 5100 bytes .../topology_description.cpython-312.pyc | Bin 0 -> 29110 bytes .../__pycache__/typings.cpython-312.pyc | Bin 0 -> 2225 bytes .../__pycache__/uri_parser.cpython-312.pyc | Bin 0 -> 1029 bytes .../uri_parser_shared.cpython-312.pyc | Bin 0 -> 21966 bytes .../__pycache__/write_concern.cpython-312.pyc | Bin 0 -> 6474 bytes .../site-packages/pymongo/_asyncio_lock.py | 309 + .../site-packages/pymongo/_asyncio_task.py | 49 + .../site-packages/pymongo/_azure_helpers.py | 57 + .../pymongo/_client_bulk_shared.py | 79 + .../_cmessage.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 406864 bytes .../_cmessage.cpython-311-x86_64-linux-gnu.so | Bin 0 -> 410552 bytes .../_cmessage.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 448952 bytes .../_cmessage.cpython-39-x86_64-linux-gnu.so | Bin 0 -> 404616 bytes .../site-packages/pymongo/_cmessagemodule.c | 1049 ++ .../python3.12/site-packages/pymongo/_csot.py | 167 + .../site-packages/pymongo/_gcp_helpers.py | 40 + .../site-packages/pymongo/_version.py | 43 + .../pymongo/asynchronous/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 214 bytes .../__pycache__/aggregation.cpython-312.pyc | Bin 0 -> 11023 bytes .../__pycache__/auth.cpython-312.pyc | Bin 0 -> 18846 bytes .../__pycache__/auth_aws.cpython-312.pyc | Bin 0 -> 3986 bytes .../__pycache__/auth_oidc.cpython-312.pyc | Bin 0 -> 13604 bytes .../__pycache__/bulk.cpython-312.pyc | Bin 0 -> 30038 bytes .../__pycache__/change_stream.cpython-312.pyc | Bin 0 -> 22230 bytes .../__pycache__/client_bulk.cpython-312.pyc | Bin 0 -> 30549 bytes .../client_session.cpython-312.pyc | Bin 0 -> 54332 bytes .../__pycache__/collection.cpython-312.pyc | Bin 0 -> 160079 bytes .../command_cursor.cpython-312.pyc | Bin 0 -> 21409 bytes .../__pycache__/cursor.cpython-312.pyc | Bin 0 -> 58530 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 65383 bytes .../__pycache__/encryption.cpython-312.pyc | Bin 0 -> 55921 bytes .../__pycache__/helpers.cpython-312.pyc | Bin 0 -> 3293 bytes .../__pycache__/mongo_client.cpython-312.pyc | Bin 0 -> 142931 bytes .../__pycache__/monitor.cpython-312.pyc | Bin 0 -> 26342 bytes .../__pycache__/network.cpython-312.pyc | Bin 0 -> 11450 bytes .../__pycache__/pool.cpython-312.pyc | Bin 0 -> 75648 bytes .../__pycache__/server.cpython-312.pyc | Bin 0 -> 15310 bytes .../__pycache__/settings.cpython-312.pyc | Bin 0 -> 8403 bytes .../__pycache__/srv_resolver.cpython-312.pyc | Bin 0 -> 7469 bytes .../__pycache__/topology.cpython-312.pyc | Bin 0 -> 52288 bytes .../__pycache__/uri_parser.cpython-312.pyc | Bin 0 -> 6613 bytes .../pymongo/asynchronous/aggregation.py | 257 + .../pymongo/asynchronous/auth.py | 455 + .../pymongo/asynchronous/auth_aws.py | 100 + .../pymongo/asynchronous/auth_oidc.py | 305 + .../pymongo/asynchronous/bulk.py | 753 ++ .../pymongo/asynchronous/change_stream.py | 499 + .../pymongo/asynchronous/client_bulk.py | 755 ++ .../pymongo/asynchronous/client_session.py | 1183 +++ .../pymongo/asynchronous/collection.py | 3653 +++++++ .../pymongo/asynchronous/command_cursor.py | 472 + .../pymongo/asynchronous/cursor.py | 1375 +++ .../pymongo/asynchronous/database.py | 1468 +++ .../pymongo/asynchronous/encryption.py | 1276 +++ .../pymongo/asynchronous/helpers.py | 102 + .../pymongo/asynchronous/mongo_client.py | 2980 ++++++ .../pymongo/asynchronous/monitor.py | 545 ++ .../pymongo/asynchronous/network.py | 298 + .../pymongo/asynchronous/pool.py | 1479 +++ .../pymongo/asynchronous/server.py | 383 + .../pymongo/asynchronous/settings.py | 175 + .../pymongo/asynchronous/srv_resolver.py | 164 + .../pymongo/asynchronous/topology.py | 1127 +++ .../pymongo/asynchronous/uri_parser.py | 193 + .../python3.12/site-packages/pymongo/auth.py | 22 + .../site-packages/pymongo/auth_oidc.py | 23 + .../site-packages/pymongo/auth_oidc_shared.py | 132 + .../site-packages/pymongo/auth_shared.py | 254 + .../site-packages/pymongo/bulk_shared.py | 131 + .../site-packages/pymongo/change_stream.py | 22 + .../site-packages/pymongo/client_options.py | 348 + .../site-packages/pymongo/client_session.py | 22 + .../site-packages/pymongo/collation.py | 226 + .../site-packages/pymongo/collection.py | 25 + .../site-packages/pymongo/command_cursor.py | 22 + .../site-packages/pymongo/common.py | 1094 +++ .../pymongo/compression_support.py | 175 + .../site-packages/pymongo/cursor.py | 23 + .../site-packages/pymongo/cursor_shared.py | 94 + .../site-packages/pymongo/daemon.py | 148 + .../site-packages/pymongo/database.py | 22 + .../site-packages/pymongo/database_shared.py | 34 + .../site-packages/pymongo/driver_info.py | 45 + .../site-packages/pymongo/encryption.py | 22 + .../pymongo/encryption_options.py | 379 + .../site-packages/pymongo/errors.py | 436 + .../site-packages/pymongo/event_loggers.py | 226 + .../python3.12/site-packages/pymongo/hello.py | 224 + .../site-packages/pymongo/helpers_shared.py | 364 + .../python3.12/site-packages/pymongo/lock.py | 92 + .../site-packages/pymongo/logger.py | 189 + .../pymongo/max_staleness_selectors.py | 124 + .../site-packages/pymongo/message.py | 1902 ++++ .../site-packages/pymongo/mongo_client.py | 22 + .../site-packages/pymongo/monitoring.py | 1909 ++++ .../site-packages/pymongo/network_layer.py | 786 ++ .../site-packages/pymongo/ocsp_cache.py | 131 + .../site-packages/pymongo/ocsp_support.py | 438 + .../site-packages/pymongo/operations.py | 853 ++ .../pymongo/periodic_executor.py | 296 + .../python3.12/site-packages/pymongo/pool.py | 22 + .../site-packages/pymongo/pool_options.py | 537 + .../site-packages/pymongo/pool_shared.py | 523 + .../python3.12/site-packages/pymongo/py.typed | 2 + .../pymongo/pyopenssl_context.py | 428 + .../site-packages/pymongo/read_concern.py | 79 + .../site-packages/pymongo/read_preferences.py | 637 ++ .../site-packages/pymongo/response.py | 130 + .../site-packages/pymongo/results.py | 367 + .../site-packages/pymongo/saslprep.py | 116 + .../site-packages/pymongo/server_api.py | 173 + .../pymongo/server_description.py | 302 + .../site-packages/pymongo/server_selectors.py | 174 + .../site-packages/pymongo/server_type.py | 33 + .../site-packages/pymongo/socket_checker.py | 105 + .../site-packages/pymongo/ssl_context.py | 42 + .../site-packages/pymongo/ssl_support.py | 146 + .../pymongo/synchronous/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 213 bytes .../__pycache__/aggregation.cpython-312.pyc | Bin 0 -> 10789 bytes .../__pycache__/auth.cpython-312.pyc | Bin 0 -> 17855 bytes .../__pycache__/auth_aws.cpython-312.pyc | Bin 0 -> 3838 bytes .../__pycache__/auth_oidc.cpython-312.pyc | Bin 0 -> 12200 bytes .../__pycache__/bulk.cpython-312.pyc | Bin 0 -> 28584 bytes .../__pycache__/change_stream.cpython-312.pyc | Bin 0 -> 20849 bytes .../__pycache__/client_bulk.cpython-312.pyc | Bin 0 -> 29069 bytes .../client_session.cpython-312.pyc | Bin 0 -> 52404 bytes .../__pycache__/collection.cpython-312.pyc | Bin 0 -> 151605 bytes .../command_cursor.cpython-312.pyc | Bin 0 -> 20180 bytes .../__pycache__/cursor.cpython-312.pyc | Bin 0 -> 56369 bytes .../__pycache__/database.cpython-312.pyc | Bin 0 -> 61430 bytes .../__pycache__/encryption.cpython-312.pyc | Bin 0 -> 53299 bytes .../__pycache__/helpers.cpython-312.pyc | Bin 0 -> 3016 bytes .../__pycache__/mongo_client.cpython-312.pyc | Bin 0 -> 134685 bytes .../__pycache__/monitor.cpython-312.pyc | Bin 0 -> 23768 bytes .../__pycache__/network.cpython-312.pyc | Bin 0 -> 11119 bytes .../__pycache__/pool.cpython-312.pyc | Bin 0 -> 67857 bytes .../__pycache__/server.cpython-312.pyc | Bin 0 -> 14694 bytes .../__pycache__/settings.cpython-312.pyc | Bin 0 -> 8400 bytes .../__pycache__/srv_resolver.cpython-312.pyc | Bin 0 -> 7049 bytes .../__pycache__/topology.cpython-312.pyc | Bin 0 -> 48210 bytes .../__pycache__/uri_parser.cpython-312.pyc | Bin 0 -> 6440 bytes .../pymongo/synchronous/aggregation.py | 257 + .../site-packages/pymongo/synchronous/auth.py | 450 + .../pymongo/synchronous/auth_aws.py | 100 + .../pymongo/synchronous/auth_oidc.py | 303 + .../site-packages/pymongo/synchronous/bulk.py | 751 ++ .../pymongo/synchronous/change_stream.py | 497 + .../pymongo/synchronous/client_bulk.py | 753 ++ .../pymongo/synchronous/client_session.py | 1180 +++ .../pymongo/synchronous/collection.py | 3646 +++++++ .../pymongo/synchronous/command_cursor.py | 472 + .../pymongo/synchronous/cursor.py | 1371 +++ .../pymongo/synchronous/database.py | 1461 +++ .../pymongo/synchronous/encryption.py | 1269 +++ .../pymongo/synchronous/helpers.py | 102 + .../pymongo/synchronous/mongo_client.py | 2970 ++++++ .../pymongo/synchronous/monitor.py | 543 ++ .../pymongo/synchronous/network.py | 298 + .../site-packages/pymongo/synchronous/pool.py | 1475 +++ .../pymongo/synchronous/server.py | 383 + .../pymongo/synchronous/settings.py | 175 + .../pymongo/synchronous/srv_resolver.py | 164 + .../pymongo/synchronous/topology.py | 1125 +++ .../pymongo/synchronous/uri_parser.py | 193 + .../pymongo/topology_description.py | 700 ++ .../site-packages/pymongo/typings.py | 78 + .../site-packages/pymongo/uri_parser.py | 44 + .../pymongo/uri_parser_shared.py | 614 ++ .../site-packages/pymongo/write_concern.py | 144 + .../pyyaml-6.0.3.dist-info/INSTALLER | 1 + .../pyyaml-6.0.3.dist-info/METADATA | 59 + .../pyyaml-6.0.3.dist-info/RECORD | 44 + .../pyyaml-6.0.3.dist-info/REQUESTED | 0 .../pyyaml-6.0.3.dist-info/WHEEL | 7 + .../pyyaml-6.0.3.dist-info/licenses/LICENSE | 20 + .../pyyaml-6.0.3.dist-info/top_level.txt | 2 + .../python3.12/site-packages/yaml/__init__.py | 390 + .../yaml/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 15635 bytes .../yaml/__pycache__/composer.cpython-312.pyc | Bin 0 -> 6544 bytes .../__pycache__/constructor.cpython-312.pyc | Bin 0 -> 34937 bytes .../yaml/__pycache__/cyaml.cpython-312.pyc | Bin 0 -> 4645 bytes .../yaml/__pycache__/dumper.cpython-312.pyc | Bin 0 -> 2481 bytes .../yaml/__pycache__/emitter.cpython-312.pyc | Bin 0 -> 50185 bytes .../yaml/__pycache__/error.cpython-312.pyc | Bin 0 -> 4287 bytes .../yaml/__pycache__/events.cpython-312.pyc | Bin 0 -> 4729 bytes .../yaml/__pycache__/loader.cpython-312.pyc | Bin 0 -> 3543 bytes .../yaml/__pycache__/nodes.cpython-312.pyc | Bin 0 -> 2228 bytes .../yaml/__pycache__/parser.cpython-312.pyc | Bin 0 -> 24733 bytes .../yaml/__pycache__/reader.cpython-312.pyc | Bin 0 -> 8863 bytes .../__pycache__/representer.cpython-312.pyc | Bin 0 -> 16936 bytes .../yaml/__pycache__/resolver.cpython-312.pyc | Bin 0 -> 9080 bytes .../yaml/__pycache__/scanner.cpython-312.pyc | Bin 0 -> 49871 bytes .../__pycache__/serializer.cpython-312.pyc | Bin 0 -> 6218 bytes .../yaml/__pycache__/tokens.cpython-312.pyc | Bin 0 -> 5798 bytes .../_yaml.cpython-312-x86_64-linux-gnu.so | Bin 0 -> 2679264 bytes .../python3.12/site-packages/yaml/composer.py | 139 + .../site-packages/yaml/constructor.py | 748 ++ .../python3.12/site-packages/yaml/cyaml.py | 101 + .../python3.12/site-packages/yaml/dumper.py | 62 + .../python3.12/site-packages/yaml/emitter.py | 1137 +++ .../python3.12/site-packages/yaml/error.py | 75 + .../python3.12/site-packages/yaml/events.py | 86 + .../python3.12/site-packages/yaml/loader.py | 63 + .../python3.12/site-packages/yaml/nodes.py | 49 + .../python3.12/site-packages/yaml/parser.py | 589 ++ .../python3.12/site-packages/yaml/reader.py | 185 + .../site-packages/yaml/representer.py | 389 + .../python3.12/site-packages/yaml/resolver.py | 227 + .../python3.12/site-packages/yaml/scanner.py | 1435 +++ .../site-packages/yaml/serializer.py | 111 + .../python3.12/site-packages/yaml/tokens.py | 104 + .venv/lib64 | 1 + .venv/pyvenv.cfg | 5 + main.py | 15 +- 1672 files changed, 304899 insertions(+), 2 deletions(-) create mode 100644 .venv/bin/Activate.ps1 create mode 100644 .venv/bin/activate create mode 100644 .venv/bin/activate.csh create mode 100644 .venv/bin/activate.fish create mode 100755 .venv/bin/pip create mode 100755 .venv/bin/pip3 create mode 100755 .venv/bin/pip3.12 create mode 120000 .venv/bin/python create mode 120000 .venv/bin/python3 create mode 120000 .venv/bin/python3.12 create mode 100644 .venv/lib/python3.12/site-packages/_yaml/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/_helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/binary.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/code.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/codec_options.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/datetime_ms.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/dbref.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/decimal128.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/errors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/int64.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/json_util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/max_key.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/min_key.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/objectid.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/raw_bson.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/regex.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/son.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/timestamp.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/typings.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/bson/__pycache__/tz_util.cpython-312.pyc create mode 100755 .venv/lib/python3.12/site-packages/bson/_cbson.cpython-310-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/bson/_cbson.cpython-311-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/bson/_cbson.cpython-312-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/bson/_cbson.cpython-39-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/bson/_cbsonmodule.c create mode 100644 .venv/lib/python3.12/site-packages/bson/_cbsonmodule.h create mode 100644 .venv/lib/python3.12/site-packages/bson/_helpers.py create mode 100644 .venv/lib/python3.12/site-packages/bson/binary.py create mode 100644 .venv/lib/python3.12/site-packages/bson/bson-endian.h create mode 100644 .venv/lib/python3.12/site-packages/bson/buffer.c create mode 100644 .venv/lib/python3.12/site-packages/bson/buffer.h create mode 100644 .venv/lib/python3.12/site-packages/bson/code.py create mode 100644 .venv/lib/python3.12/site-packages/bson/codec_options.py create mode 100644 .venv/lib/python3.12/site-packages/bson/datetime_ms.py create mode 100644 .venv/lib/python3.12/site-packages/bson/dbref.py create mode 100644 .venv/lib/python3.12/site-packages/bson/decimal128.py create mode 100644 .venv/lib/python3.12/site-packages/bson/errors.py create mode 100644 .venv/lib/python3.12/site-packages/bson/int64.py create mode 100644 .venv/lib/python3.12/site-packages/bson/json_util.py create mode 100644 .venv/lib/python3.12/site-packages/bson/max_key.py create mode 100644 .venv/lib/python3.12/site-packages/bson/min_key.py create mode 100644 .venv/lib/python3.12/site-packages/bson/objectid.py create mode 100644 .venv/lib/python3.12/site-packages/bson/py.typed create mode 100644 .venv/lib/python3.12/site-packages/bson/raw_bson.py create mode 100644 .venv/lib/python3.12/site-packages/bson/regex.py create mode 100644 .venv/lib/python3.12/site-packages/bson/son.py create mode 100644 .venv/lib/python3.12/site-packages/bson/time64.c create mode 100644 .venv/lib/python3.12/site-packages/bson/time64.h create mode 100644 .venv/lib/python3.12/site-packages/bson/time64_config.h create mode 100644 .venv/lib/python3.12/site-packages/bson/time64_limits.h create mode 100644 .venv/lib/python3.12/site-packages/bson/timestamp.py create mode 100644 .venv/lib/python3.12/site-packages/bson/typings.py create mode 100644 .venv/lib/python3.12/site-packages/bson/tz_util.py create mode 100644 .venv/lib/python3.12/site-packages/dns/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_asyncbackend.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_asyncio_backend.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_ddr.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_features.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_immutable_ctx.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_no_ssl.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_tls_util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/_trio_backend.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/asyncbackend.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/asyncquery.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/asyncresolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/btree.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/btreezone.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/dnssec.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/dnssectypes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/e164.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/edns.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/entropy.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/enum.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/exception.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/flags.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/grange.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/immutable.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/inet.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/ipv4.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/ipv6.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/message.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/name.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/namedict.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/nameserver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/node.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/opcode.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/query.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rcode.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdata.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdataclass.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdataset.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rdatatype.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/renderer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/resolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/reversename.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/rrset.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/serial.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/set.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/tokenizer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/transaction.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/tsig.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/tsigkeyring.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/ttl.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/update.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/versioned.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/win32util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/wire.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/xfr.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/zone.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/zonefile.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/__pycache__/zonetypes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/_asyncbackend.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_asyncio_backend.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_ddr.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_features.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_immutable_ctx.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_no_ssl.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_tls_util.py create mode 100644 .venv/lib/python3.12/site-packages/dns/_trio_backend.py create mode 100644 .venv/lib/python3.12/site-packages/dns/asyncbackend.py create mode 100644 .venv/lib/python3.12/site-packages/dns/asyncquery.py create mode 100644 .venv/lib/python3.12/site-packages/dns/asyncresolver.py create mode 100644 .venv/lib/python3.12/site-packages/dns/btree.py create mode 100644 .venv/lib/python3.12/site-packages/dns/btreezone.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssec.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/base.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/dsa.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/ecdsa.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/eddsa.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/base.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/cryptography.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/dsa.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/ecdsa.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/eddsa.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssecalgs/rsa.py create mode 100644 .venv/lib/python3.12/site-packages/dns/dnssectypes.py create mode 100644 .venv/lib/python3.12/site-packages/dns/e164.py create mode 100644 .venv/lib/python3.12/site-packages/dns/edns.py create mode 100644 .venv/lib/python3.12/site-packages/dns/entropy.py create mode 100644 .venv/lib/python3.12/site-packages/dns/enum.py create mode 100644 .venv/lib/python3.12/site-packages/dns/exception.py create mode 100644 .venv/lib/python3.12/site-packages/dns/flags.py create mode 100644 .venv/lib/python3.12/site-packages/dns/grange.py create mode 100644 .venv/lib/python3.12/site-packages/dns/immutable.py create mode 100644 .venv/lib/python3.12/site-packages/dns/inet.py create mode 100644 .venv/lib/python3.12/site-packages/dns/ipv4.py create mode 100644 .venv/lib/python3.12/site-packages/dns/ipv6.py create mode 100644 .venv/lib/python3.12/site-packages/dns/message.py create mode 100644 .venv/lib/python3.12/site-packages/dns/name.py create mode 100644 .venv/lib/python3.12/site-packages/dns/namedict.py create mode 100644 .venv/lib/python3.12/site-packages/dns/nameserver.py create mode 100644 .venv/lib/python3.12/site-packages/dns/node.py create mode 100644 .venv/lib/python3.12/site-packages/dns/opcode.py create mode 100644 .venv/lib/python3.12/site-packages/dns/py.typed create mode 100644 .venv/lib/python3.12/site-packages/dns/query.py create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_asyncio.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_common.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_sync.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/__pycache__/_trio.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_asyncio.py create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_common.py create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_sync.py create mode 100644 .venv/lib/python3.12/site-packages/dns/quic/_trio.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rcode.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdata.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdataclass.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdataset.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdatatype.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DSYNC.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AFSDB.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AVC.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CAA.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDNSKEY.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CDS.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CERT.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CNAME.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DLV.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNSKEY.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DS.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DSYNC.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/EUI48.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/EUI64.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/GPOS.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HINFO.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/HIP.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ISDN.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L32.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/L64.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/MX.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NID.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NINFO.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NSEC3PARAM.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPENPGPKEY.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/OPT.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/PTR.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RESINFO.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RP.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RRSIG.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/RT.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SMIMEA.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TLSA.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/URI.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/WALLET.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/X25.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/ZONEMD.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/A.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/APL.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/DHCID.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/KX.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NAPTR.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NSAP.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/NSAP_PTR.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/PX.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SRV.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/SVCB.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/WKS.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dnskeybase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/dsbase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/euibase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/nsbase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/svcbbase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/tlsabase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rdtypes/util.py create mode 100644 .venv/lib/python3.12/site-packages/dns/renderer.py create mode 100644 .venv/lib/python3.12/site-packages/dns/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/dns/reversename.py create mode 100644 .venv/lib/python3.12/site-packages/dns/rrset.py create mode 100644 .venv/lib/python3.12/site-packages/dns/serial.py create mode 100644 .venv/lib/python3.12/site-packages/dns/set.py create mode 100644 .venv/lib/python3.12/site-packages/dns/tokenizer.py create mode 100644 .venv/lib/python3.12/site-packages/dns/transaction.py create mode 100644 .venv/lib/python3.12/site-packages/dns/tsig.py create mode 100644 .venv/lib/python3.12/site-packages/dns/tsigkeyring.py create mode 100644 .venv/lib/python3.12/site-packages/dns/ttl.py create mode 100644 .venv/lib/python3.12/site-packages/dns/update.py create mode 100644 .venv/lib/python3.12/site-packages/dns/version.py create mode 100644 .venv/lib/python3.12/site-packages/dns/versioned.py create mode 100644 .venv/lib/python3.12/site-packages/dns/win32util.py create mode 100644 .venv/lib/python3.12/site-packages/dns/wire.py create mode 100644 .venv/lib/python3.12/site-packages/dns/xfr.py create mode 100644 .venv/lib/python3.12/site-packages/dns/zone.py create mode 100644 .venv/lib/python3.12/site-packages/dns/zonefile.py create mode 100644 .venv/lib/python3.12/site-packages/dns/zonetypes.py create mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/gridfs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/__pycache__/errors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/__pycache__/grid_file.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/__pycache__/grid_file_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/asynchronous/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/asynchronous/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/asynchronous/__pycache__/grid_file.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/asynchronous/grid_file.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/errors.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/grid_file.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/grid_file_shared.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/py.typed create mode 100644 .venv/lib/python3.12/site-packages/gridfs/synchronous/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/gridfs/synchronous/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/synchronous/__pycache__/grid_file.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/gridfs/synchronous/grid_file.py create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/LICENSE.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/entry_points.txt create mode 100644 .venv/lib/python3.12/site-packages/pip-24.0.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/__pip-runner__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/__pycache__/__pip-runner__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/build_env.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/main.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/pyproject.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/build_env.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/main.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/check.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/completion.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/debug.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/download.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/hash.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/help.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/install.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/list.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/search.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/check.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/download.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/help.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/index.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/install.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/list.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/search.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/show.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/configuration.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/base.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/collector.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/collector.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/index/sources.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/base.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/locations/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/main.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/_json.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/base.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/index.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/installation_report.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/link.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/scheme.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/candidate.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/index.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/link.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/auth.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/download.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/session.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/utils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/download.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/session.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/check.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/check.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/pyproject.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_install.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_set.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/base.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/_log.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compat.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/misc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/models.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/urls.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/encoding.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/models.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/git.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/six.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/johabfreq.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/johabprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/macromanprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/resultdict.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/utf1632prober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachinedict.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/enums.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabfreq.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langrussianmodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/macromanprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__pycache__/languages.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/languages.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/resultdict.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf1632prober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/chardet/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/utils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansi_test.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/initialise_test.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/isatty_test.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/winterm_test.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/win32.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/database.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/index.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/locators.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/markers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/resources.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/util.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/__pycache__/distro.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/distro/distro.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/core.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/codec.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/core.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/intranges.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/package_data.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/ext.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_manylinux.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_musllinux.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/markers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/tags.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/packaging/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/android.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/api.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/macos.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/unix.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/platformdirs/windows.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/cmdline.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/console.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/style.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/token.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/__pycache__/util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/cmdline.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/console.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/filter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/groff.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/bbcode.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/groff.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/html.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/img.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/irc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/latex.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/other.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/pangomarkup.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/rtf.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/svg.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/formatters/terminal256.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/_mapping.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/lexers/python.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/modeline.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/plugin.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/regexopt.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/scanner.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/sphinxext.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/style.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/token.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/unistring.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pygments/util.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/actions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/common.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/core.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/exceptions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/results.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/testing.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/unicode.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/__pycache__/util.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/actions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/common.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/core.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/diagram/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/results.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/testing.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/unicode.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyparsing/util.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_impl.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/api.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/help.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/models.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/__version__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/adapters.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/api.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/certs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/compat.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/cookies.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/help.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/hooks.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/models.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/packages.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/sessions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/structures.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/requests/utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/compat/collections_abc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/providers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/reporters.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/resolvers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/resolvelib/structs.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__main__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/abc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/align.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/bar.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/box.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/cells.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/columns.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/console.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/containers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/control.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/errors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/json.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/layout.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/logging.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/markup.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/measure.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/padding.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pager.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/palette.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/panel.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/region.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/repr.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/rule.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/scope.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/screen.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/segment.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/status.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/style.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/styled.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/table.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/text.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/theme.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/themes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/__pycache__/tree.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_cell_widths.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_codes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_emoji_replace.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_extension.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_fileno.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_inspect.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_log_render.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_loop.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_null_file.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_palettes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_pick.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_ratio.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_spinners.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_stack.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_timer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_win32_console.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_windows_renderer.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/_wrap.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/abc.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/align.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/ansi.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/bar.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/box.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/cells.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/color.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/color_triplet.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/columns.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/console.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/constrain.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/containers.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/control.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/default_styles.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/diagnose.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/emoji.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/errors.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/file_proxy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/filesize.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/highlighter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/json.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/jupyter.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/layout.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/live.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/live_render.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/logging.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/markup.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/measure.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/padding.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/pager.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/palette.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/panel.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/pretty.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/progress.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/progress_bar.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/prompt.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/protocol.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/region.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/repr.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/rule.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/scope.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/screen.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/segment.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/spinner.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/status.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/style.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/styled.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/syntax.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/table.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/terminal_theme.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/text.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/theme.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/themes.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/traceback.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/rich/tree.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/six.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/_asyncio.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/_utils.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/after.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/before.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/before_sleep.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/nap.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/retry.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/stop.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/__pycache__/wait.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_asyncio.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/_utils.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/after.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/before_sleep.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/nap.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/retry.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/stop.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/tornadoweb.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tenacity/wait.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/_re.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/tomli/_types.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_api.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_macos.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_openssl.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_ssl_constants.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/truststore/_windows.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/typing_extensions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/request.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/response.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/proxy.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/ssltransport.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/vendor.txt create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 .venv/lib/python3.12/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 .venv/lib/python3.12/site-packages/pip/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pymongo-4.15.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pymongo-4.15.3.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pymongo-4.15.3.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pymongo-4.15.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pymongo-4.15.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pymongo-4.15.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_asyncio_lock.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_asyncio_task.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_azure_helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_client_bulk_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_csot.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_gcp_helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/_version.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/auth.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/auth_oidc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/auth_oidc_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/auth_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/bulk_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/change_stream.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/client_options.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/client_session.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/collation.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/collection.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/command_cursor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/common.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/compression_support.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/cursor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/cursor_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/daemon.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/database.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/database_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/driver_info.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/encryption.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/encryption_options.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/errors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/event_loggers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/hello.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/helpers_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/lock.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/logger.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/max_staleness_selectors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/message.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/mongo_client.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/monitoring.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/network_layer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/ocsp_cache.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/ocsp_support.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/operations.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/periodic_executor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/pool.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/pool_options.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/pool_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/pyopenssl_context.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/read_concern.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/read_preferences.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/response.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/results.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/saslprep.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/server_api.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/server_description.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/server_selectors.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/server_type.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/socket_checker.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/ssl_context.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/ssl_support.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/topology_description.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/typings.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/uri_parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/uri_parser_shared.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/__pycache__/write_concern.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_asyncio_lock.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_asyncio_task.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_azure_helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_client_bulk_shared.py create mode 100755 .venv/lib/python3.12/site-packages/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/pymongo/_cmessage.cpython-311-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/pymongo/_cmessage.cpython-312-x86_64-linux-gnu.so create mode 100755 .venv/lib/python3.12/site-packages/pymongo/_cmessage.cpython-39-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_cmessagemodule.c create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_csot.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_gcp_helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/_version.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/aggregation.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/auth.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/auth_aws.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/auth_oidc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/bulk.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/change_stream.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/client_bulk.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/client_session.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/collection.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/command_cursor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/cursor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/database.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/encryption.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/mongo_client.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/monitor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/network.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/pool.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/server.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/settings.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/srv_resolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/topology.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/__pycache__/uri_parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/aggregation.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/auth_aws.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/auth_oidc.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/bulk.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/change_stream.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/client_bulk.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/client_session.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/collection.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/command_cursor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/cursor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/database.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/encryption.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/mongo_client.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/monitor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/network.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/pool.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/server.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/settings.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/srv_resolver.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/topology.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/asynchronous/uri_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/auth_oidc.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/auth_oidc_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/auth_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/bulk_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/change_stream.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/client_options.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/client_session.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/collation.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/collection.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/command_cursor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/common.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/compression_support.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/cursor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/cursor_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/daemon.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/database.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/database_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/driver_info.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/encryption.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/encryption_options.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/errors.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/event_loggers.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/hello.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/helpers_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/lock.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/logger.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/max_staleness_selectors.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/message.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/mongo_client.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/monitoring.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/network_layer.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/ocsp_cache.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/ocsp_support.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/operations.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/periodic_executor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/pool.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/pool_options.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/pool_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/py.typed create mode 100644 .venv/lib/python3.12/site-packages/pymongo/pyopenssl_context.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/read_concern.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/read_preferences.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/response.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/results.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/saslprep.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/server_api.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/server_description.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/server_selectors.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/server_type.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/socket_checker.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/ssl_context.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/ssl_support.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/aggregation.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/auth.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/auth_aws.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/auth_oidc.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/bulk.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/change_stream.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/client_bulk.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/client_session.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/collection.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/command_cursor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/cursor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/database.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/encryption.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/helpers.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/mongo_client.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/monitor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/network.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/pool.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/server.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/settings.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/srv_resolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/topology.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/__pycache__/uri_parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/aggregation.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/auth.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/auth_aws.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/auth_oidc.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/bulk.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/change_stream.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/client_bulk.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/client_session.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/collection.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/command_cursor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/cursor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/database.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/encryption.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/helpers.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/mongo_client.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/monitor.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/network.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/pool.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/server.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/settings.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/srv_resolver.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/topology.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/synchronous/uri_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/topology_description.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/typings.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/uri_parser.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/uri_parser_shared.py create mode 100644 .venv/lib/python3.12/site-packages/pymongo/write_concern.py create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/INSTALLER create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/METADATA create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/REQUESTED create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/WHEEL create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE create mode 100644 .venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/top_level.txt create mode 100644 .venv/lib/python3.12/site-packages/yaml/__init__.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/__init__.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/composer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/constructor.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/cyaml.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/dumper.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/emitter.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/error.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/events.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/loader.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/nodes.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/parser.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/reader.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/representer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/resolver.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/scanner.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/serializer.cpython-312.pyc create mode 100644 .venv/lib/python3.12/site-packages/yaml/__pycache__/tokens.cpython-312.pyc create mode 100755 .venv/lib/python3.12/site-packages/yaml/_yaml.cpython-312-x86_64-linux-gnu.so create mode 100644 .venv/lib/python3.12/site-packages/yaml/composer.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/constructor.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/cyaml.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/dumper.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/emitter.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/error.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/events.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/loader.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/nodes.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/parser.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/reader.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/representer.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/resolver.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/scanner.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/serializer.py create mode 100644 .venv/lib/python3.12/site-packages/yaml/tokens.py create mode 120000 .venv/lib64 create mode 100644 .venv/pyvenv.cfg diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 0000000..b49d77b --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,247 @@ +<# +.Synopsis +Activate a Python virtual environment for the current PowerShell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + +.Notes +On Windows, it may be required to enable this Activate.ps1 script by setting the +execution policy for the user. You can do this by issuing the following PowerShell +command: + +PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser + +For more information on Execution Policies: +https://go.microsoft.com/fwlink/?LinkID=135170 + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove VIRTUAL_ENV_PROMPT altogether. + if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) { + Remove-Item -Path env:VIRTUAL_ENV_PROMPT + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0, 1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} +else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} +else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } + $env:VIRTUAL_ENV_PROMPT = $Prompt +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 0000000..26cc403 --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,70 @@ +# This file must be used with "source bin/activate" *from bash* +# You cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # Call hash to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + hash -r 2> /dev/null + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +# on Windows, a path can contain colons and backslashes and has to be converted: +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/sasman/Projects/Robotics/MongoDB-Assignment/.venv) +else + # use the path as-is + export VIRTUAL_ENV=/home/sasman/Projects/Robotics/MongoDB-Assignment/.venv +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/"bin":$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + PS1='(.venv) '"${PS1:-}" + export PS1 + VIRTUAL_ENV_PROMPT='(.venv) ' + export VIRTUAL_ENV_PROMPT +fi + +# Call hash to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +hash -r 2> /dev/null diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 0000000..3302b64 --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,27 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. + +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV /home/sasman/Projects/Robotics/MongoDB-Assignment/.venv + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/"bin":$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + set prompt = '(.venv) '"$prompt" + setenv VIRTUAL_ENV_PROMPT '(.venv) ' +endif + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 0000000..a67baf8 --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,69 @@ +# This file must be used with "source /bin/activate.fish" *from fish* +# (https://fishshell.com/). You cannot run it directly. + +function deactivate -d "Exit virtual environment and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + set -e _OLD_FISH_PROMPT_OVERRIDE + # prevents error when using nested fish instances (Issue #93858) + if functions -q _old_fish_prompt + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + if test "$argv[1]" != "nondestructive" + # Self-destruct! + functions -e deactivate + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV /home/sasman/Projects/Robotics/MongoDB-Assignment/.venv + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/"bin $PATH + +# Unset PYTHONHOME if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # Save the current fish_prompt function as the function _old_fish_prompt. + functions -c fish_prompt _old_fish_prompt + + # With the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command. + set -l old_status $status + + # Output the venv prompt; color taken from the blue of the Python logo. + printf "%s%s%s" (set_color 4B8BBE) '(.venv) ' (set_color normal) + + # Restore the return status of the previous command. + echo "exit $old_status" | . + # Output the original/"old" prompt. + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" + set -gx VIRTUAL_ENV_PROMPT '(.venv) ' +end diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 0000000..193281c --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/sasman/Projects/Robotics/MongoDB-Assignment/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 0000000..193281c --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/sasman/Projects/Robotics/MongoDB-Assignment/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.12 b/.venv/bin/pip3.12 new file mode 100755 index 0000000..193281c --- /dev/null +++ b/.venv/bin/pip3.12 @@ -0,0 +1,8 @@ +#!/home/sasman/Projects/Robotics/MongoDB-Assignment/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 0000000..ae65fda --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/.venv/bin/python3.12 b/.venv/bin/python3.12 new file mode 120000 index 0000000..b8a0adb --- /dev/null +++ b/.venv/bin/python3.12 @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/_yaml/__init__.py b/.venv/lib/python3.12/site-packages/_yaml/__init__.py new file mode 100644 index 0000000..7baa8c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_yaml/__init__.py @@ -0,0 +1,33 @@ +# This is a stub package designed to roughly emulate the _yaml +# extension module, which previously existed as a standalone module +# and has been moved into the `yaml` package namespace. +# It does not perfectly mimic its old counterpart, but should get +# close enough for anyone who's relying on it even when they shouldn't. +import yaml + +# in some circumstances, the yaml module we imoprted may be from a different version, so we need +# to tread carefully when poking at it here (it may not have the attributes we expect) +if not getattr(yaml, '__with_libyaml__', False): + from sys import version_info + + exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError + raise exc("No module named '_yaml'") +else: + from yaml._yaml import * + import warnings + warnings.warn( + 'The _yaml extension module is now located at yaml._yaml' + ' and its location is subject to change. To use the' + ' LibYAML-based parser and emitter, import from `yaml`:' + ' `from yaml import CLoader as Loader, CDumper as Dumper`.', + DeprecationWarning + ) + del warnings + # Don't `del yaml` here because yaml is actually an existing + # namespace member of _yaml. + +__name__ = '_yaml' +# If the module is top-level (i.e. not a part of any specific package) +# then the attribute should be set to ''. +# https://docs.python.org/3.8/library/types.html +__package__ = '' diff --git a/.venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/_yaml/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf133cd49d349167adc877ec316d099370691e5e GIT binary patch literal 881 zcmYjP&ubGw6rS1LB%5EYZKy?5n4Z!|lSMrUB0_B}3QZ9yh^a8yWT(lr*_kjiX`7x} zFM9Omzn~PsqyK{l^~b@k2!dXEi_}w3&SYEk9cJcx@B7|+^Jd=X^BJJ6@WG-x2>^Z? zaAq2h?D&P=UV;DwunX$I0t6BeSb@E7#{@a8V@}r!?Ya#Cv4h0M699xo90p0Ep45tD zklcqc`zh4yR5$g;f^kl_?rMg+J{iaVUjvt9_N~|h!Kxdi>)9Yv&kbNHdt}_p(K(E_ zsOsP@Z4Ugfi}7L!M!8KQB;^cKw#G}=k!kcOsh6GUWsX7~^t%KxKO_MvVk0YR5xlZ< zxY8kLo?M8h5z3atqqqJ}BfxQQAgWWZT? zVTt>JkxCSw=Fmd5ANFEbd}>sp#Hi5-l=6{dj)MC#fRk)9lh4-PXhU15kx=r8hWnZ^iF^TU^j(&1aNUFGCwbc$@<; z_uB>T_;YnIReUw|CFOm0(2tq&+w-4hZhXvKez&^2aG1#-j1@m;W)Ga%U(Tg(PT{LF qv3u`XZLc=8>~s|lU68)`C!0(phhrc;p<%AD>*{TMVlOMEb+y05?+eiY literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__init__.py b/.venv/lib/python3.12/site-packages/bson/__init__.py new file mode 100644 index 0000000..b655e30 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/__init__.py @@ -0,0 +1,1484 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""BSON (Binary JSON) encoding and decoding. + +The mapping from Python types to BSON types is as follows: + +======================================= ============= =================== +Python Type BSON Type Supported Direction +======================================= ============= =================== +None null both +bool boolean both +int [#int]_ int32 / int64 py -> bson +:class:`bson.int64.Int64` int64 both +float number (real) both +str string both +list array both +dict object both +:class:`~bson.son.SON` object both +:py:class:`~collections.abc.Mapping` object py -> bson +:class:`~bson.raw_bson.RawBSONDocument` object both [#raw]_ +datetime.datetime [#dt]_ [#dt2]_ UTC datetime both +:class:`~bson.datetime_ms.DatetimeMS` UTC datetime both [#dt3]_ +:class:`~bson.regex.Regex` regex both +compiled re [#re]_ regex py -> bson +:class:`~bson.binary.Binary` binary both +:py:class:`uuid.UUID` [#uuid]_ binary both +:class:`~bson.objectid.ObjectId` oid both +:class:`~bson.dbref.DBRef` dbref both +:class:`~bson.dbref.DBRef` dbpointer bson -> py +None undefined bson -> py +:class:`~bson.code.Code` code both +str symbol bson -> py +bytes [#bytes]_ binary both +:class:`~bson.timestamp.Timestamp` timestamp both +:class:`~bson.decimal128.Decimal128` decimal128 both +:class:`~bson.min_key.MinKey` min key both +:class:`~bson.max_key.MaxKey` max key both +======================================= ============= =================== + +.. [#int] A Python int will be saved as a BSON int32 or BSON int64 depending + on its size. A BSON int32 will always decode to a Python int. A BSON + int64 will always decode to a :class:`~bson.int64.Int64`. +.. [#raw] Decoding a bson object to :class:`~bson.raw_bson.RawBSONDocument` can be + optionally configured via :attr:`~bson.codec_options.CodecOptions.document_class`. +.. [#dt] datetime.datetime instances are encoded with millisecond precision so + the microsecond field is truncated. +.. [#dt2] all datetime.datetime instances are encoded as UTC. By default, they + are decoded as *naive* but timezone aware datetimes are also supported. + See `Dates and Times `_ for examples. +.. [#dt3] To enable decoding a bson UTC datetime to a :class:`~bson.datetime_ms.DatetimeMS` + instance see `handling out of range datetimes `_. +.. [#uuid] For :py:class:`uuid.UUID` encoding and decoding behavior see ``_. +.. [#re] :class:`~bson.regex.Regex` instances and regular expression + objects from ``re.compile()`` are both saved as BSON regular expressions. + BSON regular expressions are decoded as :class:`~bson.regex.Regex` + instances. +.. [#bytes] The bytes type is encoded as BSON binary with + subtype 0. It will be decoded back to bytes. +""" +from __future__ import annotations + +import datetime +import itertools +import os +import re +import struct +import sys +import uuid +from codecs import utf_8_decode as _utf_8_decode +from codecs import utf_8_encode as _utf_8_encode +from collections import abc as _abc +from typing import ( + IO, + TYPE_CHECKING, + Any, + BinaryIO, + Callable, + Generator, + Iterator, + Mapping, + MutableMapping, + NoReturn, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + cast, + overload, +) + +from bson.binary import ( + ALL_UUID_SUBTYPES, + CSHARP_LEGACY, + JAVA_LEGACY, + OLD_UUID_SUBTYPE, + STANDARD, + UUID_SUBTYPE, + Binary, + UuidRepresentation, +) +from bson.code import Code +from bson.codec_options import ( + DEFAULT_CODEC_OPTIONS, + CodecOptions, + DatetimeConversion, + _raw_document_class, +) +from bson.datetime_ms import ( + EPOCH_AWARE, + EPOCH_NAIVE, + DatetimeMS, + _datetime_to_millis, + _millis_to_datetime, +) +from bson.dbref import DBRef +from bson.decimal128 import Decimal128 +from bson.errors import InvalidBSON, InvalidDocument, InvalidStringData +from bson.int64 import Int64 +from bson.max_key import MaxKey +from bson.min_key import MinKey +from bson.objectid import ObjectId +from bson.regex import Regex +from bson.son import RE_TYPE, SON +from bson.timestamp import Timestamp +from bson.tz_util import utc + +# Import some modules for type-checking only. +if TYPE_CHECKING: + from bson.raw_bson import RawBSONDocument + from bson.typings import _DocumentType, _ReadableBuffer + +try: + from bson import _cbson # type: ignore[attr-defined] + + _USE_C = True +except ImportError: + _USE_C = False + +__all__ = [ + "ALL_UUID_SUBTYPES", + "CSHARP_LEGACY", + "JAVA_LEGACY", + "OLD_UUID_SUBTYPE", + "STANDARD", + "UUID_SUBTYPE", + "Binary", + "UuidRepresentation", + "Code", + "DEFAULT_CODEC_OPTIONS", + "CodecOptions", + "DBRef", + "Decimal128", + "InvalidBSON", + "InvalidDocument", + "InvalidStringData", + "Int64", + "MaxKey", + "MinKey", + "ObjectId", + "Regex", + "RE_TYPE", + "SON", + "Timestamp", + "utc", + "EPOCH_AWARE", + "EPOCH_NAIVE", + "BSONNUM", + "BSONSTR", + "BSONOBJ", + "BSONARR", + "BSONBIN", + "BSONUND", + "BSONOID", + "BSONBOO", + "BSONDAT", + "BSONNUL", + "BSONRGX", + "BSONREF", + "BSONCOD", + "BSONSYM", + "BSONCWS", + "BSONINT", + "BSONTIM", + "BSONLON", + "BSONDEC", + "BSONMIN", + "BSONMAX", + "get_data_and_view", + "gen_list_name", + "encode", + "decode", + "decode_all", + "decode_iter", + "decode_file_iter", + "is_valid", + "BSON", + "has_c", + "DatetimeConversion", + "DatetimeMS", +] + +BSONNUM = b"\x01" # Floating point +BSONSTR = b"\x02" # UTF-8 string +BSONOBJ = b"\x03" # Embedded document +BSONARR = b"\x04" # Array +BSONBIN = b"\x05" # Binary +BSONUND = b"\x06" # Undefined +BSONOID = b"\x07" # ObjectId +BSONBOO = b"\x08" # Boolean +BSONDAT = b"\x09" # UTC Datetime +BSONNUL = b"\x0A" # Null +BSONRGX = b"\x0B" # Regex +BSONREF = b"\x0C" # DBRef +BSONCOD = b"\x0D" # Javascript code +BSONSYM = b"\x0E" # Symbol +BSONCWS = b"\x0F" # Javascript code with scope +BSONINT = b"\x10" # 32bit int +BSONTIM = b"\x11" # Timestamp +BSONLON = b"\x12" # 64bit int +BSONDEC = b"\x13" # Decimal128 +BSONMIN = b"\xFF" # Min key +BSONMAX = b"\x7F" # Max key + + +_UNPACK_FLOAT_FROM = struct.Struct(" Tuple[Any, memoryview]: + if isinstance(data, (bytes, bytearray)): + return data, memoryview(data) + view = memoryview(data) + return view.tobytes(), view + + +def _raise_unknown_type(element_type: int, element_name: str) -> NoReturn: + """Unknown type helper.""" + raise InvalidBSON( + "Detected unknown BSON type {!r} for fieldname '{}'. Are " + "you using the latest driver version?".format(chr(element_type).encode(), element_name) + ) + + +def _get_int( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[int, int]: + """Decode a BSON int32 to python int.""" + return _UNPACK_INT_FROM(data, position)[0], position + 4 + + +def _get_c_string(data: Any, view: Any, position: int, opts: CodecOptions[Any]) -> Tuple[str, int]: + """Decode a BSON 'C' string to python str.""" + end = data.index(b"\x00", position) + return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1 + + +def _get_float( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[float, int]: + """Decode a BSON double to python float.""" + return _UNPACK_FLOAT_FROM(data, position)[0], position + 8 + + +def _get_string( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy: Any +) -> Tuple[str, int]: + """Decode a BSON string to python str.""" + length = _UNPACK_INT_FROM(data, position)[0] + position += 4 + if length < 1 or obj_end - position < length: + raise InvalidBSON("invalid string length") + end = position + length - 1 + if data[end] != 0: + raise InvalidBSON("invalid end of string") + return _utf_8_decode(view[position:end], opts.unicode_decode_error_handler, True)[0], end + 1 + + +def _get_object_size(data: Any, position: int, obj_end: int) -> Tuple[int, int]: + """Validate and return a BSON document's size.""" + try: + obj_size = _UNPACK_INT_FROM(data, position)[0] + except struct.error as exc: + raise InvalidBSON(str(exc)) from None + end = position + obj_size - 1 + if end >= obj_end: + raise InvalidBSON("invalid object length") + if data[end] != 0: + raise InvalidBSON("bad eoo") + # If this is the top-level document, validate the total size too. + if position == 0 and obj_size != obj_end: + raise InvalidBSON("invalid object length") + return obj_size, end + + +def _get_object( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy: Any +) -> Tuple[Any, int]: + """Decode a BSON subdocument to opts.document_class or bson.dbref.DBRef.""" + obj_size, end = _get_object_size(data, position, obj_end) + if _raw_document_class(opts.document_class): + return (opts.document_class(data[position : end + 1], opts), position + obj_size) + + obj = _elements_to_dict(data, view, position + 4, end, opts) + + position += obj_size + # If DBRef validation fails, return a normal doc. + if ( + isinstance(obj.get("$ref"), str) + and "$id" in obj + and isinstance(obj.get("$db"), (str, type(None))) + ): + return (DBRef(obj.pop("$ref"), obj.pop("$id", None), obj.pop("$db", None), obj), position) + return obj, position + + +def _get_array( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], element_name: str +) -> Tuple[Any, int]: + """Decode a BSON array to python list.""" + size = _UNPACK_INT_FROM(data, position)[0] + end = position + size - 1 + if data[end] != 0: + raise InvalidBSON("bad eoo") + + position += 4 + end -= 1 + result: list[Any] = [] + + # Avoid doing global and attribute lookups in the loop. + append = result.append + index = data.index + getter = _ELEMENT_GETTER + decoder_map = opts.type_registry._decoder_map + + while position < end: + element_type = data[position] + # Just skip the keys. + position = index(b"\x00", position) + 1 + try: + value, position = getter[element_type]( + data, view, position, obj_end, opts, element_name + ) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if decoder_map: + custom_decoder = decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + append(value) + + if position != end + 1: + raise InvalidBSON("bad array length") + return result, position + 1 + + +def _get_binary( + data: Any, _view: Any, position: int, obj_end: int, opts: CodecOptions[Any], dummy1: Any +) -> Tuple[Union[Binary, uuid.UUID], int]: + """Decode a BSON binary to bson.binary.Binary or python UUID.""" + length, subtype = _UNPACK_LENGTH_SUBTYPE_FROM(data, position) + position += 5 + if subtype == 2: + length2 = _UNPACK_INT_FROM(data, position)[0] + position += 4 + if length2 != length - 4: + raise InvalidBSON("invalid binary (st 2) - lengths don't match!") + length = length2 + end = position + length + if length < 0 or end > obj_end: + raise InvalidBSON("bad binary object length") + + # Convert UUID subtypes to native UUIDs. + if subtype in ALL_UUID_SUBTYPES: + uuid_rep = opts.uuid_representation + binary_value = Binary(data[position:end], subtype) + if ( + (uuid_rep == UuidRepresentation.UNSPECIFIED) + or (subtype == UUID_SUBTYPE and uuid_rep != STANDARD) + or (subtype == OLD_UUID_SUBTYPE and uuid_rep == STANDARD) + ): + return binary_value, end + return binary_value.as_uuid(uuid_rep), end + + # Decode subtype 0 to 'bytes'. + if subtype == 0: + value = data[position:end] + else: + value = Binary(data[position:end], subtype) + + return value, end + + +def _get_oid( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[ObjectId, int]: + """Decode a BSON ObjectId to bson.objectid.ObjectId.""" + end = position + 12 + return ObjectId(data[position:end]), end + + +def _get_boolean( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[bool, int]: + """Decode a BSON true/false to python True/False.""" + end = position + 1 + boolean_byte = data[position:end] + if boolean_byte == b"\x00": + return False, end + elif boolean_byte == b"\x01": + return True, end + raise InvalidBSON("invalid boolean value: %r" % boolean_byte) + + +def _get_date( + data: Any, _view: Any, position: int, dummy0: int, opts: CodecOptions[Any], dummy1: Any +) -> Tuple[Union[datetime.datetime, DatetimeMS], int]: + """Decode a BSON datetime to python datetime.datetime.""" + return _millis_to_datetime(_UNPACK_LONG_FROM(data, position)[0], opts), position + 8 + + +def _get_code( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], element_name: str +) -> Tuple[Code, int]: + """Decode a BSON code to bson.code.Code.""" + code, position = _get_string(data, view, position, obj_end, opts, element_name) + return Code(code), position + + +def _get_code_w_scope( + data: Any, view: Any, position: int, _obj_end: int, opts: CodecOptions[Any], element_name: str +) -> Tuple[Code, int]: + """Decode a BSON code_w_scope to bson.code.Code.""" + code_end = position + _UNPACK_INT_FROM(data, position)[0] + code, position = _get_string(data, view, position + 4, code_end, opts, element_name) + scope, position = _get_object(data, view, position, code_end, opts, element_name) + if position != code_end: + raise InvalidBSON("scope outside of javascript code boundaries") + return Code(code, scope), position + + +def _get_regex( + data: Any, view: Any, position: int, dummy0: Any, opts: CodecOptions[Any], dummy1: Any +) -> Tuple[Regex[Any], int]: + """Decode a BSON regex to bson.regex.Regex or a python pattern object.""" + pattern, position = _get_c_string(data, view, position, opts) + bson_flags, position = _get_c_string(data, view, position, opts) + bson_re = Regex(pattern, bson_flags) + return bson_re, position + + +def _get_ref( + data: Any, view: Any, position: int, obj_end: int, opts: CodecOptions[Any], element_name: str +) -> Tuple[DBRef, int]: + """Decode (deprecated) BSON DBPointer to bson.dbref.DBRef.""" + collection, position = _get_string(data, view, position, obj_end, opts, element_name) + oid, position = _get_oid(data, view, position, obj_end, opts, element_name) + return DBRef(collection, oid), position + + +def _get_timestamp( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[Timestamp, int]: + """Decode a BSON timestamp to bson.timestamp.Timestamp.""" + inc, timestamp = _UNPACK_TIMESTAMP_FROM(data, position) + return Timestamp(timestamp, inc), position + 8 + + +def _get_int64( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[Int64, int]: + """Decode a BSON int64 to bson.int64.Int64.""" + return Int64(_UNPACK_LONG_FROM(data, position)[0]), position + 8 + + +def _get_decimal128( + data: Any, _view: Any, position: int, dummy0: Any, dummy1: Any, dummy2: Any +) -> Tuple[Decimal128, int]: + """Decode a BSON decimal128 to bson.decimal128.Decimal128.""" + end = position + 16 + return Decimal128.from_bid(data[position:end]), end + + +# Each decoder function's signature is: +# - data: bytes +# - view: memoryview that references `data` +# - position: int, beginning of object in 'data' to decode +# - obj_end: int, end of object to decode in 'data' if variable-length type +# - opts: a CodecOptions +_ELEMENT_GETTER: dict[int, Callable[..., Tuple[Any, int]]] = { + ord(BSONNUM): _get_float, + ord(BSONSTR): _get_string, + ord(BSONOBJ): _get_object, + ord(BSONARR): _get_array, + ord(BSONBIN): _get_binary, + ord(BSONUND): lambda u, v, w, x, y, z: (None, w), # noqa: ARG005 # Deprecated undefined + ord(BSONOID): _get_oid, + ord(BSONBOO): _get_boolean, + ord(BSONDAT): _get_date, + ord(BSONNUL): lambda u, v, w, x, y, z: (None, w), # noqa: ARG005 + ord(BSONRGX): _get_regex, + ord(BSONREF): _get_ref, # Deprecated DBPointer + ord(BSONCOD): _get_code, + ord(BSONSYM): _get_string, # Deprecated symbol + ord(BSONCWS): _get_code_w_scope, + ord(BSONINT): _get_int, + ord(BSONTIM): _get_timestamp, + ord(BSONLON): _get_int64, + ord(BSONDEC): _get_decimal128, + ord(BSONMIN): lambda u, v, w, x, y, z: (MinKey(), w), # noqa: ARG005 + ord(BSONMAX): lambda u, v, w, x, y, z: (MaxKey(), w), # noqa: ARG005 +} + + +if _USE_C: + + def _element_to_dict( + data: Any, + view: Any, # noqa: ARG001 + position: int, + obj_end: int, + opts: CodecOptions[Any], + raw_array: bool = False, + ) -> Tuple[str, Any, int]: + return cast( + "Tuple[str, Any, int]", + _cbson._element_to_dict(data, position, obj_end, opts, raw_array), + ) + +else: + + def _element_to_dict( + data: Any, + view: Any, + position: int, + obj_end: int, + opts: CodecOptions[Any], + raw_array: bool = False, + ) -> Tuple[str, Any, int]: + """Decode a single key, value pair.""" + element_type = data[position] + position += 1 + element_name, position = _get_c_string(data, view, position, opts) + if raw_array and element_type == ord(BSONARR): + _, end = _get_object_size(data, position, len(data)) + return element_name, view[position : end + 1], end + 1 + try: + value, position = _ELEMENT_GETTER[element_type]( + data, view, position, obj_end, opts, element_name + ) + except KeyError: + _raise_unknown_type(element_type, element_name) + + if opts.type_registry._decoder_map: + custom_decoder = opts.type_registry._decoder_map.get(type(value)) + if custom_decoder is not None: + value = custom_decoder(value) + + return element_name, value, position + + +_T = TypeVar("_T", bound=MutableMapping[str, Any]) + + +def _raw_to_dict( + data: Any, + position: int, + obj_end: int, + opts: CodecOptions[RawBSONDocument], + result: _T, + raw_array: bool = False, +) -> _T: + data, view = get_data_and_view(data) + return cast( + _T, _elements_to_dict(data, view, position, obj_end, opts, result, raw_array=raw_array) + ) + + +def _elements_to_dict( + data: Any, + view: Any, + position: int, + obj_end: int, + opts: CodecOptions[Any], + result: Any = None, + raw_array: bool = False, +) -> Any: + """Decode a BSON document into result.""" + if result is None: + result = opts.document_class() + end = obj_end - 1 + while position < end: + key, value, position = _element_to_dict( + data, view, position, obj_end, opts, raw_array=raw_array + ) + result[key] = value + if position != obj_end: + raise InvalidBSON("bad object or element length") + return result + + +def _bson_to_dict(data: Any, opts: CodecOptions[_DocumentType]) -> _DocumentType: + """Decode a BSON string to document_class.""" + data, view = get_data_and_view(data) + try: + if _raw_document_class(opts.document_class): + return opts.document_class(data, opts) # type:ignore[call-arg] + _, end = _get_object_size(data, 0, len(data)) + return cast("_DocumentType", _elements_to_dict(data, view, 4, end, opts)) + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) from None + + +if _USE_C: + _bson_to_dict = _cbson._bson_to_dict + + +_PACK_FLOAT = struct.Struct(" Generator[bytes, None, None]: + """Generate "keys" for encoded lists in the sequence + b"0\x00", b"1\x00", b"2\x00", ... + + The first 1000 keys are returned from a pre-built cache. All + subsequent keys are generated on the fly. + """ + yield from _LIST_NAMES + + counter = itertools.count(1000) + while True: + yield (str(next(counter)) + "\x00").encode("utf8") + + +def _make_c_string_check(string: Union[str, bytes]) -> bytes: + """Make a 'C' string, checking for embedded NUL characters.""" + if isinstance(string, bytes): + if b"\x00" in string: + raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") + try: + _utf_8_decode(string, None, True) + return string + b"\x00" + except UnicodeError: + raise InvalidStringData( + "strings in documents must be valid UTF-8: %r" % string + ) from None + else: + if "\x00" in string: + raise InvalidDocument("BSON keys / regex patterns must not contain a NUL character") + return _utf_8_encode(string)[0] + b"\x00" + + +def _make_c_string(string: Union[str, bytes]) -> bytes: + """Make a 'C' string.""" + if isinstance(string, bytes): + try: + _utf_8_decode(string, None, True) + return string + b"\x00" + except UnicodeError: + raise InvalidStringData( + "strings in documents must be valid UTF-8: %r" % string + ) from None + else: + return _utf_8_encode(string)[0] + b"\x00" + + +def _make_name(string: str) -> bytes: + """Make a 'C' string suitable for a BSON key.""" + if "\x00" in string: + raise InvalidDocument("BSON keys must not contain a NUL character") + return _utf_8_encode(string)[0] + b"\x00" + + +def _encode_float(name: bytes, value: float, dummy0: Any, dummy1: Any) -> bytes: + """Encode a float.""" + return b"\x01" + name + _PACK_FLOAT(value) + + +def _encode_bytes(name: bytes, value: bytes, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python bytes.""" + # Python3 special case. Store 'bytes' as BSON binary subtype 0. + return b"\x05" + name + _PACK_INT(len(value)) + b"\x00" + value + + +def _encode_mapping(name: bytes, value: Any, check_keys: bool, opts: CodecOptions[Any]) -> bytes: + """Encode a mapping type.""" + if _raw_document_class(value): + return b"\x03" + name + cast(bytes, value.raw) + data = b"".join([_element_to_bson(key, val, check_keys, opts) for key, val in value.items()]) + return b"\x03" + name + _PACK_INT(len(data) + 5) + data + b"\x00" + + +def _encode_dbref(name: bytes, value: DBRef, check_keys: bool, opts: CodecOptions[Any]) -> bytes: + """Encode bson.dbref.DBRef.""" + buf = bytearray(b"\x03" + name + b"\x00\x00\x00\x00") + begin = len(buf) - 4 + + buf += _name_value_to_bson(b"$ref\x00", value.collection, check_keys, opts) + buf += _name_value_to_bson(b"$id\x00", value.id, check_keys, opts) + if value.database is not None: + buf += _name_value_to_bson(b"$db\x00", value.database, check_keys, opts) + for key, val in value._DBRef__kwargs.items(): + buf += _element_to_bson(key, val, check_keys, opts) + + buf += b"\x00" + buf[begin : begin + 4] = _PACK_INT(len(buf) - begin) + return bytes(buf) + + +def _encode_list( + name: bytes, value: Sequence[Any], check_keys: bool, opts: CodecOptions[Any] +) -> bytes: + """Encode a list/tuple.""" + lname = gen_list_name() + data = b"".join([_name_value_to_bson(next(lname), item, check_keys, opts) for item in value]) + return b"\x04" + name + _PACK_INT(len(data) + 5) + data + b"\x00" + + +def _encode_text(name: bytes, value: str, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python str.""" + bvalue = _utf_8_encode(value)[0] + return b"\x02" + name + _PACK_INT(len(bvalue) + 1) + bvalue + b"\x00" + + +def _encode_binary(name: bytes, value: Binary, dummy0: Any, dummy1: Any) -> bytes: + """Encode bson.binary.Binary.""" + subtype = value.subtype + if subtype == 2: + value = _PACK_INT(len(value)) + value # type: ignore + return b"\x05" + name + _PACK_LENGTH_SUBTYPE(len(value), subtype) + value + + +def _encode_uuid(name: bytes, value: uuid.UUID, dummy: Any, opts: CodecOptions[Any]) -> bytes: + """Encode uuid.UUID.""" + uuid_representation = opts.uuid_representation + binval = Binary.from_uuid(value, uuid_representation=uuid_representation) + return _encode_binary(name, binval, dummy, opts) + + +def _encode_objectid(name: bytes, value: ObjectId, dummy: Any, dummy1: Any) -> bytes: + """Encode bson.objectid.ObjectId.""" + return b"\x07" + name + value.binary + + +def _encode_bool(name: bytes, value: bool, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python boolean (True/False).""" + return b"\x08" + name + (value and b"\x01" or b"\x00") + + +def _encode_datetime(name: bytes, value: datetime.datetime, dummy0: Any, dummy1: Any) -> bytes: + """Encode datetime.datetime.""" + millis = _datetime_to_millis(value) + return b"\x09" + name + _PACK_LONG(millis) + + +def _encode_datetime_ms(name: bytes, value: DatetimeMS, dummy0: Any, dummy1: Any) -> bytes: + """Encode datetime.datetime.""" + millis = int(value) + return b"\x09" + name + _PACK_LONG(millis) + + +def _encode_none(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: + """Encode python None.""" + return b"\x0A" + name + + +def _encode_regex(name: bytes, value: Regex[Any], dummy0: Any, dummy1: Any) -> bytes: + """Encode a python regex or bson.regex.Regex.""" + flags = value.flags + # Python 3 common case + if flags == re.UNICODE: + return b"\x0B" + name + _make_c_string_check(value.pattern) + b"u\x00" + elif flags == 0: + return b"\x0B" + name + _make_c_string_check(value.pattern) + b"\x00" + else: + sflags = b"" + if flags & re.IGNORECASE: + sflags += b"i" + if flags & re.LOCALE: + sflags += b"l" + if flags & re.MULTILINE: + sflags += b"m" + if flags & re.DOTALL: + sflags += b"s" + if flags & re.UNICODE: + sflags += b"u" + if flags & re.VERBOSE: + sflags += b"x" + sflags += b"\x00" + return b"\x0B" + name + _make_c_string_check(value.pattern) + sflags + + +def _encode_code(name: bytes, value: Code, dummy: Any, opts: CodecOptions[Any]) -> bytes: + """Encode bson.code.Code.""" + cstring = _make_c_string(value) + cstrlen = len(cstring) + if value.scope is None: + return b"\x0D" + name + _PACK_INT(cstrlen) + cstring + scope = _dict_to_bson(value.scope, False, opts, False) + full_length = _PACK_INT(8 + cstrlen + len(scope)) + return b"\x0F" + name + full_length + _PACK_INT(cstrlen) + cstring + scope + + +def _encode_int(name: bytes, value: int, dummy0: Any, dummy1: Any) -> bytes: + """Encode a python int.""" + if -2147483648 <= value <= 2147483647: + return b"\x10" + name + _PACK_INT(value) + else: + try: + return b"\x12" + name + _PACK_LONG(value) + except struct.error: + raise OverflowError("BSON can only handle up to 8-byte ints") from None + + +def _encode_timestamp(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes: + """Encode bson.timestamp.Timestamp.""" + return b"\x11" + name + _PACK_TIMESTAMP(value.inc, value.time) + + +def _encode_long(name: bytes, value: Any, dummy0: Any, dummy1: Any) -> bytes: + """Encode a bson.int64.Int64.""" + try: + return b"\x12" + name + _PACK_LONG(value) + except struct.error: + raise OverflowError("BSON can only handle up to 8-byte ints") from None + + +def _encode_decimal128(name: bytes, value: Decimal128, dummy0: Any, dummy1: Any) -> bytes: + """Encode bson.decimal128.Decimal128.""" + return b"\x13" + name + value.bid + + +def _encode_minkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: + """Encode bson.min_key.MinKey.""" + return b"\xFF" + name + + +def _encode_maxkey(name: bytes, dummy0: Any, dummy1: Any, dummy2: Any) -> bytes: + """Encode bson.max_key.MaxKey.""" + return b"\x7F" + name + + +# Each encoder function's signature is: +# - name: utf-8 bytes +# - value: a Python data type, e.g. a Python int for _encode_int +# - check_keys: bool, whether to check for invalid names +# - opts: a CodecOptions +_ENCODERS = { + bool: _encode_bool, + bytes: _encode_bytes, + datetime.datetime: _encode_datetime, + DatetimeMS: _encode_datetime_ms, + dict: _encode_mapping, + float: _encode_float, + int: _encode_int, + list: _encode_list, + str: _encode_text, + tuple: _encode_list, + type(None): _encode_none, + uuid.UUID: _encode_uuid, + Binary: _encode_binary, + Int64: _encode_long, + Code: _encode_code, + DBRef: _encode_dbref, + MaxKey: _encode_maxkey, + MinKey: _encode_minkey, + ObjectId: _encode_objectid, + Regex: _encode_regex, + RE_TYPE: _encode_regex, + SON: _encode_mapping, + Timestamp: _encode_timestamp, + Decimal128: _encode_decimal128, + # Special case. This will never be looked up directly. + _abc.Mapping: _encode_mapping, +} + +# Map each _type_marker to its encoder for faster lookup. +_MARKERS = {} +for _typ in _ENCODERS: + if hasattr(_typ, "_type_marker"): + _MARKERS[_typ._type_marker] = _ENCODERS[_typ] + + +_BUILT_IN_TYPES = tuple(t for t in _ENCODERS) + + +def _name_value_to_bson( + name: bytes, + value: Any, + check_keys: bool, + opts: CodecOptions[Any], + in_custom_call: bool = False, + in_fallback_call: bool = False, +) -> bytes: + """Encode a single name, value pair.""" + + was_integer_overflow = False + + # First see if the type is already cached. KeyError will only ever + # happen once per subtype. + try: + return _ENCODERS[type(value)](name, value, check_keys, opts) # type: ignore + except KeyError: + pass + except OverflowError: + if not isinstance(value, int): + raise + + # Give the fallback_encoder a chance + was_integer_overflow = True + + # Second, fall back to trying _type_marker. This has to be done + # before the loop below since users could subclass one of our + # custom types that subclasses a python built-in (e.g. Binary) + marker = getattr(value, "_type_marker", None) + if isinstance(marker, int) and marker in _MARKERS: + func = _MARKERS[marker] + # Cache this type for faster subsequent lookup. + _ENCODERS[type(value)] = func + return func(name, value, check_keys, opts) # type: ignore + + # Third, check if a type encoder is registered for this type. + # Note that subtypes of registered custom types are not auto-encoded. + if not in_custom_call and opts.type_registry._encoder_map: + custom_encoder = opts.type_registry._encoder_map.get(type(value)) + if custom_encoder is not None: + return _name_value_to_bson( + name, custom_encoder(value), check_keys, opts, in_custom_call=True + ) + + # Fourth, test each base type. This will only happen once for + # a subtype of a supported base type. Unlike in the C-extensions, this + # is done after trying the custom type encoder because checking for each + # subtype is expensive. + for base in _BUILT_IN_TYPES: + if not was_integer_overflow and isinstance(value, base): + func = _ENCODERS[base] + # Cache this type for faster subsequent lookup. + _ENCODERS[type(value)] = func + return func(name, value, check_keys, opts) # type: ignore + + # As a last resort, try using the fallback encoder, if the user has + # provided one. + fallback_encoder = opts.type_registry._fallback_encoder + if not in_fallback_call and fallback_encoder is not None: + return _name_value_to_bson( + name, fallback_encoder(value), check_keys, opts, in_fallback_call=True + ) + + if was_integer_overflow: + raise OverflowError("BSON can only handle up to 8-byte ints") + raise InvalidDocument(f"cannot encode object: {value!r}, of type: {type(value)!r}") + + +def _element_to_bson(key: Any, value: Any, check_keys: bool, opts: CodecOptions[Any]) -> bytes: + """Encode a single key, value pair.""" + if not isinstance(key, str): + raise InvalidDocument(f"documents must have only string keys, key was {key!r}") + if check_keys: + if key.startswith("$"): + raise InvalidDocument(f"key {key!r} must not start with '$'") + if "." in key: + raise InvalidDocument(f"key {key!r} must not contain '.'") + + name = _make_name(key) + return _name_value_to_bson(name, value, check_keys, opts) + + +def _dict_to_bson( + doc: Any, check_keys: bool, opts: CodecOptions[Any], top_level: bool = True +) -> bytes: + """Encode a document to BSON.""" + if _raw_document_class(doc): + return cast(bytes, doc.raw) + try: + elements = [] + if top_level and "_id" in doc: + elements.append(_name_value_to_bson(b"_id\x00", doc["_id"], check_keys, opts)) + for key, value in doc.items(): + if not top_level or key != "_id": + try: + elements.append(_element_to_bson(key, value, check_keys, opts)) + except InvalidDocument as err: + raise InvalidDocument(f"Invalid document {doc} | {err}") from err + except AttributeError: + raise TypeError(f"encoder expected a mapping type but got: {doc!r}") from None + + encoded = b"".join(elements) + return _PACK_INT(len(encoded) + 5) + encoded + b"\x00" + + +if _USE_C: + _dict_to_bson = _cbson._dict_to_bson + + +_CODEC_OPTIONS_TYPE_ERROR = TypeError("codec_options must be an instance of CodecOptions") + + +def encode( + document: Mapping[str, Any], + check_keys: bool = False, + codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS, +) -> bytes: + """Encode a document to BSON. + + A document can be any mapping type (like :class:`dict`). + + Raises :class:`TypeError` if `document` is not a mapping type, + or contains keys that are not instances of :class:`str`. Raises + :class:`~bson.errors.InvalidDocument` if `document` cannot be + converted to :class:`BSON`. + + :param document: mapping type representing a document + :param check_keys: check if keys start with '$' or + contain '.', raising :class:`~bson.errors.InvalidDocument` in + either case + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionadded:: 3.9 + """ + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _dict_to_bson(document, check_keys, codec_options) + + +@overload +def decode(data: _ReadableBuffer, codec_options: None = None) -> dict[str, Any]: + ... + + +@overload +def decode(data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType]) -> _DocumentType: + ... + + +def decode( + data: _ReadableBuffer, codec_options: Optional[CodecOptions[_DocumentType]] = None +) -> Union[dict[str, Any], _DocumentType]: + """Decode BSON to a document. + + By default, returns a BSON document represented as a Python + :class:`dict`. To use a different :class:`MutableMapping` class, + configure a :class:`~bson.codec_options.CodecOptions`:: + + >>> import collections # From Python standard library. + >>> import bson + >>> from bson.codec_options import CodecOptions + >>> data = bson.encode({'a': 1}) + >>> decoded_doc = bson.decode(data) + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.decode(data, codec_options=options) + >>> type(decoded_doc) + + + :param data: the BSON to decode. Any bytes-like object that implements + the buffer protocol. + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionadded:: 3.9 + """ + opts: CodecOptions[Any] = codec_options or DEFAULT_CODEC_OPTIONS + if not isinstance(opts, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return cast("Union[dict[str, Any], _DocumentType]", _bson_to_dict(data, opts)) + + +def _decode_all(data: _ReadableBuffer, opts: CodecOptions[_DocumentType]) -> list[_DocumentType]: + """Decode a BSON data to multiple documents.""" + data, view = get_data_and_view(data) + data_len = len(data) + docs: list[_DocumentType] = [] + position = 0 + end = data_len - 1 + use_raw = _raw_document_class(opts.document_class) + try: + while position < end: + obj_size = _UNPACK_INT_FROM(data, position)[0] + if data_len - position < obj_size: + raise InvalidBSON("invalid object size") + obj_end = position + obj_size - 1 + if data[obj_end] != 0: + raise InvalidBSON("bad eoo") + if use_raw: + docs.append(opts.document_class(data[position : obj_end + 1], opts)) # type: ignore + else: + docs.append(_elements_to_dict(data, view, position + 4, obj_end, opts)) + position += obj_size + return docs + except InvalidBSON: + raise + except Exception: + # Change exception type to InvalidBSON but preserve traceback. + _, exc_value, exc_tb = sys.exc_info() + raise InvalidBSON(str(exc_value)).with_traceback(exc_tb) from None + + +if _USE_C: + _decode_all = _cbson._decode_all + + +@overload +def decode_all(data: _ReadableBuffer, codec_options: None = None) -> list[dict[str, Any]]: + ... + + +@overload +def decode_all( + data: _ReadableBuffer, codec_options: CodecOptions[_DocumentType] +) -> list[_DocumentType]: + ... + + +def decode_all( + data: _ReadableBuffer, codec_options: Optional[CodecOptions[_DocumentType]] = None +) -> Union[list[dict[str, Any]], list[_DocumentType]]: + """Decode BSON data to multiple documents. + + `data` must be a bytes-like object implementing the buffer protocol that + provides concatenated, valid, BSON-encoded documents. + + :param data: BSON data + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.9 + Supports bytes-like objects that implement the buffer protocol. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON regular + expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + """ + if codec_options is None: + return _decode_all(data, DEFAULT_CODEC_OPTIONS) + + if not isinstance(codec_options, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + return _decode_all(data, codec_options) + + +def _decode_selective( + rawdoc: Any, fields: Any, codec_options: CodecOptions[_DocumentType] +) -> _DocumentType: + if _raw_document_class(codec_options.document_class): + # If document_class is RawBSONDocument, use vanilla dictionary for + # decoding command response. + doc: _DocumentType = {} # type:ignore[assignment] + else: + # Else, use the specified document_class. + doc = codec_options.document_class() + for key, value in rawdoc.items(): + if key in fields: + if fields[key] == 1: + doc[key] = _bson_to_dict(rawdoc.raw, codec_options)[key] # type:ignore[index] + else: + doc[key] = _decode_selective( # type:ignore[index] + value, fields[key], codec_options + ) + else: + doc[key] = value # type:ignore[index] + return doc + + +def _array_of_documents_to_buffer(data: Union[memoryview, bytes]) -> bytes: + # Extract the raw bytes of each document. + position = 0 + view = memoryview(data) + _, end = _get_object_size(view, position, len(view)) + position += 4 + buffers: list[memoryview] = [] + append = buffers.append + while position < end - 1: + # Just skip the keys. + while view[position] != 0: + position += 1 + position += 1 + obj_size, _ = _get_object_size(view, position, end) + append(view[position : position + obj_size]) + position += obj_size + if position != end: + raise InvalidBSON("bad object or element length") + return b"".join(buffers) + + +if _USE_C: + _array_of_documents_to_buffer = _cbson._array_of_documents_to_buffer + + +def _convert_raw_document_lists_to_streams(document: Any) -> None: + """Convert raw array of documents to a stream of BSON documents.""" + cursor = document.get("cursor") + if not cursor: + return + for key in ("firstBatch", "nextBatch"): + batch = cursor.get(key) + if not batch: + continue + data = _array_of_documents_to_buffer(batch) + if data: + cursor[key] = [data] + else: + cursor[key] = [] + + +def _decode_all_selective( + data: Any, codec_options: CodecOptions[_DocumentType], fields: Any +) -> list[_DocumentType]: + """Decode BSON data to a single document while using user-provided + custom decoding logic. + + `data` must be a string representing a valid, BSON-encoded document. + + :param data: BSON data + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions` with user-specified type + decoders. If no decoders are found, this method is the same as + ``decode_all``. + :param fields: Map of document namespaces where data that needs + to be custom decoded lives or None. For example, to custom decode a + list of objects in 'field1.subfield1', the specified value should be + ``{'field1': {'subfield1': 1}}``. If ``fields`` is an empty map or + None, this method is the same as ``decode_all``. + + :return: Single-member list containing the decoded document. + + .. versionadded:: 3.8 + """ + if not codec_options.type_registry._decoder_map: + return decode_all(data, codec_options) + + if not fields: + return decode_all(data, codec_options.with_options(type_registry=None)) + + # Decode documents for internal use. + from bson.raw_bson import RawBSONDocument + + internal_codec_options: CodecOptions[RawBSONDocument] = codec_options.with_options( + document_class=RawBSONDocument, type_registry=None + ) + _doc = _bson_to_dict(data, internal_codec_options) + return [ + _decode_selective( + _doc, + fields, + codec_options, + ) + ] + + +@overload +def decode_iter(data: bytes, codec_options: None = None) -> Iterator[dict[str, Any]]: + ... + + +@overload +def decode_iter(data: bytes, codec_options: CodecOptions[_DocumentType]) -> Iterator[_DocumentType]: + ... + + +def decode_iter( + data: bytes, codec_options: Optional[CodecOptions[_DocumentType]] = None +) -> Union[Iterator[dict[str, Any]], Iterator[_DocumentType]]: + """Decode BSON data to multiple documents as a generator. + + Works similarly to the decode_all function, but yields one document at a + time. + + `data` must be a string of concatenated, valid, BSON-encoded + documents. + + :param data: BSON data + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionadded:: 2.8 + """ + opts = codec_options or DEFAULT_CODEC_OPTIONS + if not isinstance(opts, CodecOptions): + raise _CODEC_OPTIONS_TYPE_ERROR + + position = 0 + end = len(data) - 1 + while position < end: + obj_size = _UNPACK_INT_FROM(data, position)[0] + elements = data[position : position + obj_size] + position += obj_size + + yield _bson_to_dict(elements, opts) # type:ignore[misc] + + +@overload +def decode_file_iter( + file_obj: Union[BinaryIO, IO[bytes]], codec_options: None = None +) -> Iterator[dict[str, Any]]: + ... + + +@overload +def decode_file_iter( + file_obj: Union[BinaryIO, IO[bytes]], codec_options: CodecOptions[_DocumentType] +) -> Iterator[_DocumentType]: + ... + + +def decode_file_iter( + file_obj: Union[BinaryIO, IO[bytes]], + codec_options: Optional[CodecOptions[_DocumentType]] = None, +) -> Union[Iterator[dict[str, Any]], Iterator[_DocumentType]]: + """Decode bson data from a file to multiple documents as a generator. + + Works similarly to the decode_all function, but reads from the file object + in chunks and parses bson in chunks, yielding one document at a time. + + :param file_obj: A file object containing BSON data. + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + + .. versionadded:: 2.8 + """ + opts = codec_options or DEFAULT_CODEC_OPTIONS + while True: + # Read size of next object. + size_data: Any = file_obj.read(4) + if not size_data: + break # Finished with file normally. + elif len(size_data) != 4: + raise InvalidBSON("cut off in middle of objsize") + obj_size = _UNPACK_INT_FROM(size_data, 0)[0] - 4 + elements = size_data + file_obj.read(max(0, obj_size)) + yield _bson_to_dict(elements, opts) # type:ignore[arg-type, misc] + + +def is_valid(bson: bytes) -> bool: + """Check that the given string represents valid :class:`BSON` data. + + Raises :class:`TypeError` if `bson` is not an instance of + :class:`bytes`. Returns ``True`` + if `bson` is valid :class:`BSON`, ``False`` otherwise. + + :param bson: the data to be validated + """ + if not isinstance(bson, bytes): + raise TypeError(f"BSON data must be an instance of a subclass of bytes, not {type(bson)}") + + try: + _bson_to_dict(bson, DEFAULT_CODEC_OPTIONS) + return True + except Exception: + return False + + +class BSON(bytes): + """BSON (Binary JSON) data. + + .. warning:: Using this class to encode and decode BSON adds a performance + cost. For better performance use the module level functions + :func:`encode` and :func:`decode` instead. + """ + + @classmethod + def encode( + cls: Type[BSON], + document: Mapping[str, Any], + check_keys: bool = False, + codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS, + ) -> BSON: + """Encode a document to a new :class:`BSON` instance. + + A document can be any mapping type (like :class:`dict`). + + Raises :class:`TypeError` if `document` is not a mapping type, + or contains keys that are not instances of + :class:`str'. Raises :class:`~bson.errors.InvalidDocument` + if `document` cannot be converted to :class:`BSON`. + + :param document: mapping type representing a document + :param check_keys: check if keys start with '$' or + contain '.', raising :class:`~bson.errors.InvalidDocument` in + either case + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Replaced `uuid_subtype` option with `codec_options`. + """ + return cls(encode(document, check_keys, codec_options)) + + def decode( # type:ignore[override] + self, codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS + ) -> dict[str, Any]: + """Decode this BSON data. + + By default, returns a BSON document represented as a Python + :class:`dict`. To use a different :class:`MutableMapping` class, + configure a :class:`~bson.codec_options.CodecOptions`:: + + >>> import collections # From Python standard library. + >>> import bson + >>> from bson.codec_options import CodecOptions + >>> data = bson.BSON.encode({'a': 1}) + >>> decoded_doc = bson.BSON(data).decode() + + >>> options = CodecOptions(document_class=collections.OrderedDict) + >>> decoded_doc = bson.BSON(data).decode(codec_options=options) + >>> type(decoded_doc) + + + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions`. + + .. versionchanged:: 3.0 + Removed `compile_re` option: PyMongo now always represents BSON + regular expressions as :class:`~bson.regex.Regex` objects. Use + :meth:`~bson.regex.Regex.try_compile` to attempt to convert from a + BSON regular expression to a Python regular expression object. + + Replaced `as_class`, `tz_aware`, and `uuid_subtype` options with + `codec_options`. + """ + return decode(self, codec_options) + + +def has_c() -> bool: + """Is the C extension installed?""" + return _USE_C + + +def _after_fork() -> None: + """Releases the ObjectID lock child.""" + if ObjectId._inc_lock.locked(): + ObjectId._inc_lock.release() + + +if hasattr(os, "register_at_fork"): + # This will run in the same thread as the fork was called. + # If we fork in a critical region on the same thread, it should break. + # This is fine since we would never call fork directly from a critical region. + os.register_at_fork(after_in_child=_after_fork) diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de7f5279daf4784f006f74b3c9fe46f0527dfa49 GIT binary patch literal 59646 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 O?P_q{@89)F!v6p-XE9~~ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/_helpers.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/_helpers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5046f8251d73b5aab689e9cf1c92b33958ca3571 GIT binary patch literal 1501 zcmZuxL2KMb6rPbr(t3C8wT+2W*tSY+61F%NNpq`PNaN;Eh$%I+;5HGmG`p6g)hsi! zPS@EbE)=>3L)Y}+9D8)|Psyci8fpgLdI-HaaeK+BZ?tl8LIyPR-kYa4?|t*Vc``a$ zL@+KqG2*W*g#OaX=`e-@K3M=^4@GDNML04N+%#4UtYtGXn`DJFtrZLOLPUNbD|R#z zS+F`$F|uJTbevjglP^;#WFV;i|EcILf^vwJ)>tz_-{D6 zsB3i1gIvdGXNsT>dW@IgXe*YqDU>59q$7EaC?aT5S5}4$C{VDnHiD#01%Yrbj91iK zV@>LbKs1BY`+~C!BkxPL%48f0?>0*tZ1K}8H-v~AX_KbXo8O@6hL^;vUaKS5So-1o z)oY%B!>_c0@LtfM!dn$A_53xOv?v$zt&XbrkX4q)5O-dI1g7B=mtopMhtt))2fGh` z?!83Vn%OS(NuM4SDg!cz9)0$#aPfr+<`Zy5uVJcj$S}PH0$_^XhG~Qka-?}NGP-yZ ze0#WyyGTpRC^FA#HL|1(-&fdI`g)6yG?zki1|TMB(4@+4aaxbR6Z&>tmyf}s%0A%N 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 z zqVSG&=pk)j(P=o?38=u8SFmvkf96_)qt{m>{sFu}R1s9eo99!{4N(nz90UcWiNzp7 z+P5f<(I-MO8DMdk3$C5R%P*AZ`D_r1R$X>Md$G%NEqCHsobntLOJuF5|08RqcCB`E ze05v0-oO8o-JeYT;r#9M^|2#=IaZG!t%r}YA(*N(V>UFJYQ6`J{}62IiJ^fQ26~qWBw${sMajX7If3gqUVmn8C?SifjxAMxpTQhzSfZ;xFJc9BBr=&XQe7i6NH& zzZ1#jN~T&VVm+42y;G5k-UtM7)l_&l2K8J?W*0T)#B;fpPc?9H43ti|%;2+u*;Rf} z8G1O|g9Z9D+mA&O3-nv}Ky$Caq96WN4T@iqZ^Z4vaN`i^>t8yx)-}2+jW!}A^2wwXG3=_oMEiMiAb3>PPtqgty-QMkn?~NTho`inX5zB-H(_C$tEJB`vyPlPj zXB7TX%C_QA2pEEI1#TLKR~Ux(B^Jg4E#aPlb_Sk=kGRYqWj}-|80#!F7ldI!_-2R* fhrcAd|4q{WAcwyohaZO~1Yy_m!T%6^bGZKl@n!!p literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/codec_options.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/codec_options.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2b3c54e541b4c326b5338ac8b6eb5f95ee26196 GIT binary patch literal 22805 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 X`Cr8|Qd?%r$f`8)*wZNYvbFj@?i+O7 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/datetime_ms.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/datetime_ms.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b25af44993ce4e08a3f20925f6abdb0af0bd6751 GIT binary patch literal 9251 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 M)%VPbEtE9>1=>Va%>V!Z literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/dbref.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/dbref.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4afe59daad721d01e8f814c7f14715a13d763978 GIT binary patch literal 6489 zcmbtYeQXow8GrA5cKq$c&IbV!ju1Xf!KN%-y9_AM&?r#CN~kCTWM06T4|1g6z@beq-~n|U)EIG%3ph)JKxy} z?y7ZX`Te@*eeQjq=l6WP@25VWo4~X6Q#QE}B;>EyF(1xoFsmXoW{F5*L}WxZ#iZF7 z%V67&U(Cl4mh=*_?g|l| zw^-vVG5@41?9LxPBg-kukH`w27Bb0fE+weR%qZV;^t3cW`CW=MA}LZPAyHnH`IB;H zRPH&-i*h2DmNF_0N4`LIVMg-^nT)K$WI02j>l7s^n~<}UVVh<f5>hGX2OC%CwKvb;A#B3rP z<0N~`A=Sm4qAlif5f>5bv3&d|Hi-5s_Ly7ph(XbDg}YV$M$8K%ArMw�MjdqO)qm zFE)v;D_krfHj8ek>w$*{>IShz^gL}FQFF)RqM!=H0+lppJbqz9P)6w& z2%!Fiy}To=9PsxL!mK(N6q*X9@e~-fj0i$ZY|s|fnkzDDU4`~mdEO23Hfy%uU<$U^ zcUEzTZ^oGXR%i zSS95TjHzmtc1NSI|FPU~B#n+*9FEA!Xfy$?Bx=wd8j44(HRYeEl6o3e!ViPG*Mbv3 zxd*|^_vgcwXugd$?9$vxYGPco0Taz-S&PQ0U^rSZuG`ldm*zIdz_HbvZS%95BbSA? zr1{M^h8n^SjiXX(L~~cRqp`rjS(zD^E~$#3IgA+;orhu5kA|aTa$1T~fkOC;{z#F> z!M)PxX?a*ylL;CvOMdSWN|U1*jCs+>MJaPJno15wvy+ncs%dd_Z%_%9k34iV<@J`SNll)#<|_UPTUXdeh}PLZ14N5 z^Im@Gc&ym>N^$e~rQl%EJ6Q6Bik>aMb>DvGgMs_Ij_7o~pK1|z z(R=RU<{i`AeP?T_v3Z(%(^cBo0ac*n^k4H`^<5wNd1{`$@7z|}&_2!m%(sdTF=2%R z=wKUSEVH^18ndK8#-U#S3_#4!@9LFQ-2^M93+qKdK zAmZS!W91AqW+BU}jg|s~pB^CFNF_DsBnl*^l|-Y!+$y6`ByX@27ta9vyxItYw}ztM6|rEGbutL4u>5IE>(9F z3L2$?fK|{p>9DYr50P(;@*M(=!&u&H=-ucI!Zi9K6yR~)BoMs*{9MmFr`|eM4DDIS z-4@>)y)#;j94ZE%UGg8Ewv{}AYX`3$EP6Uit!?kLz124T+;#ia<0Yr(n)|BzdKaSQ zzH@6S5S(^D?h0_(IuZ^8uHiVxjF<5ZGf5cw?o;m2ciKuvr5u zH`sy=zGi>hF+je{Y#sA8@NDsQ4#57H@(w0Q*wL?%JiCW~#2m~!bd~OU#Dp>UkITu7 z;UVl0gwxaj2nH4%FJaE`4$K9Oxv1IoOn{=bVFsWFAq>z6>goV(^v8gV&OtFnO7+3n zx|zD$J@1{ob8@+>cd@H?sjF{!!||Iv3r7}So7+Dp%ni&l^IPYee$l?P;rLShiD|n9 zAimVJsf@Huk%i&gjqkPGX}RsWd+hGhA8jp$dWznjQbWre_fFugK(S@_LdU|HchBBD zyKv&Ra<}utz3=ZW{^0n%r|#`6zIeJA7$`aizFw^(ttS|YK)D$_y3w7eh7oR0hu#V_ zs+~}+)1D%L@^Qwxr#4t>0|iEFfMkTb44|yZo2dmvpsfKy7_H44pkAb^_q(x zAj)oNhwGFOR0@t8aSsy^Bh>5B#NRKged%mU!kkVLO_bPWIF2Y9o1>7Yjscq1BE=Dp zORoV=%1*`TWq>n%6^beHz|(YH`sGXaJ=;pb=Go{>biQkOTi4>YF7PeMJIRk4KWY28 zt=MpC`e>=Kb-A%~v9WVrTHf)@;*MwTZvSxi`@27qKFNHXDTZE}?kRe zfwrQv?eXY~D%aJFnF(fU1qQ|81%c>7xJk<1Q8|Q5L~;}Iu#t>Zy%Fr?kP1)J0QXSK z+*30Li{FrS!Bv9s<=fC$1F)L@?0*56YQ-xRzoI`d-%_wSMo6uLzv(5-SZ3BEoY~Sd z*Dyvlfgcm-7;l^(@dixKsLbQN?qh}`VW-9+nV!HJ_`zI8v#U7(kLIAV0`SrOkMcCU zSf5{^bHVx5&c*0o0&02~iYfB9K&a$vSoXCq`r7BY-?)C|TIhQB*_+RPaO@8|?lu0Y z?T>BCedibZ&M)=7x^y(YbYQ6H6;^F*y?ceQu5#$9wtp0%4vgPJqgp*Gj2h23sDv@^ zfYwmfaMX~lmA|-#vZnpkq^x1n6`r@y?BLOrwR0es&4QTukz7W<`_iu~;ld?588NRJ zcZES!4#V7W?mBg?Nfo%Xl$O_61Kg~cj6hF8GgGXL{RCLjJ}6LEjSswy*Uv0B?O1Hu zvA`{P_v-!Gb2I1Wp|{=W#b@I4;*$3Xqc=D+IN!764X?WFs5E<7X<@d%KkPMfn?_u9 zI~M5E^&s}hXWo^*0%zz!g@+gUP1acXMit-f!ONY&L1n8sUvnt>aY5toxBzaTAK&0N z%r<_-Y@ft7r|d-<{p&h)6-C|$DPZM8K#LxwLaTAZaF1MOr`eJj6-Z93A<=acZbo~+ z<6=CHcb92d%%!jm8h9-yq{?sVKp%1fhFx*vVi*E6A`8hNeK&55;|jtYhpanew6N5A7XI?iykac(aH;0JcmUm79CjV(F6;00rX=i zu8==--+x$tc&e`y>X>>#FCK=Pr(Rg`l5IOnk^L*4t@bSs8(UWFP{U!?SHDt+Ehh=_ zD=uugNy8qU3Z{gbSA00;CvN|009(&9#Nq#vW$jy6ZO~f57ECtATCK6x@|fMer96f$ zKWE>!>UG&etIeGK472LB+uK+D4DfPrcK27_I=kUK^o{k01IkOVCDhAE4$hjO7X|%= z#-XVxXjYm7#0$6hhUU>ND0n-p`S8Xg5+9RNn9S?CGPL8=U%*Qvi`NCXR;Z7h2MiP( zQigyP=EyV*1st>(=AZ3^>194A9e*dSe<9&T68;D2{+w+1mm|e6?k@?Jx_T>oNgZ_$YIkXg z=VQ&dN=w#4L?v!@s^wa4)rN*sR0RUmK?Br4f*?Tt-1*dq*{Z0D7)bO_HeI+i0=P5Z zzV42+BqU9{5@)~t9<$%hH#6USGjl(4yB!o4+!!*FyD4OXyD4r=m_ugLW{z7D){r$}3)vF(kUilDITFs0leAgl zu7o?}PIy8dnlezFX?lzLQvJ?aKcMBOX-2PAOCkS5jN7jE*6*Q8))uN_?V)Pc5vpOG zp<31zs$<=ude#$aV7;MDtS_{gtLHYSVGv7C8bXa+J-Kh;8j5PSat)-G;Wic3HgTIs zZ8KNL8K;Nz_o^kXmaAd?QxvowR_pXT)IP00sU@g=RxRuIDI@D=E3Vo?EkFt5^fA3f zEwP(o6xT96sy^%Y50v6{`df795zYzY2_NV^u*5ye?c(fQ8)xI%>hZa4(uns-6T(o? zKpmpk%JURk#d$t3LKW7wliF&it>N5SZ3nxVtvzoFb+V0Y9o%=5UiHwc;RE0okP!sTlCeKIC)q~1GqYSW z%1P$2*_k*enZ^LEWImCE5dnkb#77vDVmV%Z7)+9fv|-Ns#c`_cEf}6-kX5bc$M-ns9qWMwE8qsTyK<#CUqe3)DwSlEWM$W{VS;Kjg${8Uu zYhjJBQkFSWz$$qZqA8yqalyn6rjkH*QDBbEiKkLYrWal)2)*OV0tJ;JVuD6%eU~Os!knd*>0_CGeXUGD!;*y7XZYm~-{M>$An&Zkt z>q`n=Wsa?ZlYxMSYeGmRgRshgtM#>Jwz*g3BXydsEuF!RmcaZj2Kz+A@N(ps{Wyt$ zJj?Zb)*bBZA+Lxks)|q>%Rp*+%|pm6l1c=l)cf=ZZ-Nr=p-JH4lLGc-zMN`5l}d2! zLPSVJlI_R%)HD|rh4$mAiIfd;>JXw>p$J!IHmk;5S31WPUXgpXCHZ z$uN8)R{4N&lLAP7A}miOy|rwH+_Ba_Z@whSY76dyHpqK)a+rr^wB*BHlk|P{Wq}bUwmR;fSnb}BOX|aaGY$^)X zwi!M(1MEM?aA=rNHA9N74ZG0NF zLfOR{V$c(9rBNziOn>t@(JBHHNiwmdAV$D;U{aIo_CJs|kvk`jGm#n86?`lra%8hu zXWI-94i2{M?dfKK*liOrQQe!E$#@D37chZlQn94S$SN~Pknq(oOG43r33SxKu@}Ne zkBto<9T}BO(G;k9tFlCrRhbAdlL>dTV&1Ng@*GS{^!oXtfH;&wPR$M%=bRh3+&W-`dndDU}*%8sWBnxyNVDV-# zjZ#r|i6*r|-AH<xm;yR*1OE^F-; z(WSPIQldvM!#EV8vP&KJGJN5@Vcs}zf_{}{o#0XJ$Dha;d8df0NG{f(e#sj&jI34b z^}9Cm!86BAgj_Il+=7+z1HTw|W~HyF9mxO(>P2#~#_4TpS-)qD1Jp%x)G!4m!b#YU z=glJShvdqb)$h7wxZpKGk9n;|ZJD=ZEYqkz$}aVb=dG-1-Ufem5p@f>#7ETLY;}qm_$@^+CFI*rM6L>@TF(ri2fo)!@p(v!XKYAzDS*=qfm$R zKT8Fi>6B&-L{xpFYI9<-4-YV{r(#p57*I&UE+(EjOG*L70O*~G@R5Y9l6sl`QdL;C zJ2=pqA4#{2L`LMp9Lr72f>Mjjfqr9|n83gZjS*&NW>UP!u@c3jTDbcp03HSJZYO{^ zs)es!$A3mD_SFHXZikvcJ^wh|Bv(8Yjl>n@B{{JmJ9?jvXBt92pBg zHQYa1{CuQ;^cg@mI(%p(;3Af*WQ>aE;QRoymMm;+3f4!mapz`INjMltW>OAt>IsCWmjd^?H)zbz0BljrU{K!gl)mM4T*PQn?U+XFO0*mG^j8J{gLfPE6taW*7 z-A!x5YF*>I-Yedlb%6`BIa~Ml5Zq~Oe)p9tuM`^F@}>sZUUn|G-m$v=B=e2T`9J)H z(|@~SbFOiyP;n^dI<)HaUEII4|H6S)7!$q{zS$UjYfsMA`~4vR8{z(>qI!Aa^3g^DK^o>}#8zU6Pp`&$bBz{1maoG#ep9L|M&+Ou?I>EJyxw8L)csa)Q@ zoG!SVaX@qV0N-*y44=5N^U{&Lt7&NfM>XHGQMFsIZ^_rRFC4vNbuRMXNH0J4#_Oxz z%H?FKJlX0G5|~c2CM-*e@5^Ycv%yA3s@g(hP#!sfp^MMFV0MXHWMorsw{1AaA*^l2y-dtb969mG}5A1~d&Ajk_4|st^qZ)~PaSW@DYsO!BD& z^HeMu;pdoPFk7cMUL_yX`qc1103C9Ph^Q!k@PvTI249V4^!}kvj&WvI;2zpKRwf3D zdongP%X2Jo{b?4lj+Qxlic8{t6NA$rv59d5;zaQL$rdrgjDpGoM;H^2o#wQespJ_> zlCekO&`z9zK&NsF4ky706&1&KDU$-#xvtMq@Ogn*eiqDno@3yEH8TTq#-4m61`3sl z2;j(Vzj-!oF2W*OZ$iik7-d! zCR+&TSRI{9iji|IeJ3@fARWbJm0Lg(Tbnxv+xB$s-TVLfZDb)g_#|bCAO1`BbasQf z&_4D5c#%|Y+u%#ork1{4O!>kcM@KQo;#Vp&A4@S@QULiPj&zD)Uxn~e63m)c)w zmMKHV%jF}MKod~qQH|=TiWR0Lqs4RJ7yeqTt@?7|KoUMsS-RLtY%N6@z%K>MN*6N_ z8)6OV-`ooA!O|DE5<%|Lx0F(GD;kUTueQ4oeNgg&zxR1+VyV-oSTPjOw1Y_Sp5JP= zV0jN8>3EawZ)rk-&C}0oOtOnwD2?PM=9671TxQlwA)>Av7cLxtTO|zY@)FJ!tQ;lcf81S2oHMfDLRju>RDM z_YnY@yP5s_i?1nh7K_ei+qdbkB0*?b5o16sGQMP=` z4MBwtDiLDfTbD!9ASPe)L5dg`%|xlurZ5Disr)S%nhJ->aoijZgE4s*9fc*)0?noE zgJX8F$23EWn$J}Cly2mH$nJCB{nT1E#8i2*o>W|McyBSX22!vqTdywP}fEbV{ zHA4a7m7sp!)y$QJ4;KMDa*Z9=#-zKdg6GmfKfSg{~MA&I)4o#M5L1*AV{< zjMz8Pv?t%9X(vDl(N5npSa&h;Cmw`7d0ku^7d!2tMuUq48PD@M-c#@M%v3UrUAtFEP*oJj_6Iu|lI7 z_^Ibj52S`YQ$`K&LL)ZkhS>DTg-eypc{A&tw}`szdcZB@QEjjC-`Y7A(x9<^rPR159AZ=207o+n#vwNr+OSMKi<*A(x|QOegr>V* z1gO#Ok~jcL``xeLx{EH!yBKgMVF9?q1^H4Pa4U&CqUPvA-Lo4C&pY^ktf+F8EE<9QAj`vouKyfC)YaqhZ%J1Uq<)F&78nzqIe0;=pk{nrBZam~?d+*?An=W;q}H>s6S)Y_N9OnwSeZgVHY)ra z$$?J4F!mAnr?BZDf&l;l2g%Xl$FU-U;3Whn0Z2wbD47xw;WR&il`kU*0T4ixDRupo zlpY?bp03teq=+hRB*D)CEKuP0_ElYcZRs`eYkMj#PA^R_8dt09uMU3u<)t$#G`O!_ zzPEaop1peFV`ua2%7)7aE*-cwu+qE|y>_5b*_Cs3!LYZ+mQG(~KX(6?9q<)>c~{f* z;af zw`tLI+g}6WgU;+k!QTYI0DI$N|7uNrcHq+VHGZZ4y;pBEy_d*WcP}1Xrk93RD{Hfz zmmVh(0ed4-rdh^t2I2+Bzq3_C(2yOwbb5te>3`o02r~Cauv$@*t-AE+wdTBk`=W8N zf64N>g{r6pZu_3_{jPuP`-i@dwtX~P=sHqpAGuj`wBSFwX!*olb<5q9cQ;*swBQax zV8{L>ePi2dWzDV19r?-~*Tj|QZ@iG(*;lAM0O1{I{DO4(jKD8RJn1KORYQA>KiOj* zI$$j^Z&7lIN$@cgzRtW=Rhdw$=V!3AsHFku#^&49J4NAhUCHV_Sc77YI^l)nVzrI` z4cY)oL4?4ye8Dyf*^mudY5{A(J)xxC%$myE!5{`^#tX7Utm$7JB$*Xomn5Q#1gO^a z&Ua0bD*Y6fzT$pIDJu;PbnXGCn}{wu^!ziepnyms3Hs3)$Si5w-O=%w>O#}fO0>5i zLOI0*{KpRw27n+i4#6ueJ3g2MTGQY(EK7SY)4n%qTeCB23CMYQg!>8*T#z|TXc z5wambPSI(2kj%1*I)h~sCNAu1R(>$w

20}Pu_?D@=*Qwu2Y>Y1M~U3#u|ma(oa@AGr)P2Y z&E9Oq$IeY88o9qv(U)`e{lZ)E*3;S9YtLOfo}DXH1y-8!RlB}7{bBWo{73YM^@ZTU zkNWe$pz#tK9To6k+VIqo;0}E6Vl)|gZuaWGUrI95RNFjK2V-{dXCoB ziLM}kI0lHjnICFI4S@h2HsE?UA_kPHd}2j{SgqK{SThVg^9!7xa+ zgFP*^lqz_(!iM1VTK0$A}qdBWaiZ zzSfqsp(Q}9taX`-a8TsA8JuA0K~b(ehBNfbGxRrR+3X9~j_16sIa{kdA3R~Jm$n3G zpPQK=pykA3+-GY=Tp&g{v3Y-YpbC7)&PS@%m1P58YVHD?xdIPA~ z+>*C#T{K;AE|2|LXg$erx-d=I(j#0V&!z-!e8$AA894#;ZRov@j4;)yubZmg@H|cx z4@H<=S&7z6cHrHouRNXe?#$VCt|zGpx{~>hL!m_Ai#$B)7RcAb%yC571zB+$)Z%Fn zvbMCdv6@Y4A_|qqHRQhoK=O>F#9`I@&9Pc;b_#rbe93Zwzlu8p3*|3aD6D!BzZ(xI zs=pz0)7c7E%;m=}J(it)ckaquzOpsvYQ61k%-I^((?MG|nbkAETsVIhe1W_syKuVi z0VtnP5;>=ILRj?*P6!VUun=HO^<3JYD^3fSv%ew3=K20-PYkL_lO*$1GL1kEjgkh- z_W+83g};ce??$j4!36|65g-}WzF4c4(ZW`8%)j)KpOm}=SwI+?6;H||zgyIMMehAq zaMLr=CDR1_3MNpoQBtjB2LXwvM1ka{<9--UMEKJj&+~`_EpEw40=ej3PqF+mRzHdW z*POqM;0l7P2>uemUnBS%1ccalz?HMu39BG@2mXX<0GP%85)EnWX#?fozA${JvU=g! zHIvJHh+f;=Vt$gocbujy{?80f^DvF+yZ(Emc&~w^y&KH?l$>5H+05p;H4kkLtU-2f zD}t6ai_yI0bBC4>PiBC#QplV3vP!-KM5i&oR!!&sTj(Q`myooZWFTWE|Ku!$C4nz^ zkT%&zCs{yk&qhV!tCK81Lok|T>Z5VV+Yq4FipMjJRDrqG!IGba@MuQzE^M`o5?t^b z9SGg?-zHVFN%HV5?4+N^+Y0;NLho*@WAk);jvNvn>g}3f9FctG0e;D*hKwbv5(ek@;`m+!k0CgS znBZqbkaesd6nR3~I&2Rj*oS~cpsfv&YQiyJ+SK#|{)f;7M3EBC0K8|UY5LPGM%whB zHj3W%OUm=Fl>6VPP5(}H{|nXqD{Ai@XWil+%;?HiL4NTq=eE3a+k$P?-LUxl#gj`X zv)u(ZbIaY5cegA!?)d8#Jgfd{0KTdP_o}ZNfXBDsyko6jFs%|`YgjO^BDiCBFPoP( zFQ0*|e5kU!7tbu(Nnyikhj)3$Qd`!avu|3ktiqFjv0=discY`KRbTBbUrXNCvg)eG z|33e!+q>$izUA4O_v~D;-fDd;-}=~xE&ts9gZ7;J=~cJqQ@`D3Sa98|p}bobr_cz< zd%CZ=ZZVyCrnA6w!PA0s6^@5zFTZ~2^%e7qaAWYpZ6BWdsqqK%KaJ*kpUZb2&($8! w`$rc%pIf|i)ACE7Q*iryyO(a3%gt-sE%dgviZ;6Xi*foXBMnDPgk(wn2e=;9V*mgE literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/errors.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac630dc94755b0c266e3b541ef753bbe561a94fe GIT binary patch literal 1417 zcmb7^&2AGh5XbFJnr54ZQXvEq5*R8W<&dm^3ld+VN;?Rk4$EiCQIcNigsXY{?S?X&lnOjd=b*AFY4nM1GqSofl`2Mpm?}k@LJ}Is z$N>s z?!A=aEeo{rwnaneC{W%D!CRudT70fl)Z!h+wO86@eAkPkhS%-uw%`v+_wIWt(yTa6 zUU>~AxW@oqmAZWiyJ*c7<0y#s&?sG$i>LZbmC053E{&rQ3N(1Ko$SGfHseIge#Bcu z3ld01H6t{slW0Izq$x#*M9Ie_q?(qhG6M)XFcu~c-qNYLW}FG`Mlu5iXTn_`L$nIHbe{FTQ!b{_rB~Wxxh=V;Z}XV1qsr_R<);EP@_v z`=ER_^!b&h{BNi*zD<^G-ptzKyW8FM{*!+o7qjX`P-HM?ZlJpL{ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/int64.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/int64.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d2cfdd50064f797526ea16012df8523eb9f23f3 GIT binary patch literal 1199 zcmZuwO>fgM7`D@-?LG>u)8GQgfP_|wHDa4~s1VWy5}E`BF;2N;ZsK-fiCyfZQ+C>6 zhy8;|{DX;~vOj<#At50SyKO35IPuykpPE(Kk6pj-^E~hCoHiQM2*#aLD|&Au^hIxe z4W|TnyawPHg~&r84y_m`mSUJsTqnJwu^=h(d=sA1Y;>SIw)f+Wz7?+1Ps7 zC3}(%hfI>bkR%p-Ko%pOl8BRGp6&>~(mK;&EnLh{&P7VoNN@$)$~w=3GGjSg<$}kH z0FDtu9tLSvh`mZ^d3IRwoUjIBY_A&DLkHFx2$?FJO`fi;QWqoinhhmWjF&7#^hvtI zN|_*PZIz_?khQ0$35+*8ks|QpEMQ7enUfw1Xr>rpBL+JNU;{~G!n33Y{@^O*q!Ywc zsm?b1yA4-NrdP%&S!t84gvRmzkq{>#DGx-*LXwK#dRCT6FDD`E(=1LOm{h$iCC1K# zRgt|2GSYaD<_ZK}mS$~r;;1vEk|tzCV50Ty|BIvZWyncIkjAx2FVmO|#m=yqNX<0ZSG1 zlllIB)0C$D_t`y=qO`aJ^zrK4(bO0q3MNlUV>65)_zb{Mj->uFN<)^M0bDqSKsw))+Y7Wi+1rQ}g{$1aLeBQeu%Rb3;F1KcRA$N!ifJ=k%ltU?dpvQn$`b z3jowDm=4i}Rded+O*A)uu=#m@;o$k$#!mBF6+4YGQU`TgHF*P8Evuh_5s)RJc&MlX zal{A4OHqOTlqCNW|ENDI%{-QxR6i4S6Q=VD#`xkk!tN(@_ba;ot=hzmqbJ`HEQb6i DS9UuM literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/json_util.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/json_util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a44545ae1ed63e11c8b37c8b3747b92d789021f GIT binary patch literal 46525 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 Rjm-URT%!wr5)f(Ce*nF9#G(KI literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/min_key.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/min_key.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33af616cb8091809f7b8a25cd702f2ad09e18fc3 GIT binary patch literal 2167 zcmaJ?&rjS&6rS;V%>ug^*o72ST9cH3Rah%+s#fX`i7IMUwLw)<^{^Mq>oJ)z zkrr{_kVB8{3I2dwQ2vx$w&Kv#Y9*wqhuqq3q)NH8?|GJm1%eN6-g|!I@0)MljGvWC zBLtrLXGVCOC*%(dP99S`w0j$zeZokMFv^S;jf|Q>(a*JVky$e-G02yMnU4v}KQ;8M zTE1<$Lp!(c@h!;}7fFzWv2ZqG=_F0=+>J#ezVo>giiArMw4A%4_?ow!q`k$fFYpsC zO-BPEVm(nom|GR?y8gPhJt*33LcC8nsZk(lFj~toqh_*PEzfMAW!5Y<&hju0u?c3u zSYVTE2*zQE3owqbG8=|*6ygyWi!hGDSb}^J#xYi5B^d4Ym^+nPn(WTpm8&=;Fa85w zb$W&}`Z+>q*UA`&A{kIIo!3j3e%;Kt+M@zLoI*_yCl_DP#aY^iKb zt|5!yr}+#);Vj3IB}@Ubx(xX=`8Jd(=t)E_6r~N`04~C1xYxniCrMxDU7GayowVb9 z&FaudM?w_};3Mi>;w~JXaEzfZJ*kV z!#)cr0Ct+;GB^Va$%8&ac-4EJ_U87ux<+a?1Jwkn@KpFwAUC;`qfo7TLc;}bO!q1h3Bl~8jaPy*^NGOwY44i$kOLpnz5+Oxs(bEl;)A&s+ zJ=sUlNMP@rYJ?$7BuZ=aynOB8gP%U_*iNV5T!e)}4U5|e-9uet?K^!SPBw;ZM^XDc zXloD@!*>z1*D!YZB2>R&-pQb@ZkW?`SD^aRb+?j%bz@jX3X@#-Jb=-a_UxXf7NcNM4Zri%fPo53kOv-56XdhWJzHt6k9u zXPf^rgq?zJ(IrxeMVrac*5H6xk|*0zNZeZ2loK#yzQhxknrpG( zkiK>9Q=Wx1h?RHro?^hSExj~<0+go5k#PBI^z3L%(k)R@EUu#9khCH zhJ2sJbx04x?beDD<@*vZP>E+sVqeHN(9EI1`%T_Jg9lYEqH)n+A8UPZ+p<%h-Bg*G zf|ObX^O*c7j%K2$LxoteG1JKAh70jxB9kaK zWtxi3ndV|LlPtDmTG&{m&{}NEw6S(Wp}p9V>0s?>VMDPq(}{L0(-q+4$n3+;VZB;v zyn*x5Gdp6{{xY-a8|TIU?7Yq2IM3T}zkesIo!Kh2NQt*YnQc<5)P%Yxz&*`L&2Mv3 z@a=#`u<-okteiK;B_n-@=B4;r zTvS!f6ir1_jS0Lbj+YHnE*=wANh_wpwcsJOT#HU<$7HjltF`F!a|9|DYM~S5Ik^@- zsp1v$+Ov{q%BE73v!<3UDuse#T<2@CarLxVP^57QFu{_USHAxGYpxPFI9>@I9N%N) zZ9KDE5Vr(5j$hq|#ud(V$0UB9t3)`?!R`qQG`>?>8VQrcf*y zrZ6dEaCl^JvTVwNqRy2}0mu=Bkw*seGomi$O<5OaOhwsx6l&Q6v?3dg?sYQ zj8>F~4ACfx>d^OejZkO|9n&T?Q^^}cHf{$G8HO^g7G>2ON}rb1(?bPia%iq>&S>iH z^zg`#p_uaEoR~i)PRqv7q@k%p8Y71yrRU1E)~qfnhMZL_gk=>;Z^iyQ@n>v7vB2GK zY+niRvHs-;2fqmNvGi&%7>}%Q!Ej_1<6NE!=Lw>LKYn!p4L>SpZH93Gsg`&LvB$Yo zpz_euvMHD|varX(n$CDZm{R0|BxqBDsJeJg-w9)WDo_h(My;V_L0;$dHuUN(C~8f( z7O_&62w>-8EeOA%PZGIGM}v&>;bd&jcY$ zp;@MRu#)e&VYf38Kr}dSvxLhvj}(w&uKY9tIzCIsTk|+8WSRic?7k(XV5UV1Wm=_f zX_FLrJDh2g+ojD?!`mSjrUN}&q^PfF1A4YfF<(!ov`uP+Om|5=QXKV0xf>9Wc6XIc z=n+Wn-JZ>WXa?*SsaHy(-YRXET2OC8-HN(L>XX_~3#i*s_oD7Vy&d%i)P1NsQSU(A zg?gv7L)wV?fpR#t6C!RaiAwvS`xHl7C;ovfhqB<%I}Lc5$opB!mEo%xy28!#3$63q ztSiYLa-G}oT$wkCUD-3wn>NiM?e@BQr%C(R;|{Azfm8ipvgkZ?L)M*B#@lS%f%fWo zm&IwnR8Y)-amQ7~RG^(Iq$5=MjK|iEje`9g@|c~Jji*aWs`7dXDn)|8E07Ic$U}uH zhDl0Sn;P|&5QIU@d&CkZ=qb$72%Q<||pUUNg zye>n(69NcP_gy|MoS2c_b85Dc5ays^i!!O$926>6f$kuUtO}#~f@m0{?k2ebrkB++ zl(oB#1H^Pk&UetKpu^>}VsWk@4=`*Ej|dD%nA|m`X@irZKKOd6bas~@Y0N-OX@!Dz zhJiZQa(6N2r+jflBk@euggD%VTuh}@|E-!d6 zyY}wgJGFOeMA|#DcX;2(h_r8b-yZzizboYt!NZhTFf_}6If#If%8?2ld4$<4Ntsqm z#8|P%a_Q4Tdbg|g5WZ!A$(`XH0MT#h5$5EnoJkTr;;Yqnm#?0*L2yn z=ElKXwtlnIvTndlg2SOs%M$k7o!;-_0CaFJZQ;`O3LH+&frZZq3UNCf&R}fp9PEgH zp?X%fw-p{vdm@G1*gXtf1k6Jh$!o*b5E)p zCcCu5QxkU*(7mY$+(PFP+3d8e$~u%ttx60(7e2H^SL8G8UNe@x;pkrLa>|@zo{eFCrCcl zQ)u{oEWbIsVVgdQuVAkG7N}s(8-V~f&&>y%UU*+Y@mbiiF{W(6!BVMMEW7#x{;P#G z%c&OyhgyP-TJV@aF1U~TPRv05pn_*KiR@R&kf%xo79dC(86Q}JX<2|J(-jCc=8Kl2 z=aLzA$rOQEz&dQ|;1{B4AIR|S2;n*->NEk0$zcf8fcPfgywc&G@ZQ_Ah|$qM|ZeMuyk2WU6c!!gLAN3iguh zuV}iv8~)kW_Ju-OnA5e>F#c%|&Y=R-5;L==cN+f^#*9{iBMmbMVn{G!`2wzTTE?1+ z>QKUm&zwixED<+{)e8lh4{SliO;UrJaPq`hT9^>Akl;jO?yfGG+^HtxFi8+enP{cy z8QbO+&@Yu8I_#(h$zf$grTf+;%vM+jBYhFlVr* zN5x4Q!Ky4gE2_XgjQ@Zz^6>D(h+Zum`RpxOTg+oP5FT?cnRag5PZI}dg<;RQ(Ih5B zI0Pm+9F?BZ5t6twon`Ci!9#;5gjsnw4~Y37UFj)BmHL0ct~4tAVEc}OxqZ~#;EgwY zX52PKYlt`M3elU+?(PM0ghQJxTx^j5F(Szh(i|aYLQE`dos)#Yy`DQ=`A=vlBMN2X zJP6ie+3y`1`)+o8;)G5CR4P~t<_ktGx#p~vj>UWqXRxEWI=odn-3{ydPhpBNgkpia zyPs?7yxE!haA>J>^f%IC=i#Nq*mCnQ0@$XJl^_>Od=ceZ+Ar5F8ltGS) zXuB#_4uI5@-&w| zdWi%lrh9{@sBD&~x8^yu5BKV1F`GvzH@b7?`y)E6<7Z!cJ(x~2@-GfEXYG`QO@>+!FmpQS%cyjstC$lt^9oDX6h4v!GdP%N{-SQg-b5lM^SNJeGao&~qoBthKlbHGAyP#F6KZ)`E&^ z)jv}(P!T}Bj;R_w2f5Pjp2+L`d(1NE*2tPQZMfCcv)I)01s8}N z;%~QYx-xiq@Z8w)R^i(4PhY&5dh|EqV(Rh5El<4j%sWTF3KP)XMsCB-UmX8s$FI7t zci-5&v~$ltp1rm2>BW6dFYOy&>Uj3wUar2F`S`W!_g}5PmaQJmEq3J2g?E#br64Rwhh;wYvlp0Ie;rZ=d>drH~iB)x5(Ur#-NqFBRv%0Vvy zj5S;(;I6UP<3Z01NIXjmZGn++xp6&C^Jc8slk@|7JP4$Wf*vPI0ki9Vn4fX)xqkc) zc^D!}aI(jwWGH)>A!>#ct-A-zQ_wX}ArTzRy+EmRxT;8)kiTmshNj`-sbq5Q=U>nA z*g?8$Qpn0-3M?B-mt(#YN+6ry`1O=s@G?nGua$gZ!MV$pg1x>Q@3Zw?2VZn;9J5wF zzr-EzThg_Wx(jrN^0Ae@IJle3uRnP|I~?V=kmPbsml|M~oJb{f0rM?6j!eSo*UL-> zYeB&4+i9#9MXiCn7kmTI)AZH|te33Z1IxfejFDu}St6d)lH&_OVA<_JpE`kcI)4RN z<8M&F7PN7R&P(}s_uq`~Kxp~%#-BD)cxgqR$8R?E-cGiW3v_kl$8W9#f~^nT7IuEn z^nTNa`6XfG(viO%zZ>ORw_oe~q~(D>wr#F%J8|>m%hk-QODA8ep2$|W7U|e|E&tn&2Vv%0AG&7{8~$@((_xPLh}(2{Yw)8jJjyy_PcqA7 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/regex.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/regex.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..716226eed8845d0a0caf610db207b1dac36d3ea2 GIT binary patch literal 5650 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/son.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/son.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37e61b39ae40dbffe92cdf86029ae2f132b8a78a GIT binary patch literal 9453 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 F{{|p<{(1la literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/__pycache__/tz_util.cpython-312.pyc b/.venv/lib/python3.12/site-packages/bson/__pycache__/tz_util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38603287e90298d9c727571f917a6b2a79037dc6 GIT binary patch literal 2510 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 B{Vo6i literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/_cbson.cpython-311-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/bson/_cbson.cpython-311-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..64f14b9d740251745520efc6ca7509d76ff6cbd6 GIT binary patch literal 340448 zcmeFadwdi{)&@L75)6WNT!MyML$M^m5{eC-2 zrq88LojP^u)T!#~p4mnIey!YYm;Fg{-QY?CK~PU@?($~F7W>ZT(wTRp1}_X&ZOOD3>Ss>{hAnIc`tJBQ8&?d2?N!D;Ll4)I4A9zA%}* z{M?0qlksl~{#D}NRQ$UK|7PIdO#GXLfBei=U-EYj{?5g}H_qub(U*Qj*`?!te)nj{ z{gM6s$Nk~h?tdNsHEUPJ2c37Xt{;AWtFw#GEV*`S>#Wz_>0VkjeL>#5es>M|dc>lg z&)#30+^1&3zZPD1?trhKojv;P)gAhc%gJn;`)U2A%imaW)V=(l>BA=dr_B?4_m6c{ zeahuH7{<1-llT~%{j(dD$vMzMd!Ipje~W)Qbo?!Rw-)5=Z9$*B7UaJS$Nnw(=e8h!Xbbv` zZK1vQwV=##l^R+GX`}`LAF|#E-Td?ytEy$VM0{y;uh?-wgvl7YeCOJE!3OSf}LM%LH1g%>ri z6!0Z2wD$%XFKu0QOH`qDR@qn4-uAAxu8bEH;%W=O0^h`!yrK~Ifsdb0EdII|6%4b> z=SB;^#gboQ;kQ}!RxVYDn=E`c_!Z?Zo~IB$S@QdfA3~qE75)yipPxG{`6ZT~uPU@_ zt|h9RqrDfKF#unJPW@O4#4)-Sb~F)E1%NR@sp-Z zE}a$_JvC5T>MHdODJ~sbKDB&&#k4^A)FH)vCr+7MK4kQmiRFs++mzC>8KXI1$xPp*$|;bozKW-et%9hMnS;jMSzZ<>?Oz`71_DzD2c}j` z9uKsl6FoREI#BMaoC)p5PbnQ!HEvw_)Y9?g0qfFLHf9?14pdAkA3v#kx8{t~r&geJ z$;`f^E2p7Ct9A!sVEJ@FLnl|1O&MEW+HdNVNpT(gsIk;LjbHo+wjouOkfDkcRzPjX z<>2ywFHk-STzzH+%BML+!46h4`@$IgswS5ODxmXV*k}m&t+sl{j#XSh6-}L5I;gCy zYUDMEURuy}vHAAo-2szo(K%C6(ojg_;p{#f6_|lTmQ>T@y z_S$+T1oV+!pa|6+zG;1@Ob%3xubNUdt$7bjhJl;)-n7y8l$TDc99?!wzgAU_MQ@i@ zPMKCg6S<}ZrcNxMoT%vL@`>f6r=3!4+|+WmuxLhE=`EutR+YniQ>Herl|B;}OxNsF zRWWg_@W;Et(dCe5o4K%@&OuYN5_C;pXfc&q#e2Hnl&OUwNh2Lsj3u;-2!!ab)pmaF$OaFXY@@J}oe1s-wT1 zE{7V>w`8@{95S<#tm4ZUYvRB(%|Hxxcu`duo0Mp!axB|Z;!;>ead}|E6gsd}Kp8`( zRwZPxqEg?q!Bt}ds>*&7r;HBBASs+u#i&v;Q;bi;SfkC-5tV|-whg3_8Cl|Omk`r$ z^u%d)O)46UpTv03y;~_uT4JaqD?vQ7hHYulWQ4Yx%Q4KR!J-yJslOcV*&Hjyyp@&Z zlQ|ww($vzncywhYVppj?dfJWUGZXdNW>rmy_qnB2VfjQWYB!AUInN`OMKIOlQG3i#>ls- z9auiYF>I8vSS_rA4p&503!AE1;g3_MOcXW1rrgLFYg;fH*)8by4h=Hiil0Y|}IvRt6=A9twPOiYTDIEkh710aJ z$Cdhkcfo1Z6bMD&#Kq;4roiq~t(kFB`J}SSnRXXVsw`y)v_Of56_}Psk3)}*o;*Gg z`pTwM&MXZ~DuojaDaOR7oI|jLi4RP&u;WJGRSuUa8$FROf>9B_b~30@1FE{PNR#eT zW67m1T!f@J>MUi4Po5&8B|h06YSknoP~nu1K;P+ON862!<1n{dwZ#>K^uWE-$^#bO z9?`Ce6=Ny`6JWcsS5KRAbq?V&UUhf%_xt+vExq>YYp>3WU*~p1a^sf&TH$Xi>=zT_ z_5b!?g6FrcoTU?VH};vG(8+a?(wy3o94Fp+{Xdm=^8MEJ3&{1-V2=#^t2(!$4R_Di zTKOL}TW--oc0KmUACU1UyFRpVioaEy-@bog6?ZND9ZR`fqpW>=8((SRCp5#`aAh<6 z(jp~iMl<}rKP!B7Gkl%35kIdPK8Fh~d}^BEw^;J${jBJ=o+;}TytoIH8NSAnGpZTBnmXHhe}VaZwC3}0r+S<(!jbFY$9+YFzzS>Y#eV*?)y61lG=0RB`u@NH}) zzGpb_=R5G#4*Vq!{5%K#G6%lKfzNi}7di0PI`E4f`0E__B@TQy2Y#so-`#<)b>RCt z@O2Kn?!a$!;0HMHTO9a74*WI;evAXZ(}5rFz`OP-0LxrN)x;;&fwz{(LQHeuv85CL zq&x7HaV*A>13%Y+&vM|C9rzpvKE;9W;lR^Z?2pHRXB+H~?!cdJBk^70z_)eahdc20 z9sH>@H-v& zE)Klmz}tIYB=2+Jr#ko#JMhkX&9425iN14zgFn@QpXR`)Iq=pI1)-)p@EH#N3A-Js;Cngn+Z^};2Y#mme}e;WIPkq4_J5}@V7YdOC0!N4*XIFez*f)>%iaYz}GqOBOLgR4*YEn z{1yj(qyxXrfxq2>-|4{L;lLXXe5nJ!&w(H1z#n$tM?3JYpA|s=FLU5i9r!5@e3}D4 z)`3rV;L9EO32Yk~h-;J+65|C0rN)-E`z2Y*b}qbYw}?Q-eixRM?=Q{HBv zn=bhm{%T!4xFReWgSoQ_pG(#dc7xg6BJc{r-1;%=1b&S$w|vZ{0>4O@TQ}xnfuAGH zEgQ2&;3o-ltH!Jrcp+h?S!Sic4--x$JWAlXgt^scmI!<=VQ$@+9)YJ4=9Z0_Bk-Ms zxm9Ck2t0-`w`j~XfkzPL){N;A_-4Y~k}>z40OFDXgwqJ`6u1xJvj}ey_)1a3u`TP0?R zz(-dB<`#+R5%>UMZjG2Z0`DQrEfF(A;9Z2dC2FP#{3YQF2)hK{O1LZGeaG4U&4e=u z?-Y0q;R^|G5qJe*ZcUhV0>4I>TN36{fnOxdtq60mz|RrBgm8_(PZH*qgjp@{Lc*63 zt`zuT!rYoLM+rQaFt=jO5`pg}%q<7gBk)wh+-fj$1iq6nw;0R}fyWT$)`FQP@Cd@( zQZQWt-%OZW3Ff~4NdFV&7J|7`;68+N32zbjdcxNdt`qoL!q*XAD)8lmx#epv7Wg8< z++r|m1U{EAw|>oPf!h=2R)bk7@acrP#bAySxD{b;Etn+&AAJWfw-ii|zy}C(E5XbW zcn@K2A($Bg?;>14I8ER$3Ex22CGb|ly$SC-CjC#?LwKjaYY2M@ZxMI}VQvYSbppRe zxG&+Q0>4O@TLb1|fuAGHEdjGe;3o-l$!}H*ypV8z!j%F)Ojsv8O5nMKxwJP+1iqK> z0Ky)DrxNB8-^>yCPQqN;n;8O+A() zTw63H%!2GQvv*evvSjz~*9spCinruURATlZ3gn zHLC?)NSI4kvr^!P33CE6M+rQaa0THKf$t@JCt(j@eE*LhJy!4q45-Iaw(=|gaG(>W z!hyAFDs0oVQ|nqy0c)*q@V_AZdC&1H`-C3Zr`PZ8t=At;(%tLzk4^+SLcsl&fYhd) ztwQ(l>CLb?L!t+**k1bv=4tIw|e-4SmxSq>YMHVv}UQNtODO zBK08YOqA+jmkQhiVHq|$BaR*fbe4@~RF>)kSwQF5Xa-A#ZgZP$huh3n=$&yj#<9aq zO${HxNFj2$j0VClCgAOag9-ROz~-$mfU>KxlJM0D_$9(gad@a6IS#{iddTG(%sj`~ z4yV>5YnkYD)+2lM$Oa=7MfBhmZawmDK+_`)%zpI9w%cxBJFZ5L>@bFd-mqKM=N;mU zd?G?4hXxz-m9Sc-KYDn3Aj2Q|fhCq5VkJdr^c8OSL%2?RtWH~2=dQc$cJD}-z=Iy& zVYpatw8+))&A6-W_765Lhh11A^mQ_XMtg{|(z{|H)lZ)a?Y>l+n7x}ynj&phq%@n9 zmLRQGq~G$1Hxx;Fm;y5rrCv}Z>1C4Qy{v_vQ6%YUlHxtBg&tKT@g$Pso}`85DU!$5 z(37a^enrx4lAa(G z=0`E`IWs-Faw!oO1F!i}3>PbgS|Th4Uh|_E+AD@SA}j`8^P?EtieV!W76Y&OQ4ISq zyrIJuA}j`8^P?DcD28oBSPZ=8M=^Y$7`ic+HPucv&&*Bf?_f zH9v~sDaCM@2#bN&{3wQp6$9@Y+YI&{WW_L3F{C;ec%6{I)dcL2=3wA;f?=3qNOv&s zI>FFSF=RLxc%5L#Qw&)S23{u^G8F?Kld)UD>jVQI^nsE+91OfpFtkz(9tQ)j6Ab%X zi3Huj!0QCVPQ_5-VBmFv;X}nR+`+)>1OuNgMWmj=CUB6>NRVDvWvhX)8^~*ZR5v}R z80HaSbu_Q}Q4EhLh8iNkfaxj7YktTONY1UhZLM+aKvNShtoh7n0Bl}}%*%@J+X-Kp zfZrqhy9B(FaGwPH65-+m{1oBg2{=l)ECJs~cya>1i}1_@d>i3V0=|*(f&|=?@Ztn~ z1>t`t;PVKtNx*Fhf1H4i?FamQ0&XDuYXaU*xHU3y%O39$?wo*E63$A%FA>g9z)uk# zkbt9vZ%e@U5uTWU?;?DE0=|v#`~-X>;b#+YPr^$R@D+sX6YzP2w^`OPQl8{?tj8(C*b!8UzUJZ67G?JUn1N$0Y61}NCJ)$E|0_BA(+fRMHcn5@lR+b z6Yn+|nLT`wb@baakR>#Ij0ris26$g|R;J9l3G_=AT4vk?`Y{VFvuy%>zlF{?SvH>+ zr)-&J6WoI=w9K#xbPo$Hvugr;={mEDjF()zR63cn#Bvn z%E4dujVulJ{n*qLxiX#UKXZRsx5@e{_@hUAtN%ILvSD4cWz)|eS6`PpPMf?{d#es# zA6zx@G?#0fHfD3AFtek!tp9mI!;Q-{Ty_N=iGTkK^a$1uled8jawpbn*~yv4{sye# zmfd)vUa`f%2-TML&8Yu2wSH$>n~gwU1T{Rjwb`gw)O}n$CQYw5F4f&z){3H?5vgQN zK~=G?SesZsfS$f0l`W415U67KV;Bf|Hha3TH{(a*=>j`ep!j1f{~y?E67nhffOb|P zDtPFW67ltncUbO0?=9Y;5@)#*dt#rh8eLAKU(N=B3JR_R>X?NZtWfU)br>ikxVNcE zW(>5Xsx<^{D9lW!hv|_rwZ7h%@SItXF<8=c{<56EwWpYb3ndRktdNn!Xod&ysyXZ>jCmuMdp{$(<1YQARv1{ zzEzR=WpuH~e^q21c?QRLnve`AryDe`cTuT|t+l2=>g{)#*b zx$=T&hK@5gJyDu})uDq@OTMXpbr(t*quSi*;IO zQ9rb(!$9>%1DV(!J7f$no0|O5iKrnG97RH;(dZ5wzzG0-!K>X>sbu{2dqmXWB$t4V z##VrOa3za0VO-%05K90Wh_Boxz`218(ZRYNeVv2K z=x;PN1?#YBipUTB3;NKWb0N$ZSuX{C5d5r6y5hb7dGhw-%h-#N;*GpZ3t$d_Ot9qE z`6CCw(1jfk!D_)6PR&6|^`U;Oe5+E76rhTco80S*_D|J=F6=$_ElD)}hws>QaKaEx z#x_9y$S%m7F_khKjR(=DaNW$a$JO|PS7y2c**fhMNcX;j*$!4(*P}3EFwybe?!CiX zx|U@YLQ2Co{>W})9bmvqS%iB4P>TNLI+C?SQhU(RN_@}_xAhs8KG#YzQk5czyrZ2VB80nV1 zzmDi(pm@VZm48w5Z6YqgIxdmE)aY|qe)jCn9&OqB6}-$|qnB;f^N-F-b{}3_GZgx! z$MyY&J)t&#uY*cCmcFMs^u5g1cMP1EwO=3C_iU%W+`#QkML#4{90`XP63#r!%1a{V07#WK^ELS?Y>V7>o zEu#?Kk7_PZs5@y~?4yFa7XIsM7#+`N(cJ7xc;+wEI`S@hFwhGv?I(`2!8o7#&{vSE z1cn!fzYkmh>U8)r9b*t$Ar3{q+ii>zX6#_MhpzcQ%lP(DoyZxS(wj5cs0J9GMrB|6L=QFbbf4OeD* zh%{!XwwWR$VWqYhrf}0qkuPQMz0o)daTt(~DV34#GT(<;+T%E;@ipZt2k420Ujxse z!?xZ@vnYq{LkFN$MvHee8u?-di9>AI0O4JiX00S_zqRaR9>dlGjdL;B8s0$ri*moW z?ic8h&y-i}rW>MXZqj2zkfigT!3Jn$Gy)$yjCl44n;J!Gtrv)Qck@Qpfe@IbN4sT4 z)=-TO(#$Gxg(}F{Vf=Fk8?i2}FtSs63T?h%GTLlX|AZ^_gnO{f?S!XmmhNi7l3ZxT-GRCuIBZZmmw6GVb$Y*Rg^%b4IfI7JciSk1}>T8#*g&sgYvXUdX;kHD8 zGNRBpHZsXwnE#y?ya83Bd$PAtfd_K{MZWR7_ZCKP@CKWJ;ciJ`9GlR6bpZ9$17vXa8RNHQnI~j&v=QCb?2x zYxE9#x=TD>u&;+hMd@oT^bQ-gotFOYD@tuNw$qQZxuR-&P8b! zcnt+|x5EKv$i1`p++)RuSivKALD`Y?I;HFr5YjM7DH~>Owz8@0r&!)kpg~zJJiG~U zWgPw>zM}q*o)q#PjAz#WB-&}-C)7gayGtO<>~8VL3eI7@-$P4PuNLBx5b~#3b+>WW z{UZzjTB%+4S0^BA5B}KWJXWv?HUVe+K2BX4wn%O(5JqZoe;ZbKUqWy-E5_3vE9x^<87tx9LlOcHh0B z>KkO&_cVCT7xBlkUp``XU>I5vq||{9sbMAc3-lISF)blAFa@paWJ?@CiNi&rWuIEG znw{CH@YlBN%}67n>4mgI*~h5sGm*JIx6U|!2dQh#b6FXhdOwSDH<~~tf!-o#hfxNg z;h`GuFz+q?G7R+(yhDAsl^prpJESOJcv};c8Sgqw08vkTLX`QOdenZQiX&)ip_LpAn2|2Ujp_A;nVR(2XJ1oe&hAxr zu_7%L0vqou%S@f+Nc}9k8*6C|@PA`gp@oU@jQVDvK5D3iM}XAuiFJRPwYiPE;#CD6 zr*aLj3uo&Bu+G*NyoHb?A(2vP|EH*YWWBF!ou0p53%7$_)w7^>AOqCgt;R~~VddeM zps*gyg8Ic^^J20<9=w+ef;`wLvPO?4>DWxuBT3<{S~x-j)6!hfdJk@Bh4m$_pyoLk z79%sy0WMh!U#qC8T6n3tLaOngy2{YPTsL@wyFFU?dR(zVd$v&_B*u-g61VX-nu|KZ zZ(xW}k*`4QvpzYett49Jvodn%cVt=s$dQcN_w}<-#+)XcQbRi79u^!ch>U~h4}lp~ z4{D+7L4t>uU_{V+W_}LP&e8gVPzm>y7;+XE8LuSPHh zy;qV+>llxt#Tdu~%VD~6#KY+PTBwObHDOQdx(ze0S6jA0FH4?_Jz<_va)F4v@fj32$ssvkoWn+bPnlNymQ6Iw zfAB;4TN^ZzK}5n$=nNp)&b814l^lYAPOt`I=YFv}PtIte$3bP;+ljMdf67_nr-A!W z=6gE%YWA9%>~EFex54--UI4M}tqv^kQ=k`C%-En{Rp=&E^@oq+1v?>_(5gRwqH5AY z*PYFPio{h5E$qZgtbh?_+cN+noCu+tMhN57c#keYl^<@yBy(b>#~&M;*@IiMIcNk7 zG%+&=%K0OEi0dIZTzKRH74M`nq|q#csDwWTk0)sl zevTm)O)83BnXE_Fcw z^Weh4q%@QIN%IaPEJm-xK|R0whr($0!u(&gx!(il4W2;G{UxsY#0qne^Y=+2R3*U-5Z0{+C;@lxChBt9JwWLI&lT{rYk;7-_SlO8@8==Wnk1+#4&r)(vB1sNK*_qp);M zf;DdZ5G}zIu0 zd<^c&m@^~Ico@108zP*ntsjC<(StC+)F{GM-CpA_?16Yp`dY#;C#vx2P>-2GE4KMK z>Qr%21wpiL08pp2FAqpN7-!@9l=gK5@;BO7EPkMJrTvE`7>n*u6cYwkM9@0bID|fl z>l&`0C`7%{??b_n0GQWW_QyDQ5n`xcFHROQPjV!wF#otV_fepWqTP~nn+hZQy|H_m zg7xh@+QJR_d$mXF&@Bt=Q#WeiN#H^d3YX(M)+ZH?d+lkFBx^z`0n(1VjQ@J{rZgi; z-K-wE1`N@*h3@^{;QG|a2a&c2=+U#i?$5M^bw&9nJlf+MJgJAZ@GwNg;L&7hsrH}` zP^|DQHP2W<{@;fZRU!7um>G?L6(1pX=f*H9+zcJ5W>)IP_m+XxWNoH8+^RIXaImCjThzGJjPd#$j z@7^8lj3#Ig_5>>bQ*G{b_>TG0I7_v2AvULL;S6%Wk+~6ZBz#Z{eStlm$Y*Fus`21^ zpx^?UBSI|#U+_3&H68@I1LpaTm~09;=)VRgc5`yDE(KH8l^r8Vp6cVRv`}|!eicUc zvJ*TZB!iy<77sA4gk*bSTFk;l$HmOO+}cCegUcH{jya(OCbt$S_koG--T?*8$pEbN zN338D9$GN`u%Dfmt_Kx{`Wz1{a2mLKGeA2p{RNGby!6kCY4J!hH5yMtthlZgeuxyH zUO+kXUI4NlL;lNpzK6B#dIl&FQ>=R0SoPp$j~03w^%T>Zmyn719ycQ|MJ=4X{y{Ay z36zxT@5u`6;m7F-Iil6Yh*BC@RfB7fwFS_fDz5z%{v@+l_(NQBUfa6~ z_%#4vq!8+28?paGY!keX`7Pj$goh!bTW~Gm&eCA?Qj@WS@L39GsKpK%L^96KC*?+` zeB>m5#KyWg9zNpFf7h=K#3b~7u~xj%A9T0Q&g zitP#qX~%n0t_a+t!;LeH4>!PH{Wzc&O?mnE5HHUuUgd_qvAf+MjBKh*9XdF+oCk0U z0goy~N-`D~6X5(4n@W0MH!{N>o!>}j*@+v|qR21sWegyfq&rPUiNhE9zHMLhF*!s; zo_W$28;n`-BTUpX-bca9?fDrqGt!T6DX6aM&m8CLsl%Z#JXDs2UGG9 z6d-BLpD}cpod5JQd$kZ~^z%*>Gp8Joeh$_lvLZ2jdOhn`@g@Vz(Y9!EdVW3fb%=<0 z-I2{q+z?Z>W$ik00MGLyA4u6Kmc)I6HRw2ObkF?@*$dBVg}HwBaMp)#0r_7+K36oU zboW1i=T4V5g5LfD!=OpUnnGsP3M$$2Tr5n$}y++(B#dYl1gq}rt z-1LA?ig;rOw6KNG`y+|L%i*0p;L6xoGxniw^9lgPWe=qL%KCL#Mc*i{XsW)+jWlJ0 zH`*@)5Rrd8bV6SQl(=`OOge(MOK?*($(LP^Rn-GY#o33ek0F>{g8UgiJ*y4;Tbq9c zA@s>w_KCIX|6E$oXmn*Tu@=c|f#V^xN^7Dtwo-Xn>LPI|La(rk3aD}q=^`~=5&sa! zA4FkqZ0LWyJv(AYY6eBcW+f-n6wuPx{VsIiS_b+`3ULcKa0BzWBF}(oHd#F8X}GR0 z+Cx@8h#;6eibNT?d4meI zL1RVV%&^dHEc8Z&wz=C=P`Hksl&XaaMO3`Zuc*>bw-k$E>%hd2hs-P`(=W5$QW!;; z=)0$b!5_WklesiqpHyEkIny1#^;jrF5WW|>O0Hl?-tufTCaq($k*BFUpmzWiYj&YE zmo`E}*Ue_>$Fwa!pfiCT`*=EDSJV}tt{+{wt| z^ufDoIB0=A)m@1l4||(~z1j}GhVXj-9Ga8IKCrvd1FGZhWR;rfa7y8J<4tO)c5+J4 zpvl9~pl{J26$%5_$|lVYf|-5M*2R%SzDR3mtcCbAgf)!*CVha_^4mW#OG`6vXHTMA z{Z!{>r0{BR{0U@go=0>I0JRopRR2VLurbVXuuY4%2ak>9;fqJdK8JSto9L7nCqY7p#o2UtR!Uxp=0CZZzIzsm>w-%dzeAg!#A@9Ol!ANv!fE0(-2%_Y8s61maVr@gizYOr@A_-o!xS6lu?npmT z$NOBaKzkpnj*~Wk1XlJ9t%wi8!UG)C+A`aNka_fq*!i{_@>ml^3 zF&!>JM^}8f)za68#4u38jJOmv64JuC2L0i4W7FGcR3-pn%2RVnxPwp|jh9GmEkKj; z3;+>I)~cl}9R;f4Z@ij=E35ElyD7TSn9RbP0ML@735@*@fktBpfzHR32K@=Fhy9zI z;s)5;VTx-2O7+(2tq^k$?kDqnj4$#p+(Z6#FqTHaANA^)!{LEAJg9~5;!uwj_z_2p zK`S8+vo$xwT?{7>&Z0LmeA@@VR zhX8N>H(KyVTolD7yRk{5Eeo9C8W@{(6_)xFGcz&NJo5tRjW19uB4;f)t9`hK8{|^v z##mvVEAl0m4#C5qaML&qMYuXP&`f(x!r#$?$7Jd;e`cmP@`cVd?+Na&^O=ZDZ+1Nv z6hmYQk*+5AJ>RqRyTeD|PLPUpg_!*_i?9fv_YG86sjh6OPZ|@b(H~ z*v4=dL1Fz56x%wtBL_!Y=@J>->vYGh7<<1mn(vQ#OR!MXBOl$SM-JSMgG?h4$Zzw* zVsd)YTm*sf^*GLd=;)fq-~v`c#Pf9*-v~t!_TPbm4Ah97feE~A-xb7;5Jex_2RRbH z0(}$f`{+o-9o*-Jb#M?=ZgvOGjd!}?r>pfsLu`HFm>W?^!&j8JidS#bS>NE*{NKm` zd1t)E43H&H7Xzj6WV>_-Gr;c96lVog3YN&ojAv@0jSt$WPUwv*cEayaxp^%BIPOdR zVV!%#g(MGr8wN-jpse#OinAoTJ1{^hiDOQotu=x%_vBN&NJU2dK?H20pO?vjmU|r6$ds>riSO&cm$egp$@HJ%ja? z0Qjj!kKq3GB3yFKktUBHF*t003+XJ+H2x$c^zkYR;3WSAj;WvF<75Xf+GAXZ`Y_0u z|E$L)ivH)>0CX(QB{jr!>AmBziOK=vp0h+qiIt;^kRdxhO(Bh-AYlYH&+Dyz+rcp61WnE>lUE?*y7p)^X+C7 z5c55c0fdrs*3lr2s8fR?ZYgp^-4}TyUSukYG`uG>Jq`@m@y0up=PQdN@7hzwaXCwi z^N83C)6PoL&gz?HJb|J(B*SIb{ft6yy+Ih8Ge0m>c&u-RQ6(IGvHv53(q}vd7T!?8 zeMe!r(70K#q)|!*vgIeIp)rxW(raa~Jl+=%q!l5Z4f5eQ9`3`5ZTcOyt1(WrVLxe`*fvSBse53*fhD5t8n;ceqR zz;N0(t$ws;(_e*_1KN6{@9I} zTXvWDV}0A814I3wQ4A>_7P)=XOE3=pvkWq@i->605#QFSUrr3F{r>2i;`7lf;G#?+ z&J*9LD#i4xM`N|p#K)KN3&*(q(Qut$_*lTV5Do2KK=k(oPn7#3WhLmV;j&Yq^O1mP zUp$K_CQp-2u>A-%!{^Yw2aP|WHnj%VLbqdBz`c&%ji{Dilp5$_&5Kikr)H15j_6Ph zK;6%2H0od<;lXSlIFq~H^UeX;EVK%YLV*UuG5k@loxjlB$8hWyMjKXy#?ASW(Ui9L z;2VwgBaY)h7q(!3Oa-=w!r}r}^@9qN7iRV{o`w#$-of0Z4?{G@_$V&^Jq#xpbmyOt zKoGiwL@eE~GvKwd(Nrze2%*SBviz~p8Av*E3UQkE%EH*aOf^yqkxz9S6zkSYTQ=mZ z3`CWALoz!X|9K5Yiqu=j*B&|pa(%Jn(+V+unc5tu?hsjt1Ai!@^G1u2NJ7 zSJ=*ir$5-=TwfxYm#r!Vp(v?JT*lEa}6g(YG zG;(m&u-eGx*OzvDlJoIA9B-Y9$Eca}<2E!*kMxo(IbB8(vT0cIY?zrx<1=WQbhe(* zDd*tVKVbmR9MQTfj3mP79fR9CB??^q?(M+GQmlxio&6AU*@Q!MB{URu6PD2l=@$<+; z6vg8maC45I5ok3{lxpmIg|348XYe+A{w>P=39~r+%YV`nRW=>ow7eI*DFh{~@#2T@ z4it{@@(M&+;{`{+X@FSXtzLX#=wxz$dm_s z$7JYej*&K1Lpf;IXeH>#g$)G43B}q zoGr2-9BJkgxTd2_K?(C=GGR1~Ay8}qLkV=DzM$v?N-dx#fQCOe%hxd8hd5@)-n5D} zLt=(N7PgDy8mUThCXFLQzS&4O=Au718u?4;jG3c2$Wau7e3y}OND;3Rmzu~WBz6UG zq+_q~CCbUzZh~ZMY-9i8B~^fL!=NDKQ@F{!3M`ykGH8Z*lBHm5<3M&enSAn?4C6@j zY#FN$;WF}(j=PNuzGBG&*Z^n1x9~c@zDiSLw^wVI7WzdLEBI4B`e*_Nfx3C{JM_;X z^c(%>kr#oM8)~?p^P}+R9RZDNLXG}h2c4DwkH&)~Kny|&&RwT*BKe(_f2Pz=fV@~9 z-HU$yFiMg6B8&M;j7-P;xE;e;QWd`7Es@8vzDks6c-$I4@c$n4e{`?y{|xe|=y#U> zAHCqvzl+lUGNr#3=91m)hd=gu1O88WBOUTCQ}T9Suk10$Dep(xG?sT2T$TGG;UD3M z=0W^P$n#QOJBK_T<3yP!EP1&Jd3@9ln=3rV35M6eU_K?XV6l`rL9LdpveP203jCnHyrjtxGix>2l%V?xnLE7(N zu4Rm?=vCLmmi>;T@ z?$oksTp*lDd#u=f8+C_||44r8c5R(lV1r@UCV5p5J5=7el~@TmyeYfJdMFXe6Rd@W z(a+D5JPYkbBC!Oos1Y;1V`1{?x{dYo~W zK(ezZehcM46x2x;Z7$3=I>+(dtf`_2gdPf9J@HwlfljVNQ3e|Y2DYxaN#<+ZDUb`+(|A+LGw;J zx%89tKWiT*6{+I3g!Q7CUSl zyv2BgU4*4breiqP$P<1iKj0_rSd}b&yxX`Bg9x@t#}pLUiYI?~QhYUN5`Y>Ym#0V4 zIDm81&1sBS1TxNXI5dTlX${@!=U6tX!&2n$Tp~9=c)SPW(a-btSXvrgJL8A$OR4Zs zG{Vo77@Q2RWZhvTLk;AAwLEHc*$PxEf#4f7oNew7V>2*(1Iu%_8PQ{RVfH>`#y|d* z{kq0iwm#COmAnyL^%G2(CPQe4G^7&^v=626c!s$6rI2{1yGUF1!E)TqLuEfSakh?i z@?NI~Ua%uNW`y@PR(}M(XugI&>Uo)T#vN;%zRh_zR`4t5&Fc7A4PbTj4MWtQz^YEI z1kOk3{$R9J#jyPGUl?=bQ3wqhsGRaF4*5TVf<}aBWZ-!hu{-Lfl^y`e%%&o7Ia6Y;`MRf)ggo9D~qQH*qfIrOB+ojFi6;U7p^8jJmW|aj`K%;5a&Q6m46s#Vry~4 ztrXFVoCr@@vJ-UTVjEbKDVBg>Q0oFEl(p+R4hT2T(%lRhdpQY@H z&*&uoK)Hlf{MH5$-ySIW52c<50OQYiR0#`c(&hjYPeVg{_2tPgYxu)JsR(QZc#o#`_YUrN#LKWLC1V6b7{7Jl}d^)L@_> z7^nKnc6x*V!507BGUy;cgyQJkmk`Qkx3)O#6+*M)sj4L$$ zbr^%Am3g&}GQQw?cQKwU3ipG%aU(L_Uxr4}KAeMkTXRqoTWQ<^jYy&E97Lx!T@SkZbHKzt2b|ewS3X}i_z2A zn#Y-QFce20TtXNPOEk-zx(1TrOZTz{YqmUr5XfoqAT5QNO^&QQ$5sW8!yjM66)WtF zB2)$M62$X=NkY$xX2QF zI?A$9eV$g0I_o6+F6WprjpI`FPOIwasM_8*dk-Ys-Ff>~#s$9Bnz9Z)6(8oBu?d2m z!~BJ3K(vRr3^|@qIbWWS7xu zEOaFoR;JquE}S=YE5|aC0hpC7$o8n*!^B=Z4<{i$Grv!1yb>pN{!=xQcNpAecPeRi!bV zNUNUV!D#7>DHYhv1h$V$3s0m=WAUZVNZ}?6J)#_5!h7}DN8mghE0~rIu~X1tc6{lJ zTFLp+6UujxAza5BQ;{!u$GH)4{-di=gyTcqwdur(?R{{cG~UI@eNc5B?8&eoQ|D2PgIaWYHBVjE8yR?Z)pCI^P?#oe&!=2G z<~vkeUfNIyQ?bKpDP3X$+0JMdw~ylPnNft!64&rUDRZ6G_NG4oxJiIU;{buh0E~2t zIpiTJnvyz{Cd|vdiiYlCyuijKLk{ci#kw&nR2XLcc>fXAd=w0{Z%=fwUI5p^MkQY; z87oN+9zKR4c$ftdzmf2GAjyum#tK-4?)aQAIA(5Rv7&@-=D>o{#MC{hHh)=%m0eBX zX;enwLIPiaV)UmDlsOQE%!{nnyGd^=zy{-&#{qDEnrVTVMd2)PJ`3E8VL)%)LY@sM zAn6s>V)E9#=v`@&tLYqnb}ME2+jU56#B9#!h{_UnLVIbd6i$c6rYGacNcdG0IXTF@ z*?kX(s2dXUxIZD^UoiwPDLGP0r$JpbtjI=`ZF`kD*-XquK-)P#uCMny>y0#&dZe@OfUP;fnK%`4oyE z0I5At>Z9h8?$7}4i*1`7MmLmaqt+ir!C_Wi;X5Ek%%Cls(Z*OTW~>)23PFJn>F8%) z^ah;0X>RxI+he5f8SWfnu_Qgqx>$B#n6{dlVh(y+s~NlZ~JZ zG;P(6$R<4vC35uUHPf9B9mfvjcs!1#hx@f``Xq%S|>T?t>&> z^bZ?}R(UV)@3X0MP>Mdf9O}x5G-spRi21vPnQcx22F>iDMuGJ6iwjVhmCW8i5p)x5 zIXYA4gCX8qPmAH~$B1HBH9r6B(My13>mJ&^-0i)UL zOYg8^zJJC$v?%hvy~X_LVA<|SM{c8zA*WWI%hQTD4|M??FdrQrStBQq!dnA(SgUgE zrz}KFimXRy4)o^}HW}gV51d|ozbjJ+24jK7TL8u_@O5Bt zfbLk-tK0=doyKDW-i6ri;Z$NQhUK%XITeQ;Wv)#3e5 zmQFKSnTm;lt6(swu{Tp`7On&<^X6SR6D&I%E{82HD#3F2bc8FU)c{kL0=Ne^&QByG!*y z9s7Iug!I3&I@#byb<5au!FUG+MnUIw7eh!RbXFD#^g%G7fOc>`>l6_}61M#s4IgC6 z&T17h=0@XOfQj{wrLWkVlTBzlEvpVq^^`7bG>j<18P?o7?kc6jvbf)i%4NnmN<$+>!2ygjdFpOAk0;_r6mF#PC}KJ)+sl!8 zI+SC0=eZ~5q8W5yb+~Gm5&KjTUm?*x`ibKg*5S_!;ANa={>GX|i-$8O3e41&p;j3X z#gU!x6UQCNo_aC}w84|ph#9S((D%Z=Yhau}F?@~;t%i2GjhYH?ucr5O3#0vh1rwh!EAkiO&uIA1KBVDSIqq-hX zr{c9BE<}jc{6oh>lHWn>EN7-q$E1OnXy7f>?2(FxkirBYFZZX0fAbLF#o}7(M;23z z0U(pD559T)UbR=INBmx(^2ecr3!_uo;f02IcSdHZ@gU_^{MxIS7rfz{BfjxO`=JyU;=7MtDG>yS$xx{jnT`c63P1$@+`#jYemO z?$<(by{s57Tza?R?{a_2t@Qqq(;xKS48?8}b6W2&ht7@0Ct>Kk1By3%=csO6O|*YI zrv-7p>$H>L$aK)-Zy)6S))RPT_?FXlf^fr4wHKu5t1VP3U3+!C?p~wU zZ*HaMe?Ik6eZ|r^&u2EUuB8=V3O#ZZ54^O}@$^f=hCulE>W@a<4Qdl#6M_M7M8`WL-1U0> zx5>JDzwCBk{HC09p{hjkr|2ymrSJ2Hb)0=?ExmVES#+Q}|DHc6ZT2k+Hz+!=o#GqR zZgwAq>lq!GYT;66U#)PLMF%EZxa8S9Ney`&qGu|UHak^&x(@HLIDlps@VF^>sz_>MSOco@I*gtembzr`>}swS4~6@_*U8; z;#*;PsY_pJd!3_l&d0Gid>hK@$&lgGUR~?M$=Lc$$;J7br*&C93CM%>NmcFPCUDWU zcx{rr#cNg=DM-DWot6JV)!Pm)kFBCKxvAh=9`|AGRg8qVZ@UlE>wGvBIJX}v@J2oD zd}Wn*TRgTU8FkPyIDOeidaUEkcrNM>xRvxbj`gyR4}0&@lY3#vcq=@qX=f6*3FhGA z;ViV#SC)#u{MjP}S=B$wVHQ{5DaN{!>|$$^(9$2foseW}fab0y`bbLqST+CF=0iVQJ zKzlG_M~ia2v8OXRlJv6eJm>2nA&vVTm3k~#d>CC0YzeU$yc&hQhd%d6et0AHUJ7H2 z9|I3xzvRKM1#mxgh|zuyFi4;tp3Bs2Ep`z)#^$LyHtG3aXtDk1($!D`M~{z~tsf$}8Z`?Hl1i)hTaG!doZjiYQT<;GsG zzVH`L?q@4`m6cW11I71<&k;n!;>ld>~s>k;c@BS z8~SCSC!jo@Gd+T6;$tY`6nivtZPcl@{nCS!9ztQ}UQ zTlM-QDeQ4y1ka~_9=VVILG`Q0KN97F^4z4-TJP4zF4e&kEVq_BDG@8pm&cq z>8s*1O5-OZNH?;DP(Kxs<9lI)fzmC^v>M4_`Wn=^9@nZvG-8x&EPJR6(E$@FH%O?DEUpJ3GcE&5H zU9bNk*;m#r7t@lqfcM!c4`kx~?hpKB1JT}1+5+~MKYJ74RDrw1;dTOdj>BmJw~xae z{V4zYGvaW%&{N}Z7ojJ`;S7OKB-{063VZ~xUUp9wbP!|zLO6TBcR3CRfpEYZDe0)^ zf2M^O(idXM9y~9NA5na-C^8C*^^S9K{Wbn=D2nK5dMxE;90uj@D@MEFkV&2U10*z{ ze&mf*;@2+5Z064edbP8V=$y1c=nbwr&FNEk)!7y%!}W(XiHg}pwo57rPJn;1_shtHUplo{4LC zaY*gZj^Z-B$n41d*HcF~d}I%N zb@X8PCKtOm`^u(X>5csCJ+!^JY`ZW2J#Bs!6?5+gAuCSsb4nQ>C*a)jS3$N-<^Ci&o_$4SC-2=Y+z1!WN0#yG~@2+O-&nIH_}<*Q;GF3p14fJ zc<~nO*k)jtj`7|bt7wYN#w#PH;vE44nu7243a;(!2$27$;MgD4>x{I0aH0HBmJ4%^ zOoVT03-|`%Rmgp@dx-A-BTgRc!6O%F3w{RPyTQ|u%xIMPcTuVukV}t8PGsH{TO^n= zV%kyDjr6^wNKg5v9XMCnagY=8DtpRT>(BK_j$ZG8T7h^4p48(kz!9e{n0C}^mBeQ>tPWMEhMj9QjQnia2Y}T$)8nC7 zW$#oiT~u3G&$q@VR5STv-Wry!MrnJ!;Z+B!Cb5F$@CcVVMlRu5E_0Y%B5b)#zg!~X zxlFHIBHeJA-Q^M{bD3AjWhyT-2= zxtHPId*E^bQBc(5j52yJDw+czlQYhyCLp4a2*}`+A!(YGrhYTC(y}r$%OWeY*~-)= zvr?lnquFK~EbCkGe%9Li94}193{q$}^ioy3GNvDTwU!46JgX{X?u2}4aubtm!)del&>RW(ab>VzX zz^a#2)U97lo86LwwBq~c;vnYQ7qFMS>UZ2`D~lpleP*q`;;Jj6DRgQAj|`8dk%6Cn zu>s;(k?$+kKO?rxO=my;wyBB#l`nSXbG{sd%^rGb71W<&hqR&fXpNGyx5|8f!P%==fVf|xCEUWmhm6_EiUTkt=8 z`TT>*F0Hu?zyJ5_7kD6rBS(bpUc6L2!>zaIDp56rGAFH0z@tSqmrQsxv?7gd)mC`vFgdBf9kM<{uPOq4e~k4x1{D~j?K z6jaVHs#NBd7R;+sDhjHriz>^MyrQzg@;OD7z*Z=EbBeqQa|=pKXBQOCH%OGqg2hTz z&1@2;R27!vZ%J99Lai&6MHQt5g+)q16&072mlY{_C3BSSl|^%v?v$A`Td7`FQZ~07 zBzben3zeGc!t%LutBR_Xxs~M$@@i^81r(=TGaD7@C1nMbOBJ?dw#W~MwxWVmRmRP} zyr{5x#2iso&LuErq)$N0%r|E?DKQCZRaF-(s1TK;r6`UmDH~NJn^;g%mNy@TF$GJS z>k5`|9rTr~%kpZfOG=dqMe~Z5h^k7?jwq|{pDZd$IGa&aShAp?w68lw)WW+)dGh2D z8EB@WqNHq|QdnME3L{F&%c>FzW)~_MvQNiMlzg(Bx1dVN8#7{T-sDNc^MK47lQyk6 zFY3)oNL^4-Qc7K;j3`@FP+Br)Vs#~&gY*JrK}}V)KD$US&hf~Ew4ibxdZJ*CUQ}LgG8I%-7A$3B}Juk^s<5l-cIOIS~>@jID6T+akG_iBSws?P{xie zQO1okY|#<%Y>eKcN?(pY$|QJjK|$G4y`;KmK@}XTR~1#3pqG{vB`AnNy;qN_UOIPE zS$Q=(w4kI+@0HLC4UJnqTaIuH*^GR&$}68s zqmgy1Vlg5H@~SaB@=D9g=7~QLSyf#r=OqzGvb%(i2&KV8ba&t60m&(z{>fO~aEW-* zF*6mIR0UOqB_-t6n(DbJQXUytR90RyuUOE|1-d|VilG}*ir6xuWpP23PMqj(5lV=3 zj7VM1Y*l}EUUhMiK6dix(faJA)kTol8a)i*v7%3&loc-(6cbm1Ib+52*5{NU2+(+C zQFTpa8MHN~-vpXj<`k7;j467|a17}Z%&z+6DvDg1y9&ou@JNp_IPx$87Zp`jVfYWp zz|WZ`jTn=emo|CQxZdQZG3bKw;iV-Q2(5DTYQfU0ckcv!Vo}i`y|}u%qH0iL;^M`N z6Bdw+IkOWmBP7D1Rf(kqW%Ft<9;*^7mR1*+mmy)A_&Jp&P$jXjrm_+ZCQ{GEqc;{5 zR98`m5OQ)R>)36@K! zUbLh_%qs|YF|~`yV|GooKCirbkj`5CBfY|Xo{?_GRFz&xL0(q6RA-+T=`|H(Yf3x? zvW~?D%HuNl{%_QXPcgBw`aH23BZ-8wtA)alCx$4^g3@sKZ9<%0K9?<|0!aTpZDEP6 ztkxHoR2S>L@VjSuVQu9Z^#3qKnZxLCNnc#B2wIRgFeQ=RXtyek{^~R*C|LPuKw)OU zFy&Q?Y}oEUH4(7trve_u#zburp+9EQl5iG3$WRXiw}= z#2hA?D_U3s??T(T<_b`ifc1wR33Z?WRyJrJC)O-XUL}>>Hnl%o+JSwSF6KUOIi^KL z1q-Nw7Gr}0ViFB1p&ZRFL{zk-x)O^mCe1m(EWoJObC>2-(Yl-`msGJBRYQ#oW6@e% zUV~9p4yVp7Enh5J;(b7A$%2w0FDA)2D}1z z72ta{qV6NW$AC`&p8`Gud=B^ma27!S3*jrk*MM&T-vYh^d=EGW_yO=E;3vS(fb)R& z0Ph1%13mzp0elFc^Ab8T_P*#O@e;svz!JdC0D4XO1mGt?DCp^3nz53P=m!I40G0x_ z0`>#wos*vdVW8^^7zZF)ymiMZUIA^960Wtnjy`k-O>6Y{t*jCcpkD_D1L%B>)TC3g z>6H103ocRcVuBJ5!0+?XV~|c;gySEiOOWOx-Hmh!(gvg(k=k$ywVTR# zl=w5!;uYY-+1~}ElaSi*aC0rCcv5^C=~<-oNbTq~kcTu9@9jN6>8_@x?l?(YdtX!2 zHlz)EFgB3V_pV%U1bxRTA8Esb&=aX`UsF>G&Lb~82Kh*Ho|wAT53$ z_Mr3V|5Oh~x)y0J(yd5~k*0irw|bBsK)Mm>1*8X%E@?#jNRJ}*LnlUlgmI2^3DOj# z4M?XUjrkPw1JV?vN0A==rl~0@1ohwHzcwRHLAn-c4$^H%i;*5cN*B)7`BIlnP}C(Z zwL?&#jh>!&M=izm!CAtGiqnvruGz6X-?NF zi{&AQ)0U&9;cvF4#Gtwukc`vCkw}SVjb)TGa+Nj1sTVt(kr_@`n$tEMJ|)`CfN7we zLbT~l*E&mvGjfeJ+o`Yes{%FH)19_VO$YiQU>#^Lr$Fbnt>5{f*)%PORiN?nDWIVY zzj{J!33+KG4{fjVvwmtdC8le`4dKFm*s}xYlI_7JbOwGE=`ot=pfdZ2en+?!baXZ= zbjWt*S{AWQaPs&q+Egj zrA9mj)VHnhq-hGtuK?{f(1t)~ufE{Eg-(`Z4yTUZ5nY_4DY+n{xn;?6oI)e-#l!uV z4W5O92cBg!EE7>Sdoo9Z-pwL25o4ss1<34vWmD68rc92pkrWf8M;@3^BlLjoqd^yk z_&E-q5B-E58z_cvu#RZy=QQExde!=i+Nz^5A}|&K%ONixb6J)t51ny?HHYNoP`^Qu zH31nyUcO}uq@gE$x(z<6hODEIH3zSk{ul43ix|ixS(zj&v!$$&&V#D;_11M8u31wx z=TUXM^SGMkYydQ>c|@P4qBa+`c}UYxgJ!7RROtTA9hmpz*xF!W8=7OU*@i48O|8=^9UnI$B^%IzyY-T>0M1t!$}eJ66TzbFyON^ z%XEwiJgx!ITH)H+)HF}V+6GH@E8~LuYZ5H8hLTVI?%{N5llvcQOcH*R7A57aw?MCT zG>@!7%*3qnABn(O>~O}UalnaTm8Dhs)S$=ESAgY^r(n&RE#=)nzFSY@b{)xEL*sT; zK(;e=sxxPnGk3gmTBb99lrt4GThefD#T>k_dmd`DTGnQ0N-3K76z$mVZEEU$u|DGY zLHeQ2GS#`(GS<1#G65;N$YME(Oa>~(85Nhe>L(gn?59asbB|&T{=#ez^TFTdBW`ab z#9HfHkHrxrkM?0ZA&=JXOet?Y%`fY$@bVfzjD}VIlbq*N>$Q+Vv4Yvd@H}`o0zL)* zlKQ5mZ!zY@*zk!_wqK633d_D$tsAb*Y_)!*h6SvZs6mf>&?+KLBb}CDu0DbN#~g|Q zp0ime9v1+J-*i^hrz`@L;P z5WD7_aZw*+VUb_kYD4K-x^voSXa02OwBgR&5f`CRCZWALv={v{_O%y_O^yS(rsP{1 zTa}fmSxz~N)14JloyBR+e6phfa(CjM@TY&6Tk2KI1xy^6ZZMq8>132P`tP$zwr05% zwb?C6u{Kh;G|OaBg&q_$f?UUGFMkyB z9%)5h^Sp~)MGK#d(Ja}h%JOoPv>nr|l5z}tbdraQ{4ou6$zFA|-z!C3l2@HUdo}7~ zP=6cs6)xi6in>SNzLA z77Nn%IAoWeY-*xul`dLmKSfQ?c_fcD4DW4d{NEXP=`btLWxTA9$(H0uZpFn^zg zN1^Rhv@;s_Y8!FyHcO7VbrkPwXq{T+N7JO`dGAdn2JK?6eJc1#f4cvB;7n5!LXBjg>S()1v9QWAk@r1Be?rp3?0>I+J@Bsw{`J7W9{AS-|9aqG5B%$a ze?9Q82mbZIzaIG41OL+l_2RWfr9!5~-wN{jOp%_uQ=|u%i!>=qq{k&*9%U%^(LFaV zqs+hmK-s)EO=2`Z+os%W&xkVL*CIPjw%b8eD^i@o&*V^-FY$ElK$n5XKTs>Daz33g z;4;nw=s74|I1=V7mClrL8TZ@oNddT}=Zo&O>5^plhLR{tv5HMH()kWu^azfwb|O(y z=;1ytqyB(2%+Rw=9vb1ejxVFVznX;p#gZORzt|q5-E*=$TQdA6%es{F|F>kT>}OxQ z`pftcUjR^)DY86Qri)~{My6Y2x=W_}WqMeqr)2uMOn;GSfOu_EX)Dv7GVL$ZESXM` z>0FsElIa?mZjtFOneLbAVVRzi>E|;2MWz96VR>7b_LOOVnP$m!icIIqbdgNg$aITL zcgb|WOb^TSluSRD=`S)3@W}RM+Eb?eWtt_^DKeca(?v2}BhxK1-6hlgGCeHQQ!@Qr zroYHEfDSfswUud4nc`Old}YaWicIIqbdgNg$aITLcgb|WOb^TSluSRD=`S)3;6qom zFVmhf?Jv_TnNE@ET$wJC=^B}Ck?Ag(?w9FdnVypA=Q908rUCthoo!{>Q>OF-bGou* zIz^^)Wx7bFYh=1brn_XiU#5p;dP=53YV*8plrNV0A8LuIFedz;MSbH>5}zj0jRRZK zU#w)zclmOC@udrr?H=4EFh;Zb%c9&~;&WHG#1~6^jKt?()e^r^;*%vlskSBlpu}fN zeCmXj__GpUXz(vg~(_VD3@^qB0ilY8rZlM~zteRX$|yI)e@zDat_1pI6x zr=VIQ;s^AGFj`k!BovOwPV26E3NV4lHQY#BrIGVoTbZ^-W30_5p++Q8?|K}ftrBQ@Dr4O8!`ghhu=l{Bj5|{5dI|cu%sS}Fnt$9uRRr&B%l$0 ztq1VO78$;Q@;^d=t$mvZDgPz}*gCX1O!=R%SF*2)`keAlq29i!%_s=6`1u{eknoR3 zy`y@8lHv%oY3M#Jz6&bsfn=DX-KL}9;DVnv8LwhGG%nb*Q{gBCaUn>1o-n~&aB0^= zti#ELHrjNO7Q%%{?RF;$E-plCFQYL>C>M0?S!~!GZGy-*FVQYW1R46zI6>4YAty#>{5ik05Q)tu2EQYGc};QluH_sAJPoAv$o^%EW;R6|pEtq~u>2wBlK*1G^UrZ>@5GIS%GSM$i7b~hGU?4$r zMX!Z4C*`g0A!@X+$XlqbUZjgunS)b6w!FHbNUJSsm{wMmc2AQH2;sz#UlJ+J%4V&nn8)*fI1zkG@MO-&`B7I`C?vUWR%{3W4mZY^Mqqi|xigq*EusyUA zm{e^L6mi`ix)X&Qjeg?px+CIew2-U4hL~~P>3koUY1*44;GVX>1Cy_9APsg!Dp0#v zYYV*V-gW^fRA_TCDqQzFzd+p*?G<>$^*|_nm8MRsBop?sw6z-DZ@C@}+X}Lc+MO7< zu7|?jKw+!4i$w2h69j@Cng;i{9t%%}zPq(ahz!@`Y<9i&R2vkY3ZpiUgZeKTYr^|$IeNrZ2Ojebf2?r0eal zsVGINE_##Rbwc!!>UxIwPC5&Li&0&FlD(%wZbd0cb&aR7^iFU&N-3&q1o^MQNnjZV&X)fifu94*454cpUy6z^AoZ(W1 z>I$RQKjau$qPmVy=Y1S@72I8?y6D||*QbnI3mXXcS(|Yf3V0DXG&i)3oh%Q=FBp~3 z2p&30Fn;f>lejwqX1|R0(azR=c!!p?qj07b3M$&>^%vPZGMe0fIw2aG|?4A9jRB zYmLw#MjJ<%j-eT7CP~YN??XFrAw{ED3+>E>RIM+p4b@qL9IOlL(f|IWT7Zqlf%Oh_ zMQ9f*4I}@NTus9;G}5xbPGgXVmZi)|Y>Gzzhs#nfbXKhN7M7*LS^zR@B{Z`fYP+2! z(C44Rs15q0WH@CM>jw0a<)yat3yA<5{T_k|DE65^QauheEQj0D?;QecZvTlZ(Ew4HkGu4$uMeS^9K_vygflABk&byd&w?X>6NlM}+>0_9eEN_Io&ZKV} zq+hfkRjl-mtL1pePfSGvirc19&0@mef+mX9PM&x(Bns65w$Av=R5UCE6-yPQUm(~m zCtMj!Iud`|(EKOmJcZF^Iq523U7d4OvmGYrFDo~D|K73(SZ#x*cv22&K^FWa{Q89~Ll zWT?eOG&>A(+v}=ph@F0lq3r?6mF}Y9u4tvSDh=m?O$(uUKZ6S{Z6LzgmC1!jjlQGk z%Ho2q(LXA7W%E)Pqy2$)T{$7Nvrf`H@Plgv7gDsZu^DrXe|F`f%cv|L)BCa|@O4r=Ym(wSU1sy#s~&t+UVu5}}xSzKt)Hqq*p$Aw0Xejwy3;KErgp3I)jg>zao zX;8?83$9vPH|KCcQC%Ms(_FT}rn=~#uDFUBhn3k)R+MlF_gGf?afIt~>uPu^G}l#X zr(Z>Bo2i`z!8Fw>+J0g$V+Nac4;9M8c*CLHML|))g-Gpl>iUIZ3#olcn92y6X=Ajd zwDGB8Op@kD?Nz&I%1qHxN!lV7ovQg$VKGa~QLGa&%3MpV)vyc8<}y3&`LyFSFs_h# z(NC0I%cWksN$i#3G-o#(9tu+Wd)-A+S7daXE0NV`w4Wltwjf{wVo&KhQ9oOZ|3zWIQ~MZE|PoxOLV;APcZjvjH|5Z~->zhyWXz9e{s0N`LA8 zB7pb!0es&O8e{dhPllg(3AYBw-SJK=XV$=OG>_tj3%z2sIq0W+x?=quabXRyb_WmJ zx)2$ADwI-`4*M{^!bU`r3p%VtMc6En9|v{AW{doF=*h4`kq?2EVPzu!HO5R>wa8Z^ zcETO)X^&x@OS}8nFb`U{y@a+t@@nsWIJAwR>#zh)YZKOKGY`(YHe19SK zItY)-=M$AkZ5#SJsvw+Lq7>UQX!|v?9C=pkG7&LRv#r}v#kfF31LN#UXj0SydmhTd zfO7Aa%Dr1E_im}&|5s{BJC8{|sv?})>!z(Ig% zHi(C)9pd&zTZ#URx>MZEYHO%)7Z+UG5R8bZySWgl-9kJ&xu9#)&|^{eiZ--Cq{E)j zDUh6`S&8TV&>K-m(Ol@osJ)@jqmZgyL1YhxeuYAgX2V<(^-w77u5z{Z)HC}+Y2PwU zt3UumJ;IoL?KV zqmFpeIv-Kc@mccrOHtRL-fo?C9^PVy6%pNyplcr)Nnc#VsKsCB4{*A*2;JSjeK2Wa z-HpN0!4@@{@`E6+gFTAwqy3-8pP&_(|6`&DxcXxpXgYc_I)#Ho(XPTu6+KYgFlu%P zjUL1Wm-Yv2ioS#kky=j-zUaXm$-347VI4h$3tcpS;u*??u8Nf!iXLWtof@40rSqfH zTrW|hV*-IlmyIq(_(Tt99-DR_v1D+;u4N&bqci#T)5RkuI!kI0OpRtokOjK-84Qok zVV)dqIp(_P5loh=O(v~JGG-cf+fX5TlrNh-&3f`jG3m<-HF)HQipP3 zx3&}$mO6|J_1aAGPAV4;Xn�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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/_cbson.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/bson/_cbson.cpython-312-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..fb45b75991a254d3b3dea3c1d2ccac3daa4d7d59 GIT binary patch literal 371640 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

{tyQTfF4f}(93E) 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≪O`pDBD~cH+Dk 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/_cbson.cpython-39-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/bson/_cbson.cpython-39-x86_64-linux-gnu.so new file mode 100755 index 0000000000000000000000000000000000000000..4559a275daf2baf77d1bab1a1704f6b2b6b043e3 GIT binary patch literal 334600 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%0DTfDNzBO4qa_78A(i*1_6{dcF9!0B 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 T{0i+)PvAeTDZdjCf}r~cGyt;W literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/bson/_cbsonmodule.c b/.venv/lib/python3.12/site-packages/bson/_cbsonmodule.c new file mode 100644 index 0000000..d7c028d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/_cbsonmodule.c @@ -0,0 +1,3276 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This file contains C implementations of some of the functions + * needed by the bson module. If possible, these implementations + * should be used to speed up BSON encoding and decoding. + */ + +#define PY_SSIZE_T_CLEAN +#include "Python.h" +#include "datetime.h" + +#include "buffer.h" +#include "time64.h" + +#define _CBSON_MODULE +#include "_cbsonmodule.h" + +/* New module state and initialization code. + * See the module-initialization-and-state + * section in the following doc: + * http://docs.python.org/release/3.1.3/howto/cporting.html + * which references the following pep: + * http://www.python.org/dev/peps/pep-3121/ + * */ +struct module_state { + PyObject* Binary; + PyObject* Code; + PyObject* ObjectId; + PyObject* DBRef; + PyObject* Regex; + PyObject* UUID; + PyObject* Timestamp; + PyObject* MinKey; + PyObject* MaxKey; + PyObject* UTC; + PyTypeObject* REType; + PyObject* BSONInt64; + PyObject* Decimal128; + PyObject* Mapping; + PyObject* DatetimeMS; + PyObject* min_datetime; + PyObject* max_datetime; + PyObject* replace_args; + PyObject* replace_kwargs; + PyObject* _type_marker_str; + PyObject* _flags_str; + PyObject* _pattern_str; + PyObject* _encoder_map_str; + PyObject* _decoder_map_str; + PyObject* _fallback_encoder_str; + PyObject* _raw_str; + PyObject* _subtype_str; + PyObject* _binary_str; + PyObject* _scope_str; + PyObject* _inc_str; + PyObject* _time_str; + PyObject* _bid_str; + PyObject* _replace_str; + PyObject* _astimezone_str; + PyObject* _id_str; + PyObject* _dollar_ref_str; + PyObject* _dollar_id_str; + PyObject* _dollar_db_str; + PyObject* _tzinfo_str; + PyObject* _as_doc_str; + PyObject* _utcoffset_str; + PyObject* _from_uuid_str; + PyObject* _as_uuid_str; + PyObject* _from_bid_str; + int64_t min_millis; + int64_t max_millis; +}; + +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) + +/* Maximum number of regex flags */ +#define FLAGS_SIZE 7 + +/* Default UUID representation type code. */ +#define PYTHON_LEGACY 3 + +/* Other UUID representations. */ +#define STANDARD 4 +#define JAVA_LEGACY 5 +#define CSHARP_LEGACY 6 +#define UNSPECIFIED 0 + +#define BSON_MAX_SIZE 2147483647 +/* The smallest possible BSON document, i.e. "{}" */ +#define BSON_MIN_SIZE 5 + +/* Datetime codec options */ +#define DATETIME 1 +#define DATETIME_CLAMP 2 +#define DATETIME_MS 3 +#define DATETIME_AUTO 4 + +/* Converts integer to its string representation in decimal notation. */ +extern int cbson_long_long_to_str(long long num, char* str, size_t size) { + // Buffer should fit 64-bit signed integer + if (size < 21) { + PyErr_Format( + PyExc_RuntimeError, + "Buffer too small to hold long long: %d < 21", size); + return -1; + } + int index = 0; + int sign = 1; + // Convert to unsigned to handle -LLONG_MIN overflow + unsigned long long absNum; + // Handle the case of 0 + if (num == 0) { + str[index++] = '0'; + str[index] = '\0'; + return 0; + } + // Handle negative numbers + if (num < 0) { + sign = -1; + absNum = 0ULL - (unsigned long long)num; + } else { + absNum = (unsigned long long)num; + } + // Convert the number to string + unsigned long long digit; + while (absNum > 0) { + digit = absNum % 10ULL; + str[index++] = (char)digit + '0'; // Convert digit to character + absNum /= 10; + } + // Add minus sign if negative + if (sign == -1) { + str[index++] = '-'; + } + str[index] = '\0'; // Null terminator + // Reverse the string + int start = 0; + int end = index - 1; + while (start < end) { + char temp = str[start]; + str[start++] = str[end]; + str[end--] = temp; + } + return 0; +} + +static PyObject* _test_long_long_to_str(PyObject* self, PyObject* args) { + // Test extreme values + Py_ssize_t maxNum = PY_SSIZE_T_MAX; + Py_ssize_t minNum = PY_SSIZE_T_MIN; + Py_ssize_t num; + char str_1[BUF_SIZE]; + char str_2[BUF_SIZE]; + int res = LL2STR(str_1, (long long)minNum); + if (res == -1) { + return NULL; + } + INT2STRING(str_2, (long long)minNum); + if (strcmp(str_1, str_2) != 0) { + PyErr_Format( + PyExc_RuntimeError, + "LL2STR != INT2STRING: %s != %s", str_1, str_2); + return NULL; + } + LL2STR(str_1, (long long)maxNum); + INT2STRING(str_2, (long long)maxNum); + if (strcmp(str_1, str_2) != 0) { + PyErr_Format( + PyExc_RuntimeError, + "LL2STR != INT2STRING: %s != %s", str_1, str_2); + return NULL; + } + + // Test common values + for (num = 0; num < 10000; num++) { + char str_1[BUF_SIZE]; + char str_2[BUF_SIZE]; + LL2STR(str_1, (long long)num); + INT2STRING(str_2, (long long)num); + if (strcmp(str_1, str_2) != 0) { + PyErr_Format( + PyExc_RuntimeError, + "LL2STR != INT2STRING: %s != %s", str_1, str_2); + return NULL; + } + } + + return args; +} + +/* Get an error class from the bson.errors module. + * + * Returns a new ref */ +static PyObject* _error(char* name) { + PyObject* error = NULL; + PyObject* errors = PyImport_ImportModule("bson.errors"); + if (!errors) { + return NULL; + } + error = PyObject_GetAttrString(errors, name); + Py_DECREF(errors); + return error; +} + +/* Safely downcast from Py_ssize_t to int, setting an + * exception and returning -1 on error. */ +static int +_downcast_and_check(Py_ssize_t size, uint8_t extra) { + if (size > BSON_MAX_SIZE || ((BSON_MAX_SIZE - extra) < size)) { + PyObject* InvalidStringData = _error("InvalidStringData"); + if (InvalidStringData) { + PyErr_SetString(InvalidStringData, + "String length must be <= 2147483647"); + Py_DECREF(InvalidStringData); + } + return -1; + } + return (int)size + extra; +} + +static PyObject* elements_to_dict(PyObject* self, const char* string, + unsigned max, + const codec_options_t* options); + +static int _write_element_to_buffer(PyObject* self, buffer_t buffer, + int type_byte, PyObject* value, + unsigned char check_keys, + const codec_options_t* options, + unsigned char in_custom_call, + unsigned char in_fallback_call); + +/* Write a RawBSONDocument to the buffer. + * Returns the number of bytes written or 0 on failure. + */ +static int write_raw_doc(buffer_t buffer, PyObject* raw, PyObject* _raw); + +/* Date stuff */ +static PyObject* datetime_from_millis(long long millis) { + /* To encode a datetime instance like datetime(9999, 12, 31, 23, 59, 59, 999999) + * we follow these steps: + * 1. Calculate a timestamp in seconds: 253402300799 + * 2. Multiply that by 1000: 253402300799000 + * 3. Add in microseconds divided by 1000 253402300799999 + * + * (Note: BSON doesn't support microsecond accuracy, hence the truncation.) + * + * To decode we could do: + * 1. Get seconds: timestamp / 1000: 253402300799 + * 2. Get micros: (timestamp % 1000) * 1000: 999000 + * Resulting in datetime(9999, 12, 31, 23, 59, 59, 999000) -- the expected result + * + * Now what if the we encode (1, 1, 1, 1, 1, 1, 111111)? + * 1. and 2. gives: -62135593139000 + * 3. Gives us: -62135593138889 + * + * Now decode: + * 1. Gives us: -62135593138 + * 2. Gives us: -889000 + * Resulting in datetime(1, 1, 1, 1, 1, 2, 15888216) -- an invalid result + * + * If instead to decode we do: + * diff = ((millis % 1000) + 1000) % 1000: 111 + * seconds = (millis - diff) / 1000: -62135593139 + * micros = diff * 1000 111000 + * Resulting in datetime(1, 1, 1, 1, 1, 1, 111000) -- the expected result + */ + PyObject* datetime = NULL; + int diff = (int)(((millis % 1000) + 1000) % 1000); + int microseconds = diff * 1000; + Time64_T seconds = (millis - diff) / 1000; + struct TM timeinfo; + cbson_gmtime64_r(&seconds, &timeinfo); + + datetime = PyDateTime_FromDateAndTime(timeinfo.tm_year + 1900, + timeinfo.tm_mon + 1, + timeinfo.tm_mday, + timeinfo.tm_hour, + timeinfo.tm_min, + timeinfo.tm_sec, + microseconds); + if(!datetime) { + PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; + + /* + * Calling _error clears the error state, so fetch it first. + */ + PyErr_Fetch(&etype, &evalue, &etrace); + + /* Only add addition error message on ValueError exceptions. */ + if (PyErr_GivenExceptionMatches(etype, PyExc_ValueError)) { + if (evalue) { + PyObject* err_msg = PyObject_Str(evalue); + if (err_msg) { + PyObject* appendage = PyUnicode_FromString(" (Consider Using CodecOptions(datetime_conversion=DATETIME_AUTO) or MongoClient(datetime_conversion='DATETIME_AUTO')). See: https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/dates-and-times/#handling-out-of-range-datetimes"); + if (appendage) { + PyObject* msg = PyUnicode_Concat(err_msg, appendage); + if (msg) { + Py_DECREF(evalue); + evalue = msg; + } + } + Py_XDECREF(appendage); + } + Py_XDECREF(err_msg); + } + PyErr_NormalizeException(&etype, &evalue, &etrace); + } + /* Steals references to args. */ + PyErr_Restore(etype, evalue, etrace); + } + return datetime; +} + +static long long millis_from_datetime(PyObject* datetime) { + struct TM timeinfo; + long long millis; + + timeinfo.tm_year = PyDateTime_GET_YEAR(datetime) - 1900; + timeinfo.tm_mon = PyDateTime_GET_MONTH(datetime) - 1; + timeinfo.tm_mday = PyDateTime_GET_DAY(datetime); + timeinfo.tm_hour = PyDateTime_DATE_GET_HOUR(datetime); + timeinfo.tm_min = PyDateTime_DATE_GET_MINUTE(datetime); + timeinfo.tm_sec = PyDateTime_DATE_GET_SECOND(datetime); + + millis = cbson_timegm64(&timeinfo) * 1000; + millis += PyDateTime_DATE_GET_MICROSECOND(datetime) / 1000; + return millis; +} + +/* Extended-range datetime, returns a DatetimeMS object with millis */ +static PyObject* datetime_ms_from_millis(PyObject* self, long long millis){ + // Allocate a new DatetimeMS object. + struct module_state *state = GETSTATE(self); + if (!state) { + return NULL; + } + + PyObject* dt = NULL; + PyObject* ll_millis = NULL; + + if (!(ll_millis = PyLong_FromLongLong(millis))){ + return NULL; + } + dt = PyObject_CallFunctionObjArgs(state->DatetimeMS, ll_millis, NULL); + Py_DECREF(ll_millis); + return dt; +} + +/* Extended-range datetime, takes a DatetimeMS object and extracts the long long value. */ +static int millis_from_datetime_ms(PyObject* dt, long long* out){ + PyObject* ll_millis; + long long millis; + + if (!(ll_millis = PyNumber_Long(dt))){ + return 0; + } + millis = PyLong_AsLongLong(ll_millis); + Py_DECREF(ll_millis); + if (millis == -1 && PyErr_Occurred()) { /* Overflow */ + PyErr_SetString(PyExc_OverflowError, + "MongoDB datetimes can only handle up to 8-byte ints"); + return 0; + } + *out = millis; + return 1; +} + +static PyObject* decode_datetime(PyObject* self, long long millis, const codec_options_t* options){ + PyObject* naive = NULL; + PyObject* replace = NULL; + PyObject* value = NULL; + struct module_state *state = GETSTATE(self); + if (!state) { + goto invalid; + } + if (options->datetime_conversion == DATETIME_MS){ + return datetime_ms_from_millis(self, millis); + } + + int dt_clamp = options->datetime_conversion == DATETIME_CLAMP; + int dt_auto = options->datetime_conversion == DATETIME_AUTO; + + if (dt_clamp || dt_auto){ + int64_t min_millis = state->min_millis; + int64_t max_millis = state->max_millis; + int64_t min_millis_offset = 0; + int64_t max_millis_offset = 0; + if (options->tz_aware && options->tzinfo && options->tzinfo != Py_None) { + PyObject* utcoffset = PyObject_CallMethodObjArgs(options->tzinfo, state->_utcoffset_str, state->min_datetime, NULL); + if (utcoffset == NULL) { + return 0; + } + if (utcoffset != Py_None) { + if (!PyDelta_Check(utcoffset)) { + PyObject* BSONError = _error("BSONError"); + if (BSONError) { + PyErr_SetString(BSONError, "tzinfo.utcoffset() did not return a datetime.timedelta"); + Py_DECREF(BSONError); + } + Py_DECREF(utcoffset); + return 0; + } + min_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * (int64_t)86400 + + PyDateTime_DELTA_GET_SECONDS(utcoffset)) * (int64_t)1000 + + (PyDateTime_DELTA_GET_MICROSECONDS(utcoffset) / 1000); + } + Py_DECREF(utcoffset); + utcoffset = PyObject_CallMethodObjArgs(options->tzinfo, state->_utcoffset_str, state->max_datetime, NULL); + if (utcoffset == NULL) { + return 0; + } + if (utcoffset != Py_None) { + if (!PyDelta_Check(utcoffset)) { + PyObject* BSONError = _error("BSONError"); + if (BSONError) { + PyErr_SetString(BSONError, "tzinfo.utcoffset() did not return a datetime.timedelta"); + Py_DECREF(BSONError); + } + Py_DECREF(utcoffset); + return 0; + } + max_millis_offset = (PyDateTime_DELTA_GET_DAYS(utcoffset) * (int64_t)86400 + + PyDateTime_DELTA_GET_SECONDS(utcoffset)) * (int64_t)1000 + + (PyDateTime_DELTA_GET_MICROSECONDS(utcoffset) / 1000); + } + Py_DECREF(utcoffset); + } + if (min_millis_offset < 0) { + min_millis -= min_millis_offset; + } + + if (max_millis_offset > 0) { + max_millis -= max_millis_offset; + } + + if (dt_clamp) { + if (millis < min_millis) { + millis = min_millis; + } else if (millis > max_millis) { + millis = max_millis; + } + // Continues from here to return a datetime. + } else { // dt_auto + if (millis < min_millis || millis > max_millis){ + return datetime_ms_from_millis(self, millis); + } + } + } + + naive = datetime_from_millis(millis); + if (!naive) { + goto invalid; + } + + if (!options->tz_aware) { /* In the naive case, we're done here. */ + return naive; + } + replace = PyObject_GetAttr(naive, state->_replace_str); + if (!replace) { + goto invalid; + } + value = PyObject_Call(replace, state->replace_args, state->replace_kwargs); + if (!value) { + goto invalid; + } + + /* convert to local time */ + if (options->tzinfo != Py_None) { + PyObject* temp = PyObject_CallMethodObjArgs(value, state->_astimezone_str, options->tzinfo, NULL); + Py_DECREF(value); + value = temp; + } +invalid: + Py_XDECREF(naive); + Py_XDECREF(replace); + return value; +} + +/* Just make this compatible w/ the old API. */ +int buffer_write_bytes(buffer_t buffer, const char* data, int size) { + if (pymongo_buffer_write(buffer, data, size)) { + return 0; + } + return 1; +} + +int buffer_write_double(buffer_t buffer, double data) { + double data_le = BSON_DOUBLE_TO_LE(data); + return buffer_write_bytes(buffer, (const char*)&data_le, 8); +} + +int buffer_write_int32(buffer_t buffer, int32_t data) { + uint32_t data_le = BSON_UINT32_TO_LE(data); + return buffer_write_bytes(buffer, (const char*)&data_le, 4); +} + +int buffer_write_int64(buffer_t buffer, int64_t data) { + uint64_t data_le = BSON_UINT64_TO_LE(data); + return buffer_write_bytes(buffer, (const char*)&data_le, 8); +} + +void buffer_write_int32_at_position(buffer_t buffer, + int position, + int32_t data) { + uint32_t data_le = BSON_UINT32_TO_LE(data); + memcpy(pymongo_buffer_get_buffer(buffer) + position, &data_le, 4); +} + +static int write_unicode(buffer_t buffer, PyObject* py_string) { + int size; + const char* data; + PyObject* encoded = PyUnicode_AsUTF8String(py_string); + if (!encoded) { + return 0; + } + data = PyBytes_AS_STRING(encoded); + if (!data) + goto unicodefail; + + if ((size = _downcast_and_check(PyBytes_GET_SIZE(encoded), 1)) == -1) + goto unicodefail; + + if (!buffer_write_int32(buffer, (int32_t)size)) + goto unicodefail; + + if (!buffer_write_bytes(buffer, data, size)) + goto unicodefail; + + Py_DECREF(encoded); + return 1; + +unicodefail: + Py_DECREF(encoded); + return 0; +} + +/* returns 0 on failure */ +static int write_string(buffer_t buffer, PyObject* py_string) { + int size; + const char* data; + if (PyUnicode_Check(py_string)){ + return write_unicode(buffer, py_string); + } + data = PyBytes_AsString(py_string); + if (!data) { + return 0; + } + + if ((size = _downcast_and_check(PyBytes_Size(py_string), 1)) == -1) + return 0; + + if (!buffer_write_int32(buffer, (int32_t)size)) { + return 0; + } + if (!buffer_write_bytes(buffer, data, size)) { + return 0; + } + return 1; +} + +/* Load a Python object to cache. + * + * Returns non-zero on failure. */ +static int _load_object(PyObject** object, char* module_name, char* object_name) { + PyObject* module; + + module = PyImport_ImportModule(module_name); + if (!module) { + return 1; + } + + *object = PyObject_GetAttrString(module, object_name); + Py_DECREF(module); + + return (*object) ? 0 : 2; +} + +/* Load all Python objects to cache. + * + * Returns non-zero on failure. */ +static int _load_python_objects(PyObject* module) { + PyObject* empty_string = NULL; + PyObject* re_compile = NULL; + PyObject* compiled = NULL; + PyObject* min_datetime_ms = NULL; + PyObject* max_datetime_ms = NULL; + struct module_state *state = GETSTATE(module); + if (!state) { + return 1; + } + + /* Cache commonly used attribute names to improve performance. */ + if (!((state->_type_marker_str = PyUnicode_FromString("_type_marker")) && + (state->_flags_str = PyUnicode_FromString("flags")) && + (state->_pattern_str = PyUnicode_FromString("pattern")) && + (state->_encoder_map_str = PyUnicode_FromString("_encoder_map")) && + (state->_decoder_map_str = PyUnicode_FromString("_decoder_map")) && + (state->_fallback_encoder_str = PyUnicode_FromString("_fallback_encoder")) && + (state->_raw_str = PyUnicode_FromString("raw")) && + (state->_subtype_str = PyUnicode_FromString("subtype")) && + (state->_binary_str = PyUnicode_FromString("binary")) && + (state->_scope_str = PyUnicode_FromString("scope")) && + (state->_inc_str = PyUnicode_FromString("inc")) && + (state->_time_str = PyUnicode_FromString("time")) && + (state->_bid_str = PyUnicode_FromString("bid")) && + (state->_replace_str = PyUnicode_FromString("replace")) && + (state->_astimezone_str = PyUnicode_FromString("astimezone")) && + (state->_id_str = PyUnicode_FromString("_id")) && + (state->_dollar_ref_str = PyUnicode_FromString("$ref")) && + (state->_dollar_id_str = PyUnicode_FromString("$id")) && + (state->_dollar_db_str = PyUnicode_FromString("$db")) && + (state->_tzinfo_str = PyUnicode_FromString("tzinfo")) && + (state->_as_doc_str = PyUnicode_FromString("as_doc")) && + (state->_utcoffset_str = PyUnicode_FromString("utcoffset")) && + (state->_from_uuid_str = PyUnicode_FromString("from_uuid")) && + (state->_as_uuid_str = PyUnicode_FromString("as_uuid")) && + (state->_from_bid_str = PyUnicode_FromString("from_bid")))) { + return 1; + } + + if (_load_object(&state->Binary, "bson.binary", "Binary") || + _load_object(&state->Code, "bson.code", "Code") || + _load_object(&state->ObjectId, "bson.objectid", "ObjectId") || + _load_object(&state->DBRef, "bson.dbref", "DBRef") || + _load_object(&state->Timestamp, "bson.timestamp", "Timestamp") || + _load_object(&state->MinKey, "bson.min_key", "MinKey") || + _load_object(&state->MaxKey, "bson.max_key", "MaxKey") || + _load_object(&state->UTC, "bson.tz_util", "utc") || + _load_object(&state->Regex, "bson.regex", "Regex") || + _load_object(&state->BSONInt64, "bson.int64", "Int64") || + _load_object(&state->Decimal128, "bson.decimal128", "Decimal128") || + _load_object(&state->UUID, "uuid", "UUID") || + _load_object(&state->Mapping, "collections.abc", "Mapping") || + _load_object(&state->DatetimeMS, "bson.datetime_ms", "DatetimeMS") || + _load_object(&min_datetime_ms, "bson.datetime_ms", "_MIN_UTC_MS") || + _load_object(&max_datetime_ms, "bson.datetime_ms", "_MAX_UTC_MS") || + _load_object(&state->min_datetime, "bson.datetime_ms", "_MIN_UTC") || + _load_object(&state->max_datetime, "bson.datetime_ms", "_MAX_UTC")) { + return 1; + } + + state->min_millis = PyLong_AsLongLong(min_datetime_ms); + state->max_millis = PyLong_AsLongLong(max_datetime_ms); + Py_DECREF(min_datetime_ms); + Py_DECREF(max_datetime_ms); + if ((state->min_millis == -1 || state->max_millis == -1) && PyErr_Occurred()) { + return 1; + } + + /* Speed up datetime.replace(tzinfo=utc) call */ + state->replace_args = PyTuple_New(0); + if (!state->replace_args) { + return 1; + } + state->replace_kwargs = PyDict_New(); + if (!state->replace_kwargs) { + return 1; + } + if (PyDict_SetItem(state->replace_kwargs, state->_tzinfo_str, state->UTC) == -1) { + return 1; + } + + /* Reload our REType hack too. */ + empty_string = PyBytes_FromString(""); + if (empty_string == NULL) { + state->REType = NULL; + return 1; + } + + if (_load_object(&re_compile, "re", "compile")) { + state->REType = NULL; + Py_DECREF(empty_string); + return 1; + } + + compiled = PyObject_CallFunction(re_compile, "O", empty_string); + Py_DECREF(re_compile); + if (compiled == NULL) { + state->REType = NULL; + Py_DECREF(empty_string); + return 1; + } + Py_INCREF(Py_TYPE(compiled)); + state->REType = Py_TYPE(compiled); + Py_DECREF(empty_string); + Py_DECREF(compiled); + return 0; +} + +/* + * Get the _type_marker from an Object. + * + * Return the type marker, 0 if there is no marker, or -1 on failure. + */ +static long _type_marker(PyObject* object, PyObject* _type_marker_str) { + PyObject* type_marker = NULL; + long type = 0; + + if (PyObject_HasAttr(object, _type_marker_str)) { + type_marker = PyObject_GetAttr(object, _type_marker_str); + if (type_marker == NULL) { + return -1; + } + } + + /* + * Python objects with broken __getattr__ implementations could return + * arbitrary types for a call to PyObject_GetAttrString. For example + * pymongo.database.Database returns a new Collection instance for + * __getattr__ calls with names that don't match an existing attribute + * or method. In some cases "value" could be a subtype of something + * we know how to serialize. Make a best effort to encode these types. + */ + if (type_marker && PyLong_CheckExact(type_marker)) { + type = PyLong_AsLong(type_marker); + Py_DECREF(type_marker); + } else { + Py_XDECREF(type_marker); + } + + return type; +} + +/* Fill out a type_registry_t* from a TypeRegistry object. + * + * Return 1 on success. options->document_class is a new reference. + * Return 0 on failure. + */ +int cbson_convert_type_registry(PyObject* registry_obj, type_registry_t* registry, PyObject* _encoder_map_str, PyObject* _decoder_map_str, PyObject* _fallback_encoder_str) { + registry->encoder_map = NULL; + registry->decoder_map = NULL; + registry->fallback_encoder = NULL; + registry->registry_obj = NULL; + + registry->encoder_map = PyObject_GetAttr(registry_obj, _encoder_map_str); + if (registry->encoder_map == NULL) { + goto fail; + } + registry->is_encoder_empty = (PyDict_Size(registry->encoder_map) == 0); + + registry->decoder_map = PyObject_GetAttr(registry_obj, _decoder_map_str); + if (registry->decoder_map == NULL) { + goto fail; + } + registry->is_decoder_empty = (PyDict_Size(registry->decoder_map) == 0); + + registry->fallback_encoder = PyObject_GetAttr(registry_obj, _fallback_encoder_str); + if (registry->fallback_encoder == NULL) { + goto fail; + } + registry->has_fallback_encoder = (registry->fallback_encoder != Py_None); + + registry->registry_obj = registry_obj; + Py_INCREF(registry->registry_obj); + return 1; + +fail: + Py_XDECREF(registry->encoder_map); + Py_XDECREF(registry->decoder_map); + Py_XDECREF(registry->fallback_encoder); + return 0; +} + +/* Fill out a codec_options_t* from a CodecOptions object. + * + * Return 1 on success. options->document_class is a new reference. + * Return 0 on failure. + */ +int convert_codec_options(PyObject* self, PyObject* options_obj, codec_options_t* options) { + PyObject* type_registry_obj = NULL; + struct module_state *state = GETSTATE(self); + long type_marker; + if (!state) { + return 0; + } + + options->unicode_decode_error_handler = NULL; + + if (!PyArg_ParseTuple(options_obj, "ObbzOOb", + &options->document_class, + &options->tz_aware, + &options->uuid_rep, + &options->unicode_decode_error_handler, + &options->tzinfo, + &type_registry_obj, + &options->datetime_conversion)) { + return 0; + } + + type_marker = _type_marker(options->document_class, + state->_type_marker_str); + if (type_marker < 0) { + return 0; + } + + if (!cbson_convert_type_registry(type_registry_obj, + &options->type_registry, state->_encoder_map_str, state->_decoder_map_str, state->_fallback_encoder_str)) { + return 0; + } + + options->is_raw_bson = (101 == type_marker); + options->options_obj = options_obj; + + Py_INCREF(options->options_obj); + Py_INCREF(options->document_class); + Py_INCREF(options->tzinfo); + + return 1; +} + +void destroy_codec_options(codec_options_t* options) { + Py_CLEAR(options->document_class); + Py_CLEAR(options->tzinfo); + Py_CLEAR(options->options_obj); + Py_CLEAR(options->type_registry.registry_obj); + Py_CLEAR(options->type_registry.encoder_map); + Py_CLEAR(options->type_registry.decoder_map); + Py_CLEAR(options->type_registry.fallback_encoder); +} + +static int write_element_to_buffer(PyObject* self, buffer_t buffer, + int type_byte, PyObject* value, + unsigned char check_keys, + const codec_options_t* options, + unsigned char in_custom_call, + unsigned char in_fallback_call) { + int result = 0; + if(Py_EnterRecursiveCall(" while encoding an object to BSON ")) { + return 0; + } + result = _write_element_to_buffer(self, buffer, type_byte, + value, check_keys, options, + in_custom_call, in_fallback_call); + Py_LeaveRecursiveCall(); + return result; +} + +static void +_set_cannot_encode(PyObject* value) { + if (PyLong_Check(value)) { + if ((PyLong_AsLongLong(value) == -1) && PyErr_Occurred()) { + return PyErr_SetString(PyExc_OverflowError, + "MongoDB can only handle up to 8-byte ints"); + } + } + + PyObject* type = NULL; + PyObject* InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument == NULL) { + goto error; + } + + type = PyObject_Type(value); + if (type == NULL) { + goto error; + } + PyErr_Format(InvalidDocument, "cannot encode object: %R, of type: %R", + value, type); +error: + Py_XDECREF(type); + Py_XDECREF(InvalidDocument); +} + +/* + * Encode a builtin Python regular expression or our custom Regex class. + * + * Sets exception and returns 0 on failure. + */ +static int _write_regex_to_buffer( + buffer_t buffer, int type_byte, PyObject* value, PyObject* _flags_str, PyObject* _pattern_str) { + + PyObject* py_flags; + PyObject* py_pattern; + PyObject* encoded_pattern; + PyObject* decoded_pattern; + long int_flags; + char flags[FLAGS_SIZE]; + char check_utf8 = 0; + const char* pattern_data; + int pattern_length, flags_length; + + /* + * Both the builtin re type and our Regex class have attributes + * "flags" and "pattern". + */ + py_flags = PyObject_GetAttr(value, _flags_str); + if (!py_flags) { + return 0; + } + int_flags = PyLong_AsLong(py_flags); + Py_DECREF(py_flags); + if (int_flags == -1 && PyErr_Occurred()) { + return 0; + } + py_pattern = PyObject_GetAttr(value, _pattern_str); + if (!py_pattern) { + return 0; + } + + if (PyUnicode_Check(py_pattern)) { + encoded_pattern = PyUnicode_AsUTF8String(py_pattern); + Py_DECREF(py_pattern); + if (!encoded_pattern) { + return 0; + } + } else { + encoded_pattern = py_pattern; + check_utf8 = 1; + } + + if (!(pattern_data = PyBytes_AsString(encoded_pattern))) { + Py_DECREF(encoded_pattern); + return 0; + } + if ((pattern_length = _downcast_and_check(PyBytes_Size(encoded_pattern), 0)) == -1) { + Py_DECREF(encoded_pattern); + return 0; + } + + if (strlen(pattern_data) != (size_t) pattern_length){ + PyObject* InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument) { + PyErr_SetString(InvalidDocument, + "regex patterns must not contain the NULL byte"); + Py_DECREF(InvalidDocument); + } + Py_DECREF(encoded_pattern); + return 0; + } + + if (check_utf8) { + decoded_pattern = PyUnicode_DecodeUTF8(pattern_data, (Py_ssize_t) pattern_length, NULL); + if (decoded_pattern == NULL) { + PyErr_Clear(); + PyObject* InvalidStringData = _error("InvalidStringData"); + if (InvalidStringData) { + PyErr_SetString(InvalidStringData, + "regex patterns must be valid UTF-8"); + Py_DECREF(InvalidStringData); + } + Py_DECREF(encoded_pattern); + return 0; + } + Py_DECREF(decoded_pattern); + } + + if (!buffer_write_bytes(buffer, pattern_data, pattern_length + 1)) { + Py_DECREF(encoded_pattern); + return 0; + } + Py_DECREF(encoded_pattern); + + flags[0] = 0; + + if (int_flags & 2) { + STRCAT(flags, FLAGS_SIZE, "i"); + } + if (int_flags & 4) { + STRCAT(flags, FLAGS_SIZE, "l"); + } + if (int_flags & 8) { + STRCAT(flags, FLAGS_SIZE, "m"); + } + if (int_flags & 16) { + STRCAT(flags, FLAGS_SIZE, "s"); + } + if (int_flags & 32) { + STRCAT(flags, FLAGS_SIZE, "u"); + } + if (int_flags & 64) { + STRCAT(flags, FLAGS_SIZE, "x"); + } + flags_length = (int)strlen(flags) + 1; + if (!buffer_write_bytes(buffer, flags, flags_length)) { + return 0; + } + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0B; + return 1; +} + +/* Write a single value to the buffer (also write its type_byte, for which + * space has already been reserved. + * + * returns 0 on failure */ +static int _write_element_to_buffer(PyObject* self, buffer_t buffer, + int type_byte, PyObject* value, + unsigned char check_keys, + const codec_options_t* options, + unsigned char in_custom_call, + unsigned char in_fallback_call) { + PyObject* new_value = NULL; + int retval; + int is_list; + long type; + struct module_state *state = GETSTATE(self); + if (!state) { + return 0; + } + /* + * Use _type_marker attribute instead of PyObject_IsInstance for better perf. + */ + type = _type_marker(value, state->_type_marker_str); + if (type < 0) { + return 0; + } + + switch (type) { + case 5: + { + /* Binary */ + PyObject* subtype_object; + char subtype; + const char* data; + int size; + + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x05; + subtype_object = PyObject_GetAttr(value, state->_subtype_str); + if (!subtype_object) { + return 0; + } + subtype = (char)PyLong_AsLong(subtype_object); + if (subtype == -1) { + Py_DECREF(subtype_object); + return 0; + } + size = _downcast_and_check(PyBytes_Size(value), 0); + if (size == -1) { + Py_DECREF(subtype_object); + return 0; + } + + Py_DECREF(subtype_object); + if (subtype == 2) { + int other_size = _downcast_and_check(PyBytes_Size(value), 4); + if (other_size == -1) + return 0; + if (!buffer_write_int32(buffer, other_size)) { + return 0; + } + if (!buffer_write_bytes(buffer, &subtype, 1)) { + return 0; + } + } + if (!buffer_write_int32(buffer, size)) { + return 0; + } + if (subtype != 2) { + if (!buffer_write_bytes(buffer, &subtype, 1)) { + return 0; + } + } + data = PyBytes_AsString(value); + if (!data) { + return 0; + } + if (!buffer_write_bytes(buffer, data, size)) { + return 0; + } + return 1; + } + case 7: + { + /* ObjectId */ + const char* data; + PyObject* pystring = PyObject_GetAttr(value, state->_binary_str); + if (!pystring) { + return 0; + } + data = PyBytes_AsString(pystring); + if (!data) { + Py_DECREF(pystring); + return 0; + } + if (!buffer_write_bytes(buffer, data, 12)) { + Py_DECREF(pystring); + return 0; + } + Py_DECREF(pystring); + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x07; + return 1; + } + case 9: + { + /* DatetimeMS */ + long long millis; + if (!millis_from_datetime_ms(value, &millis)) { + return 0; + } + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x09; + return buffer_write_int64(buffer, (int64_t)millis); + } + case 11: + { + /* Regex */ + return _write_regex_to_buffer(buffer, type_byte, value, state->_flags_str, state->_pattern_str); + } + case 13: + { + /* Code */ + int start_position, + length_location, + length; + + PyObject* scope = PyObject_GetAttr(value, state->_scope_str); + if (!scope) { + return 0; + } + + if (scope == Py_None) { + Py_DECREF(scope); + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0D; + return write_string(buffer, value); + } + + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0F; + + start_position = pymongo_buffer_get_position(buffer); + /* save space for length */ + length_location = pymongo_buffer_save_space(buffer, 4); + if (length_location == -1) { + Py_DECREF(scope); + return 0; + } + + if (!write_string(buffer, value)) { + Py_DECREF(scope); + return 0; + } + + if (!write_dict(self, buffer, scope, 0, options, 0)) { + Py_DECREF(scope); + return 0; + } + Py_DECREF(scope); + + length = pymongo_buffer_get_position(buffer) - start_position; + buffer_write_int32_at_position( + buffer, length_location, (int32_t)length); + return 1; + } + case 17: + { + /* Timestamp */ + PyObject* obj; + unsigned long i; + + obj = PyObject_GetAttr(value, state->_inc_str); + if (!obj) { + return 0; + } + i = PyLong_AsUnsignedLong(obj); + Py_DECREF(obj); + if (i == (unsigned long)-1 && PyErr_Occurred()) { + return 0; + } + if (!buffer_write_int32(buffer, (int32_t)i)) { + return 0; + } + + obj = PyObject_GetAttr(value, state->_time_str); + if (!obj) { + return 0; + } + i = PyLong_AsUnsignedLong(obj); + Py_DECREF(obj); + if (i == (unsigned long)-1 && PyErr_Occurred()) { + return 0; + } + if (!buffer_write_int32(buffer, (int32_t)i)) { + return 0; + } + + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x11; + return 1; + } + case 18: + { + /* Int64 */ + const long long ll = PyLong_AsLongLong(value); + if (PyErr_Occurred()) { /* Overflow */ + PyErr_SetString(PyExc_OverflowError, + "MongoDB can only handle up to 8-byte ints"); + return 0; + } + if (!buffer_write_int64(buffer, (int64_t)ll)) { + return 0; + } + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x12; + return 1; + } + case 19: + { + /* Decimal128 */ + const char* data; + PyObject* pystring = PyObject_GetAttr(value, state->_bid_str); + if (!pystring) { + return 0; + } + data = PyBytes_AsString(pystring); + if (!data) { + Py_DECREF(pystring); + return 0; + } + if (!buffer_write_bytes(buffer, data, 16)) { + Py_DECREF(pystring); + return 0; + } + Py_DECREF(pystring); + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x13; + return 1; + } + case 100: + { + /* DBRef */ + PyObject* as_doc = PyObject_CallMethodObjArgs(value, state->_as_doc_str, NULL); + if (!as_doc) { + return 0; + } + if (!write_dict(self, buffer, as_doc, 0, options, 0)) { + Py_DECREF(as_doc); + return 0; + } + Py_DECREF(as_doc); + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; + return 1; + } + case 101: + { + /* RawBSONDocument */ + if (!write_raw_doc(buffer, value, state->_raw_str)) { + return 0; + } + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; + return 1; + } + case 255: + { + /* MinKey */ + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0xFF; + return 1; + } + case 127: + { + /* MaxKey */ + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x7F; + return 1; + } + } + + /* No _type_marker attribute or not one of our types. */ + + if (PyBool_Check(value)) { + const char c = (value == Py_True) ? 0x01 : 0x00; + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x08; + return buffer_write_bytes(buffer, &c, 1); + } + else if (PyLong_Check(value)) { + const long long long_long_value = PyLong_AsLongLong(value); + if (long_long_value == -1 && PyErr_Occurred()) { + /* Ignore error and give the fallback_encoder a chance. */ + PyErr_Clear(); + } else if (-2147483648LL <= long_long_value && long_long_value <= 2147483647LL) { + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x10; + return buffer_write_int32(buffer, (int32_t)long_long_value); + } else { + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x12; + return buffer_write_int64(buffer, (int64_t)long_long_value); + } + } else if (PyFloat_Check(value)) { + const double d = PyFloat_AsDouble(value); + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x01; + return buffer_write_double(buffer, d); + } else if (value == Py_None) { + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x0A; + return 1; + } else if (PyDict_Check(value)) { + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; + return write_dict(self, buffer, value, check_keys, options, 0); + } else if ((is_list = PyList_Check(value)) || PyTuple_Check(value)) { + Py_ssize_t items, i; + int start_position, + length_location, + length; + char zero = 0; + + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x04; + start_position = pymongo_buffer_get_position(buffer); + + /* save space for length */ + length_location = pymongo_buffer_save_space(buffer, 4); + if (length_location == -1) { + return 0; + } + if (is_list) { + items = PyList_Size(value); + } else { + items = PyTuple_Size(value); + } + if (items > BSON_MAX_SIZE) { + PyObject* BSONError = _error("BSONError"); + if (BSONError) { + PyErr_SetString(BSONError, + "Too many items to serialize."); + Py_DECREF(BSONError); + } + return 0; + } + for(i = 0; i < items; i++) { + int list_type_byte = pymongo_buffer_save_space(buffer, 1); + char name[BUF_SIZE]; + PyObject* item_value; + + if (list_type_byte == -1) { + return 0; + } + int res = LL2STR(name, (long long)i); + if (res == -1) { + return 0; + } + if (!buffer_write_bytes(buffer, name, (int)strlen(name) + 1)) { + return 0; + } + if (is_list) { + item_value = PyList_GET_ITEM(value, i); + } else { + item_value = PyTuple_GET_ITEM(value, i); + } + if (!item_value) { + return 0; + } + if (!write_element_to_buffer(self, buffer, list_type_byte, + item_value, check_keys, options, + 0, 0)) { + return 0; + } + } + + /* write null byte and fill in length */ + if (!buffer_write_bytes(buffer, &zero, 1)) { + return 0; + } + length = pymongo_buffer_get_position(buffer) - start_position; + buffer_write_int32_at_position( + buffer, length_location, (int32_t)length); + return 1; + /* Python3 special case. Store bytes as BSON binary subtype 0. */ + } else if (PyBytes_Check(value)) { + char subtype = 0; + int size; + const char* data = PyBytes_AS_STRING(value); + if (!data) + return 0; + if ((size = _downcast_and_check(PyBytes_GET_SIZE(value), 0)) == -1) + return 0; + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x05; + if (!buffer_write_int32(buffer, (int32_t)size)) { + return 0; + } + if (!buffer_write_bytes(buffer, &subtype, 1)) { + return 0; + } + if (!buffer_write_bytes(buffer, data, size)) { + return 0; + } + return 1; + } else if (PyUnicode_Check(value)) { + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x02; + return write_unicode(buffer, value); + } else if (PyDateTime_Check(value)) { + long long millis; + PyObject* utcoffset = PyObject_CallMethodObjArgs(value, state->_utcoffset_str , NULL); + if (utcoffset == NULL) + return 0; + if (utcoffset != Py_None) { + PyObject* result = PyNumber_Subtract(value, utcoffset); + if (!result) { + Py_DECREF(utcoffset); + return 0; + } + millis = millis_from_datetime(result); + Py_DECREF(result); + } else { + millis = millis_from_datetime(value); + } + Py_DECREF(utcoffset); + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x09; + return buffer_write_int64(buffer, (int64_t)millis); + } else if (PyObject_TypeCheck(value, state->REType)) { + return _write_regex_to_buffer(buffer, type_byte, value, state->_flags_str, state->_pattern_str); + } else if (PyObject_IsInstance(value, state->Mapping)) { + /* PyObject_IsInstance returns -1 on error */ + if (PyErr_Occurred()) { + return 0; + } + *(pymongo_buffer_get_buffer(buffer) + type_byte) = 0x03; + return write_dict(self, buffer, value, check_keys, options, 0); + } else if (PyObject_IsInstance(value, state->UUID)) { + PyObject* binary_value = NULL; + PyObject *uuid_rep_obj = NULL; + int result; + + /* PyObject_IsInstance returns -1 on error */ + if (PyErr_Occurred()) { + return 0; + } + + if (!(uuid_rep_obj = PyLong_FromLong(options->uuid_rep))) { + return 0; + } + binary_value = PyObject_CallMethodObjArgs(state->Binary, state->_from_uuid_str, value, uuid_rep_obj, NULL); + Py_DECREF(uuid_rep_obj); + + if (binary_value == NULL) { + return 0; + } + + result = _write_element_to_buffer(self, buffer, + type_byte, binary_value, + check_keys, options, + in_custom_call, + in_fallback_call); + Py_DECREF(binary_value); + return result; + } + + /* Try a custom encoder if one is provided and we have not already + * attempted to use a type encoder. */ + if (!in_custom_call && !options->type_registry.is_encoder_empty) { + PyObject* value_type = NULL; + PyObject* converter = NULL; + value_type = PyObject_Type(value); + if (value_type == NULL) { + return 0; + } + converter = PyDict_GetItem(options->type_registry.encoder_map, value_type); + Py_XDECREF(value_type); + if (converter != NULL) { + /* Transform types that have a registered converter. + * A new reference is created upon transformation. */ + new_value = PyObject_CallFunctionObjArgs(converter, value, NULL); + if (new_value == NULL) { + return 0; + } + retval = write_element_to_buffer(self, buffer, type_byte, new_value, + check_keys, options, 1, 0); + Py_XDECREF(new_value); + return retval; + } + } + + /* Try the fallback encoder if one is provided and we have not already + * attempted to use the fallback encoder. */ + if (!in_fallback_call && options->type_registry.has_fallback_encoder) { + new_value = PyObject_CallFunctionObjArgs( + options->type_registry.fallback_encoder, value, NULL); + if (new_value == NULL) { + // propagate any exception raised by the callback + return 0; + } + retval = write_element_to_buffer(self, buffer, type_byte, new_value, + check_keys, options, 0, 1); + Py_XDECREF(new_value); + return retval; + } + + /* We can't determine value's type. Fail. */ + _set_cannot_encode(value); + return 0; +} + +static int check_key_name(const char* name, int name_length) { + + if (name_length > 0 && name[0] == '$') { + PyObject* InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument) { + PyObject* errmsg = PyUnicode_FromFormat( + "key '%s' must not start with '$'", name); + if (errmsg) { + PyErr_SetObject(InvalidDocument, errmsg); + Py_DECREF(errmsg); + } + Py_DECREF(InvalidDocument); + } + return 0; + } + if (strchr(name, '.')) { + PyObject* InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument) { + PyObject* errmsg = PyUnicode_FromFormat( + "key '%s' must not contain '.'", name); + if (errmsg) { + PyErr_SetObject(InvalidDocument, errmsg); + Py_DECREF(errmsg); + } + Py_DECREF(InvalidDocument); + } + return 0; + } + return 1; +} + +/* Write a (key, value) pair to the buffer. + * + * Returns 0 on failure */ +int write_pair(PyObject* self, buffer_t buffer, const char* name, int name_length, + PyObject* value, unsigned char check_keys, + const codec_options_t* options, unsigned char allow_id) { + int type_byte; + + /* Don't write any _id elements unless we're explicitly told to - + * _id has to be written first so we do so, but don't bother + * deleting it from the dictionary being written. */ + if (!allow_id && strcmp(name, "_id") == 0) { + return 1; + } + + type_byte = pymongo_buffer_save_space(buffer, 1); + if (type_byte == -1) { + return 0; + } + if (check_keys && !check_key_name(name, name_length)) { + return 0; + } + if (!buffer_write_bytes(buffer, name, name_length + 1)) { + return 0; + } + if (!write_element_to_buffer(self, buffer, type_byte, + value, check_keys, options, 0, 0)) { + return 0; + } + return 1; +} + +int decode_and_write_pair(PyObject* self, buffer_t buffer, + PyObject* key, PyObject* value, + unsigned char check_keys, + const codec_options_t* options, + unsigned char top_level) { + PyObject* encoded; + const char* data; + int size; + if (PyUnicode_Check(key)) { + encoded = PyUnicode_AsUTF8String(key); + if (!encoded) { + return 0; + } + if (!(data = PyBytes_AS_STRING(encoded))) { + Py_DECREF(encoded); + return 0; + } + if ((size = _downcast_and_check(PyBytes_GET_SIZE(encoded), 1)) == -1) { + Py_DECREF(encoded); + return 0; + } + if (strlen(data) != (size_t)(size - 1)) { + PyObject* InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument) { + PyErr_SetString(InvalidDocument, + "Key names must not contain the NULL byte"); + Py_DECREF(InvalidDocument); + } + Py_DECREF(encoded); + return 0; + } + } else { + PyObject* InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument) { + PyObject* repr = PyObject_Repr(key); + if (repr) { + PyObject* errmsg = PyUnicode_FromString( + "documents must have only string keys, key was "); + if (errmsg) { + PyObject* error = PyUnicode_Concat(errmsg, repr); + if (error) { + PyErr_SetObject(InvalidDocument, error); + Py_DECREF(error); + } + Py_DECREF(errmsg); + Py_DECREF(repr); + } else { + Py_DECREF(repr); + } + } + Py_DECREF(InvalidDocument); + } + return 0; + } + + /* If top_level is True, don't allow writing _id here - it was already written. */ + if (!write_pair(self, buffer, data, + size - 1, value, check_keys, options, !top_level)) { + Py_DECREF(encoded); + return 0; + } + + Py_DECREF(encoded); + return 1; +} + + +/* Write a RawBSONDocument to the buffer. + * Returns the number of bytes written or 0 on failure. + */ +static int write_raw_doc(buffer_t buffer, PyObject* raw, PyObject* _raw_str) { + char* bytes; + Py_ssize_t len; + int len_int; + int bytes_written = 0; + PyObject* bytes_obj = NULL; + + bytes_obj = PyObject_GetAttr(raw, _raw_str); + if (!bytes_obj) { + goto fail; + } + + if (-1 == PyBytes_AsStringAndSize(bytes_obj, &bytes, &len)) { + goto fail; + } + len_int = _downcast_and_check(len, 0); + if (-1 == len_int) { + goto fail; + } + if (!buffer_write_bytes(buffer, bytes, len_int)) { + goto fail; + } + bytes_written = len_int; +fail: + Py_XDECREF(bytes_obj); + return bytes_written; +} + + +/* Update Invalid Document error message to include doc. + */ +void handle_invalid_doc_error(PyObject* dict) { + PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; + PyObject *msg = NULL, *dict_str = NULL, *new_msg = NULL; + PyErr_Fetch(&etype, &evalue, &etrace); + PyObject *InvalidDocument = _error("InvalidDocument"); + if (InvalidDocument == NULL) { + goto cleanup; + } + + if (evalue && PyErr_GivenExceptionMatches(etype, InvalidDocument)) { + msg = PyObject_Str(evalue); + if (msg) { + // Prepend doc to the existing message + dict_str = PyObject_Str(dict); + if (dict_str == NULL) { + goto cleanup; + } + const char * dict_str_utf8 = PyUnicode_AsUTF8(dict_str); + if (dict_str_utf8 == NULL) { + goto cleanup; + } + const char * msg_utf8 = PyUnicode_AsUTF8(msg); + if (msg_utf8 == NULL) { + goto cleanup; + } + new_msg = PyUnicode_FromFormat("Invalid document %s | %s", dict_str_utf8, msg_utf8); + Py_DECREF(evalue); + Py_DECREF(etype); + etype = InvalidDocument; + InvalidDocument = NULL; + if (new_msg) { + evalue = new_msg; + new_msg = NULL; + } else { + evalue = msg; + msg = NULL; + } + } + PyErr_NormalizeException(&etype, &evalue, &etrace); + } +cleanup: + PyErr_Restore(etype, evalue, etrace); + Py_XDECREF(msg); + Py_XDECREF(InvalidDocument); + Py_XDECREF(dict_str); + Py_XDECREF(new_msg); +} + + +/* returns the number of bytes written or 0 on failure */ +int write_dict(PyObject* self, buffer_t buffer, + PyObject* dict, unsigned char check_keys, + const codec_options_t* options, unsigned char top_level) { + PyObject* key; + PyObject* iter; + char zero = 0; + int length; + int length_location; + struct module_state *state = GETSTATE(self); + long type_marker; + int is_dict = PyDict_Check(dict); + if (!state) { + return 0; + } + + if (!is_dict) { + /* check for RawBSONDocument */ + type_marker = _type_marker(dict, state->_type_marker_str); + if (type_marker < 0) { + return 0; + } + + if (101 == type_marker) { + return write_raw_doc(buffer, dict, state->_raw_str); + } + + if (!PyObject_IsInstance(dict, state->Mapping)) { + PyObject* repr; + if ((repr = PyObject_Repr(dict))) { + PyObject* errmsg = PyUnicode_FromString( + "encoder expected a mapping type but got: "); + if (errmsg) { + PyObject* error = PyUnicode_Concat(errmsg, repr); + if (error) { + PyErr_SetObject(PyExc_TypeError, error); + Py_DECREF(error); + } + Py_DECREF(errmsg); + Py_DECREF(repr); + } + else { + Py_DECREF(repr); + } + } else { + PyErr_SetString(PyExc_TypeError, + "encoder expected a mapping type"); + } + + return 0; + } + /* PyObject_IsInstance returns -1 on error */ + if (PyErr_Occurred()) { + return 0; + } + } + + length_location = pymongo_buffer_save_space(buffer, 4); + if (length_location == -1) { + return 0; + } + + /* Write _id first if this is a top level doc. */ + if (top_level) { + /* + * If "dict" is a defaultdict we don't want to call + * PyObject_GetItem on it. That would **create** + * an _id where one didn't previously exist (PYTHON-871). + */ + if (is_dict) { + /* PyDict_GetItem returns a borrowed reference. */ + PyObject* _id = PyDict_GetItem(dict, state->_id_str); + if (_id) { + if (!write_pair(self, buffer, "_id", 3, + _id, check_keys, options, 1)) { + return 0; + } + } + } else if (PyMapping_HasKey(dict, state->_id_str)) { + PyObject* _id = PyObject_GetItem(dict, state->_id_str); + if (!_id) { + return 0; + } + if (!write_pair(self, buffer, "_id", 3, + _id, check_keys, options, 1)) { + Py_DECREF(_id); + return 0; + } + /* PyObject_GetItem returns a new reference. */ + Py_DECREF(_id); + } + } + + if (is_dict) { + PyObject* value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + if (!decode_and_write_pair(self, buffer, key, value, + check_keys, options, top_level)) { + if (PyErr_Occurred() && top_level) { + handle_invalid_doc_error(dict); + } + return 0; + } + } + } else { + iter = PyObject_GetIter(dict); + if (iter == NULL) { + return 0; + } + while ((key = PyIter_Next(iter)) != NULL) { + PyObject* value = PyObject_GetItem(dict, key); + if (!value) { + PyErr_SetObject(PyExc_KeyError, key); + Py_DECREF(key); + Py_DECREF(iter); + return 0; + } + if (!decode_and_write_pair(self, buffer, key, value, + check_keys, options, top_level)) { + if (PyErr_Occurred() && top_level) { + handle_invalid_doc_error(dict); + } + Py_DECREF(key); + Py_DECREF(value); + Py_DECREF(iter); + return 0; + } + Py_DECREF(key); + Py_DECREF(value); + } + Py_DECREF(iter); + if (PyErr_Occurred()) { + return 0; + } + } + + /* write null byte and fill in length */ + if (!buffer_write_bytes(buffer, &zero, 1)) { + return 0; + } + length = pymongo_buffer_get_position(buffer) - length_location; + buffer_write_int32_at_position( + buffer, length_location, (int32_t)length); + return length; +} + +static PyObject* _cbson_dict_to_bson(PyObject* self, PyObject* args) { + PyObject* dict; + PyObject* result; + unsigned char check_keys; + unsigned char top_level = 1; + PyObject* options_obj = NULL; + codec_options_t options; + buffer_t buffer; + PyObject* raw_bson_document_bytes_obj; + long type_marker; + struct module_state *state = GETSTATE(self); + if (!state) { + return NULL; + } + + if (!(PyArg_ParseTuple(args, "ObO|b", &dict, &check_keys, + &options_obj, &top_level) && + convert_codec_options(self, options_obj, &options))) { + return NULL; + } + + /* check for RawBSONDocument */ + type_marker = _type_marker(dict, state->_type_marker_str); + if (type_marker < 0) { + destroy_codec_options(&options); + return NULL; + } else if (101 == type_marker) { + destroy_codec_options(&options); + raw_bson_document_bytes_obj = PyObject_GetAttr(dict, state->_raw_str); + if (NULL == raw_bson_document_bytes_obj) { + return NULL; + } + return raw_bson_document_bytes_obj; + } + + buffer = pymongo_buffer_new(); + if (!buffer) { + destroy_codec_options(&options); + return NULL; + } + + if (!write_dict(self, buffer, dict, check_keys, &options, top_level)) { + destroy_codec_options(&options); + pymongo_buffer_free(buffer); + return NULL; + } + + /* objectify buffer */ + result = Py_BuildValue("y#", pymongo_buffer_get_buffer(buffer), + (Py_ssize_t)pymongo_buffer_get_position(buffer)); + destroy_codec_options(&options); + pymongo_buffer_free(buffer); + return result; +} + +/* + * Hook for optional decoding BSON documents to DBRef. + */ +static PyObject *_dbref_hook(PyObject* self, PyObject* value) { + struct module_state *state = GETSTATE(self); + PyObject* ref = NULL; + PyObject* id = NULL; + PyObject* database = NULL; + PyObject* ret = NULL; + int db_present = 0; + if (!state) { + return NULL; + } + + /* Decoding for DBRefs */ + if (PyMapping_HasKey(value, state->_dollar_ref_str) && PyMapping_HasKey(value, state->_dollar_id_str)) { /* DBRef */ + ref = PyObject_GetItem(value, state->_dollar_ref_str); + /* PyObject_GetItem returns NULL to indicate error. */ + if (!ref) { + goto invalid; + } + id = PyObject_GetItem(value, state->_dollar_id_str); + /* PyObject_GetItem returns NULL to indicate error. */ + if (!id) { + goto invalid; + } + + if (PyMapping_HasKey(value, state->_dollar_db_str)) { + database = PyObject_GetItem(value, state->_dollar_db_str); + if (!database) { + goto invalid; + } + db_present = 1; + } else { + database = Py_None; + Py_INCREF(database); + } + + // check types + if (!(PyUnicode_Check(ref) && (database == Py_None || PyUnicode_Check(database)))) { + ret = value; + goto invalid; + } + + PyMapping_DelItem(value, state->_dollar_ref_str); + PyMapping_DelItem(value, state->_dollar_id_str); + if (db_present) { + PyMapping_DelItem(value, state->_dollar_db_str); + } + + ret = PyObject_CallFunctionObjArgs(state->DBRef, ref, id, database, value, NULL); + Py_DECREF(value); + } else { + ret = value; + } +invalid: + Py_XDECREF(ref); + Py_XDECREF(id); + Py_XDECREF(database); + return ret; +} + +static PyObject* get_value(PyObject* self, PyObject* name, const char* buffer, + unsigned* position, unsigned char type, + unsigned max, const codec_options_t* options, int raw_array) { + struct module_state *state = GETSTATE(self); + PyObject* value = NULL; + if (!state) { + return NULL; + } + switch (type) { + case 1: + { + double d; + if (max < 8) { + goto invalid; + } + memcpy(&d, buffer + *position, 8); + value = PyFloat_FromDouble(BSON_DOUBLE_FROM_LE(d)); + *position += 8; + break; + } + case 2: + case 14: + { + uint32_t value_length; + if (max < 4) { + goto invalid; + } + memcpy(&value_length, buffer + *position, 4); + value_length = BSON_UINT32_FROM_LE(value_length); + /* Encoded string length + string */ + if (!value_length || max < value_length || max < 4 + value_length) { + goto invalid; + } + *position += 4; + /* Strings must end in \0 */ + if (buffer[*position + value_length - 1]) { + goto invalid; + } + value = PyUnicode_DecodeUTF8( + buffer + *position, value_length - 1, + options->unicode_decode_error_handler); + if (!value) { + goto invalid; + } + *position += value_length; + break; + } + case 3: + { + uint32_t size; + + if (max < 4) { + goto invalid; + } + memcpy(&size, buffer + *position, 4); + size = BSON_UINT32_FROM_LE(size); + if (size < BSON_MIN_SIZE || max < size) { + goto invalid; + } + /* Check for bad eoo */ + if (buffer[*position + size - 1]) { + goto invalid; + } + + value = elements_to_dict(self, buffer + *position, + size, options); + if (!value) { + goto invalid; + } + + if (options->is_raw_bson) { + *position += size; + break; + } + + /* Hook for DBRefs */ + value = _dbref_hook(self, value); + if (!value) { + goto invalid; + } + + *position += size; + break; + } + case 4: + { + uint32_t size, end; + + if (max < 4) { + goto invalid; + } + memcpy(&size, buffer + *position, 4); + size = BSON_UINT32_FROM_LE(size); + if (size < BSON_MIN_SIZE || max < size) { + goto invalid; + } + + end = *position + size - 1; + /* Check for bad eoo */ + if (buffer[end]) { + goto invalid; + } + + if (raw_array != 0) { + // Treat it as a binary buffer. + value = PyBytes_FromStringAndSize(buffer + *position, size); + *position += size; + break; + } + + *position += 4; + + value = PyList_New(0); + if (!value) { + goto invalid; + } + while (*position < end) { + PyObject* to_append; + + unsigned char bson_type = (unsigned char)buffer[(*position)++]; + + size_t key_size = strlen(buffer + *position); + if (max < key_size) { + Py_DECREF(value); + goto invalid; + } + /* just skip the key, they're in order. */ + *position += (unsigned)key_size + 1; + if (Py_EnterRecursiveCall(" while decoding a list value")) { + Py_DECREF(value); + goto invalid; + } + to_append = get_value(self, name, buffer, position, bson_type, + max - (unsigned)key_size, options, raw_array); + Py_LeaveRecursiveCall(); + if (!to_append) { + Py_DECREF(value); + goto invalid; + } + if (PyList_Append(value, to_append) < 0) { + Py_DECREF(value); + Py_DECREF(to_append); + goto invalid; + } + Py_DECREF(to_append); + } + if (*position != end) { + goto invalid; + } + (*position)++; + break; + } + case 5: + { + PyObject* data; + PyObject* st; + uint32_t length, length2; + unsigned char subtype; + + if (max < 5) { + goto invalid; + } + memcpy(&length, buffer + *position, 4); + length = BSON_UINT32_FROM_LE(length); + if (max < length) { + goto invalid; + } + + subtype = (unsigned char)buffer[*position + 4]; + *position += 5; + if (subtype == 2) { + if (length < 4) { + goto invalid; + } + memcpy(&length2, buffer + *position, 4); + length2 = BSON_UINT32_FROM_LE(length2); + if (length2 != length - 4) { + goto invalid; + } + } + /* Python3 special case. Decode BSON binary subtype 0 to bytes. */ + if (subtype == 0) { + value = PyBytes_FromStringAndSize(buffer + *position, length); + *position += length; + break; + } + if (subtype == 2) { + data = PyBytes_FromStringAndSize(buffer + *position + 4, length - 4); + } else { + data = PyBytes_FromStringAndSize(buffer + *position, length); + } + if (!data) { + goto invalid; + } + /* Encode as UUID or Binary based on options->uuid_rep */ + if (subtype == 3 || subtype == 4) { + PyObject* binary_value = NULL; + char uuid_rep = options->uuid_rep; + + /* UUID should always be 16 bytes */ + if (length != 16) { + goto uuiderror; + } + + binary_value = PyObject_CallFunction(state->Binary, "(Oi)", data, subtype); + if (binary_value == NULL) { + goto uuiderror; + } + + if ((uuid_rep == UNSPECIFIED) || + (subtype == 4 && uuid_rep != STANDARD) || + (subtype == 3 && uuid_rep == STANDARD)) { + value = binary_value; + Py_INCREF(value); + } else { + PyObject *uuid_rep_obj = PyLong_FromLong(uuid_rep); + if (!uuid_rep_obj) { + goto uuiderror; + } + value = PyObject_CallMethodObjArgs(binary_value, state->_as_uuid_str, uuid_rep_obj, NULL); + Py_DECREF(uuid_rep_obj); + } + + uuiderror: + Py_XDECREF(binary_value); + Py_DECREF(data); + if (!value) { + goto invalid; + } + *position += length; + break; + } + + st = PyLong_FromLong(subtype); + if (!st) { + Py_DECREF(data); + goto invalid; + } + value = PyObject_CallFunctionObjArgs(state->Binary, data, st, NULL); + Py_DECREF(st); + Py_DECREF(data); + if (!value) { + goto invalid; + } + *position += length; + break; + } + case 6: + case 10: + { + value = Py_None; + Py_INCREF(value); + break; + } + case 7: + { + if (max < 12) { + goto invalid; + } + value = PyObject_CallFunction(state->ObjectId, "y#", buffer + *position, (Py_ssize_t)12); + *position += 12; + break; + } + case 8: + { + char boolean_raw = buffer[(*position)++]; + if (0 == boolean_raw) { + value = Py_False; + } else if (1 == boolean_raw) { + value = Py_True; + } else { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_Format(InvalidBSON, "invalid boolean value: %x", boolean_raw); + Py_DECREF(InvalidBSON); + } + return NULL; + } + Py_INCREF(value); + break; + } + case 9: + { + int64_t millis; + if (max < 8) { + goto invalid; + } + memcpy(&millis, buffer + *position, 8); + millis = (int64_t)BSON_UINT64_FROM_LE(millis); + *position += 8; + + value = decode_datetime(self, millis, options); + break; + } + case 11: + { + PyObject* pattern; + int flags; + size_t flags_length, i; + size_t pattern_length = strlen(buffer + *position); + if (pattern_length > BSON_MAX_SIZE || max < pattern_length) { + goto invalid; + } + pattern = PyUnicode_DecodeUTF8( + buffer + *position, pattern_length, + options->unicode_decode_error_handler); + if (!pattern) { + goto invalid; + } + *position += (unsigned)pattern_length + 1; + flags_length = strlen(buffer + *position); + if (flags_length > BSON_MAX_SIZE || + (BSON_MAX_SIZE - pattern_length) < flags_length) { + Py_DECREF(pattern); + goto invalid; + } + if (max < pattern_length + flags_length) { + Py_DECREF(pattern); + goto invalid; + } + flags = 0; + for (i = 0; i < flags_length; i++) { + if (buffer[*position + i] == 'i') { + flags |= 2; + } else if (buffer[*position + i] == 'l') { + flags |= 4; + } else if (buffer[*position + i] == 'm') { + flags |= 8; + } else if (buffer[*position + i] == 's') { + flags |= 16; + } else if (buffer[*position + i] == 'u') { + flags |= 32; + } else if (buffer[*position + i] == 'x') { + flags |= 64; + } + } + *position += (unsigned)flags_length + 1; + + value = PyObject_CallFunction(state->Regex, "Oi", pattern, flags); + Py_DECREF(pattern); + break; + } + case 12: + { + uint32_t coll_length; + PyObject* collection; + PyObject* id = NULL; + + if (max < 4) { + goto invalid; + } + memcpy(&coll_length, buffer + *position, 4); + coll_length = BSON_UINT32_FROM_LE(coll_length); + /* Encoded string length + string + 12 byte ObjectId */ + if (!coll_length || max < coll_length || max < 4 + coll_length + 12) { + goto invalid; + } + *position += 4; + /* Strings must end in \0 */ + if (buffer[*position + coll_length - 1]) { + goto invalid; + } + + collection = PyUnicode_DecodeUTF8( + buffer + *position, coll_length - 1, + options->unicode_decode_error_handler); + if (!collection) { + goto invalid; + } + *position += coll_length; + + id = PyObject_CallFunction(state->ObjectId, "y#", buffer + *position, (Py_ssize_t)12); + if (!id) { + Py_DECREF(collection); + goto invalid; + } + *position += 12; + value = PyObject_CallFunctionObjArgs(state->DBRef, collection, id, NULL); + Py_DECREF(collection); + Py_DECREF(id); + break; + } + case 13: + { + PyObject* code; + uint32_t value_length; + if (max < 4) { + goto invalid; + } + memcpy(&value_length, buffer + *position, 4); + value_length = BSON_UINT32_FROM_LE(value_length); + /* Encoded string length + string */ + if (!value_length || max < value_length || max < 4 + value_length) { + goto invalid; + } + *position += 4; + /* Strings must end in \0 */ + if (buffer[*position + value_length - 1]) { + goto invalid; + } + code = PyUnicode_DecodeUTF8( + buffer + *position, value_length - 1, + options->unicode_decode_error_handler); + if (!code) { + goto invalid; + } + *position += value_length; + value = PyObject_CallFunctionObjArgs(state->Code, code, NULL, NULL); + Py_DECREF(code); + break; + } + case 15: + { + uint32_t c_w_s_size; + uint32_t code_size; + uint32_t scope_size; + uint32_t len; + PyObject* code; + PyObject* scope; + + if (max < 8) { + goto invalid; + } + + memcpy(&c_w_s_size, buffer + *position, 4); + c_w_s_size = BSON_UINT32_FROM_LE(c_w_s_size); + *position += 4; + + if (max < c_w_s_size) { + goto invalid; + } + + memcpy(&code_size, buffer + *position, 4); + code_size = BSON_UINT32_FROM_LE(code_size); + /* code_w_scope length + code length + code + scope length */ + len = 4 + 4 + code_size + 4; + if (!code_size || max < code_size || max < len || len < code_size) { + goto invalid; + } + *position += 4; + /* Strings must end in \0 */ + if (buffer[*position + code_size - 1]) { + goto invalid; + } + code = PyUnicode_DecodeUTF8( + buffer + *position, code_size - 1, + options->unicode_decode_error_handler); + if (!code) { + goto invalid; + } + *position += code_size; + + memcpy(&scope_size, buffer + *position, 4); + scope_size = BSON_UINT32_FROM_LE(scope_size); + /* code length + code + scope length + scope */ + len = 4 + 4 + code_size + scope_size; + if (scope_size < BSON_MIN_SIZE || len != c_w_s_size || len < scope_size) { + Py_DECREF(code); + goto invalid; + } + + /* Check for bad eoo */ + if (buffer[*position + scope_size - 1]) { + goto invalid; + } + scope = elements_to_dict(self, buffer + *position, + scope_size, options); + if (!scope) { + Py_DECREF(code); + goto invalid; + } + *position += scope_size; + + value = PyObject_CallFunctionObjArgs(state->Code, code, scope, NULL); + Py_DECREF(code); + Py_DECREF(scope); + break; + } + case 16: + { + int32_t i; + if (max < 4) { + goto invalid; + } + memcpy(&i, buffer + *position, 4); + i = (int32_t)BSON_UINT32_FROM_LE(i); + value = PyLong_FromLong(i); + if (!value) { + goto invalid; + } + *position += 4; + break; + } + case 17: + { + uint32_t time, inc; + if (max < 8) { + goto invalid; + } + memcpy(&inc, buffer + *position, 4); + memcpy(&time, buffer + *position + 4, 4); + inc = BSON_UINT32_FROM_LE(inc); + time = BSON_UINT32_FROM_LE(time); + value = PyObject_CallFunction(state->Timestamp, "II", time, inc); + *position += 8; + break; + } + case 18: + { + int64_t ll; + if (max < 8) { + goto invalid; + } + memcpy(&ll, buffer + *position, 8); + ll = (int64_t)BSON_UINT64_FROM_LE(ll); + value = PyObject_CallFunction(state->BSONInt64, "L", ll); + *position += 8; + break; + } + case 19: + { + if (max < 16) { + goto invalid; + } + PyObject *_bytes_obj = PyBytes_FromStringAndSize(buffer + *position, (Py_ssize_t)16); + if (!_bytes_obj) { + goto invalid; + } + value = PyObject_CallMethodObjArgs(state->Decimal128, state->_from_bid_str, _bytes_obj, NULL); + Py_DECREF(_bytes_obj); + *position += 16; + break; + } + case 255: + { + value = PyObject_CallFunctionObjArgs(state->MinKey, NULL); + break; + } + case 127: + { + value = PyObject_CallFunctionObjArgs(state->MaxKey, NULL); + break; + } + default: + { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyObject* bobj = PyBytes_FromFormat("%c", type); + if (bobj) { + PyObject* repr = PyObject_Repr(bobj); + Py_DECREF(bobj); + /* + * See http://bugs.python.org/issue22023 for why we can't + * just use PyUnicode_FromFormat with %S or %R to do this + * work. + */ + if (repr) { + PyObject* left = PyUnicode_FromString( + "Detected unknown BSON type "); + if (left) { + PyObject* lmsg = PyUnicode_Concat(left, repr); + Py_DECREF(left); + if (lmsg) { + PyObject* errmsg = PyUnicode_FromFormat( + "%U for fieldname '%U'. Are you using the " + "latest driver version?", lmsg, name); + if (errmsg) { + PyErr_SetObject(InvalidBSON, errmsg); + Py_DECREF(errmsg); + } + Py_DECREF(lmsg); + } + } + Py_DECREF(repr); + } + } + Py_DECREF(InvalidBSON); + } + goto invalid; + } + } + + if (value) { + if (!options->type_registry.is_decoder_empty) { + PyObject* value_type = NULL; + PyObject* converter = NULL; + value_type = PyObject_Type(value); + if (value_type == NULL) { + goto invalid; + } + converter = PyDict_GetItem(options->type_registry.decoder_map, value_type); + if (converter != NULL) { + PyObject* new_value = PyObject_CallFunctionObjArgs(converter, value, NULL); + Py_DECREF(value_type); + Py_DECREF(value); + return new_value; + } else { + Py_DECREF(value_type); + return value; + } + } + return value; + } + + invalid: + + /* + * Wrap any non-InvalidBSON errors in InvalidBSON. + */ + if (PyErr_Occurred()) { + PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; + PyObject *InvalidBSON = NULL; + + /* + * Calling _error clears the error state, so fetch it first. + */ + PyErr_Fetch(&etype, &evalue, &etrace); + + /* Dont reraise anything but PyExc_Exceptions as InvalidBSON. */ + if (PyErr_GivenExceptionMatches(etype, PyExc_Exception)) { + InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + if (!PyErr_GivenExceptionMatches(etype, InvalidBSON)) { + /* + * Raise InvalidBSON(str(e)). + */ + Py_DECREF(etype); + etype = InvalidBSON; + + if (evalue) { + PyObject *msg = PyObject_Str(evalue); + Py_DECREF(evalue); + evalue = msg; + } + PyErr_NormalizeException(&etype, &evalue, &etrace); + } else { + /* + * The current exception matches InvalidBSON, so we don't + * need this reference after all. + */ + Py_DECREF(InvalidBSON); + } + } + } + /* Steals references to args. */ + PyErr_Restore(etype, evalue, etrace); + } else { + PyObject *InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "invalid length or type code"); + Py_DECREF(InvalidBSON); + } + } + return NULL; +} + +/* + * Get the next 'name' and 'value' from a document in a string, whose position + * is provided. + * + * Returns the position of the next element in the document, or -1 on error. + */ +static int _element_to_dict(PyObject* self, const char* string, + unsigned position, unsigned max, + const codec_options_t* options, + int raw_array, + PyObject** name, PyObject** value) { + unsigned char type = (unsigned char)string[position++]; + size_t name_length = strlen(string + position); + if (name_length > BSON_MAX_SIZE || position + name_length >= max) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "field name too large"); + Py_DECREF(InvalidBSON); + } + return -1; + } + *name = PyUnicode_DecodeUTF8( + string + position, name_length, + options->unicode_decode_error_handler); + if (!*name) { + /* If NULL is returned then wrap the UnicodeDecodeError + in an InvalidBSON error */ + PyObject *etype = NULL, *evalue = NULL, *etrace = NULL; + PyObject *InvalidBSON = NULL; + + PyErr_Fetch(&etype, &evalue, &etrace); + if (PyErr_GivenExceptionMatches(etype, PyExc_Exception)) { + InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + Py_DECREF(etype); + etype = InvalidBSON; + + if (evalue) { + PyObject *msg = PyObject_Str(evalue); + Py_DECREF(evalue); + evalue = msg; + } + PyErr_NormalizeException(&etype, &evalue, &etrace); + } + } + PyErr_Restore(etype, evalue, etrace); + return -1; + } + position += (unsigned)name_length + 1; + *value = get_value(self, *name, string, &position, type, + max - position, options, raw_array); + if (!*value) { + Py_DECREF(*name); + return -1; + } + return position; +} + +static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) { + /* TODO: Support buffer protocol */ + char* string; + PyObject* bson; + PyObject* options_obj = NULL; + codec_options_t options; + unsigned position; + unsigned max; + int new_position; + int raw_array = 0; + PyObject* name; + PyObject* value; + PyObject* result_tuple; + + if (!(PyArg_ParseTuple(args, "OIIOp", &bson, &position, &max, + &options_obj, &raw_array) && + convert_codec_options(self, options_obj, &options))) { + return NULL; + } + + if (!PyBytes_Check(bson)) { + PyErr_SetString(PyExc_TypeError, "argument to _element_to_dict must be a bytes object"); + return NULL; + } + string = PyBytes_AS_STRING(bson); + + new_position = _element_to_dict(self, string, position, max, &options, raw_array, &name, &value); + if (new_position < 0) { + return NULL; + } + + result_tuple = Py_BuildValue("NNi", name, value, new_position); + if (!result_tuple) { + Py_DECREF(name); + Py_DECREF(value); + return NULL; + } + + destroy_codec_options(&options); + return result_tuple; +} + +static PyObject* _elements_to_dict(PyObject* self, const char* string, + unsigned max, + const codec_options_t* options) { + unsigned position = 0; + PyObject* dict = PyObject_CallObject(options->document_class, NULL); + if (!dict) { + return NULL; + } + int raw_array = 0; + while (position < max) { + PyObject* name = NULL; + PyObject* value = NULL; + int new_position; + + new_position = _element_to_dict( + self, string, position, max, options, raw_array, &name, &value); + if (new_position < 0) { + Py_DECREF(dict); + return NULL; + } else { + position = (unsigned)new_position; + } + + PyObject_SetItem(dict, name, value); + Py_DECREF(name); + Py_DECREF(value); + } + return dict; +} + +static PyObject* elements_to_dict(PyObject* self, const char* string, + unsigned max, + const codec_options_t* options) { + PyObject* result; + if (options->is_raw_bson) { + return PyObject_CallFunction( + options->document_class, "y#O", + string, max, options->options_obj); + } + if (Py_EnterRecursiveCall(" while decoding a BSON document")) + return NULL; + result = _elements_to_dict(self, string + 4, max - 5, options); + Py_LeaveRecursiveCall(); + return result; +} + +static int _get_buffer(PyObject *exporter, Py_buffer *view) { + if (PyObject_GetBuffer(exporter, view, PyBUF_SIMPLE) == -1) { + return 0; + } + if (!PyBuffer_IsContiguous(view, 'C')) { + PyErr_SetString(PyExc_ValueError, + "must be a contiguous buffer"); + goto fail; + } + if (!view->buf || view->len < 0) { + PyErr_SetString(PyExc_ValueError, "invalid buffer"); + goto fail; + } + if (view->itemsize != 1) { + PyErr_SetString(PyExc_ValueError, + "buffer data must be ascii or utf8"); + goto fail; + } + return 1; +fail: + PyBuffer_Release(view); + return 0; +} + +static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) { + int32_t size; + Py_ssize_t total_size; + const char* string; + PyObject* bson = NULL; + codec_options_t options; + PyObject* result = NULL; + PyObject* options_obj; + Py_buffer view = {0}; + + if (! (PyArg_ParseTuple(args, "OO", &bson, &options_obj) && + convert_codec_options(self, options_obj, &options))) { + return result; + } + + if (!_get_buffer(bson, &view)) { + destroy_codec_options(&options); + return result; + } + + total_size = view.len; + + if (total_size < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, + "not enough data for a BSON document"); + Py_DECREF(InvalidBSON); + } + goto done;; + } + + string = (char*)view.buf; + memcpy(&size, string, 4); + size = (int32_t)BSON_UINT32_FROM_LE(size); + if (size < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "invalid message size"); + Py_DECREF(InvalidBSON); + } + goto done; + } + + if (total_size < size || total_size > BSON_MAX_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "objsize too large"); + Py_DECREF(InvalidBSON); + } + goto done; + } + + if (size != total_size || string[size - 1]) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "bad eoo"); + Py_DECREF(InvalidBSON); + } + goto done; + } + + result = elements_to_dict(self, string, (unsigned)size, &options); +done: + PyBuffer_Release(&view); + destroy_codec_options(&options); + return result; +} + +static PyObject* _cbson_decode_all(PyObject* self, PyObject* args) { + int32_t size; + Py_ssize_t total_size; + const char* string; + PyObject* bson; + PyObject* dict; + PyObject* result = NULL; + codec_options_t options; + PyObject* options_obj = NULL; + Py_buffer view = {0}; + + if (!(PyArg_ParseTuple(args, "OO", &bson, &options_obj) && + convert_codec_options(self, options_obj, &options))) { + return NULL; + } + + if (!_get_buffer(bson, &view)) { + destroy_codec_options(&options); + return NULL; + } + total_size = view.len; + string = (char*)view.buf; + + if (!(result = PyList_New(0))) { + goto fail; + } + + while (total_size > 0) { + if (total_size < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, + "not enough data for a BSON document"); + Py_DECREF(InvalidBSON); + } + Py_DECREF(result); + goto fail; + } + + memcpy(&size, string, 4); + size = (int32_t)BSON_UINT32_FROM_LE(size); + if (size < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "invalid message size"); + Py_DECREF(InvalidBSON); + } + Py_DECREF(result); + goto fail; + } + + if (total_size < size) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "objsize too large"); + Py_DECREF(InvalidBSON); + } + Py_DECREF(result); + goto fail; + } + + if (string[size - 1]) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "bad eoo"); + Py_DECREF(InvalidBSON); + } + Py_DECREF(result); + goto fail; + } + + dict = elements_to_dict(self, string, (unsigned)size, &options); + if (!dict) { + Py_DECREF(result); + goto fail; + } + if (PyList_Append(result, dict) < 0) { + Py_DECREF(dict); + Py_DECREF(result); + goto fail; + } + Py_DECREF(dict); + string += size; + total_size -= size; + } + goto done; +fail: + result = NULL; +done: + PyBuffer_Release(&view); + destroy_codec_options(&options); + return result; +} + + +static PyObject* _cbson_array_of_documents_to_buffer(PyObject* self, PyObject* args) { + uint32_t size; + uint32_t value_length; + uint32_t position = 0; + buffer_t buffer; + const char* string; + PyObject* arr; + PyObject* result = NULL; + Py_buffer view = {0}; + + if (!PyArg_ParseTuple(args, "O", &arr)) { + return NULL; + } + + if (!_get_buffer(arr, &view)) { + return NULL; + } + + buffer = pymongo_buffer_new(); + if (!buffer) { + PyBuffer_Release(&view); + return NULL; + } + + string = (char*)view.buf; + + if (view.len < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, + "not enough data for a BSON document"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + memcpy(&size, string, 4); + size = BSON_UINT32_FROM_LE(size); + + /* validate the size of the array */ + if (view.len != (int32_t)size || (int32_t)size < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "objsize too large"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + if (string[size - 1]) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "bad eoo"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + /* save space for length */ + if (pymongo_buffer_save_space(buffer, size) == -1) { + goto fail; + } + pymongo_buffer_update_position(buffer, 0); + + position += 4; + while (position < size - 1) { + // Verify the value is an object. + unsigned char type = (unsigned char)string[position]; + if (type != 3) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "array element was not an object"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + // Just skip the keys. + position = position + strlen(string + position) + 1; + + if (position >= size || (size - position) < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "invalid array content"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + memcpy(&value_length, string + position, 4); + value_length = BSON_UINT32_FROM_LE(value_length); + if (value_length < BSON_MIN_SIZE) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, "invalid message size"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + if (pymongo_buffer_write(buffer, string + position, value_length) == 1) { + goto fail; + } + position += value_length; + } + + if (position != size - 1) { + PyObject* InvalidBSON = _error("InvalidBSON"); + if (InvalidBSON) { + PyErr_SetString(InvalidBSON, + "bad object or element length"); + Py_DECREF(InvalidBSON); + } + goto fail; + } + + /* objectify buffer */ + result = Py_BuildValue("y#", pymongo_buffer_get_buffer(buffer), + (Py_ssize_t)pymongo_buffer_get_position(buffer)); + goto done; +fail: + result = NULL; +done: + PyBuffer_Release(&view); + pymongo_buffer_free(buffer); + return result; +} + + +static PyMethodDef _CBSONMethods[] = { + {"_dict_to_bson", _cbson_dict_to_bson, METH_VARARGS, + "convert a dictionary to a string containing its BSON representation."}, + {"_bson_to_dict", _cbson_bson_to_dict, METH_VARARGS, + "convert a BSON string to a SON object."}, + {"_decode_all", _cbson_decode_all, METH_VARARGS, + "convert binary data to a sequence of documents."}, + {"_element_to_dict", _cbson_element_to_dict, METH_VARARGS, + "Decode a single key, value pair."}, + {"_array_of_documents_to_buffer", _cbson_array_of_documents_to_buffer, METH_VARARGS, "Convert raw array of documents to a stream of BSON documents"}, + {"_test_long_long_to_str", _test_long_long_to_str, METH_VARARGS, "Test conversion of extreme and common Py_ssize_t values to str."}, + {NULL, NULL, 0, NULL} +}; + +#define INITERROR return -1; +static int _cbson_traverse(PyObject *m, visitproc visit, void *arg) { + struct module_state *state = GETSTATE(m); + if (!state) { + return 0; + } + Py_VISIT(state->Binary); + Py_VISIT(state->Code); + Py_VISIT(state->ObjectId); + Py_VISIT(state->DBRef); + Py_VISIT(state->Regex); + Py_VISIT(state->UUID); + Py_VISIT(state->Timestamp); + Py_VISIT(state->MinKey); + Py_VISIT(state->MaxKey); + Py_VISIT(state->UTC); + Py_VISIT(state->REType); + Py_VISIT(state->_type_marker_str); + Py_VISIT(state->_flags_str); + Py_VISIT(state->_pattern_str); + Py_VISIT(state->_encoder_map_str); + Py_VISIT(state->_decoder_map_str); + Py_VISIT(state->_fallback_encoder_str); + Py_VISIT(state->_raw_str); + Py_VISIT(state->_subtype_str); + Py_VISIT(state->_binary_str); + Py_VISIT(state->_scope_str); + Py_VISIT(state->_inc_str); + Py_VISIT(state->_time_str); + Py_VISIT(state->_bid_str); + Py_VISIT(state->_replace_str); + Py_VISIT(state->_astimezone_str); + Py_VISIT(state->_id_str); + Py_VISIT(state->_dollar_ref_str); + Py_VISIT(state->_dollar_id_str); + Py_VISIT(state->_dollar_db_str); + Py_VISIT(state->_tzinfo_str); + Py_VISIT(state->_as_doc_str); + Py_VISIT(state->_utcoffset_str); + Py_VISIT(state->_from_uuid_str); + Py_VISIT(state->_as_uuid_str); + Py_VISIT(state->_from_bid_str); + Py_VISIT(state->min_datetime); + Py_VISIT(state->max_datetime); + Py_VISIT(state->replace_args); + Py_VISIT(state->replace_kwargs); + return 0; +} + +static int _cbson_clear(PyObject *m) { + struct module_state *state = GETSTATE(m); + if (!state) { + return 0; + } + Py_CLEAR(state->Binary); + Py_CLEAR(state->Code); + Py_CLEAR(state->ObjectId); + Py_CLEAR(state->DBRef); + Py_CLEAR(state->Regex); + Py_CLEAR(state->UUID); + Py_CLEAR(state->Timestamp); + Py_CLEAR(state->MinKey); + Py_CLEAR(state->MaxKey); + Py_CLEAR(state->UTC); + Py_CLEAR(state->REType); + Py_CLEAR(state->_type_marker_str); + Py_CLEAR(state->_flags_str); + Py_CLEAR(state->_pattern_str); + Py_CLEAR(state->_encoder_map_str); + Py_CLEAR(state->_decoder_map_str); + Py_CLEAR(state->_fallback_encoder_str); + Py_CLEAR(state->_raw_str); + Py_CLEAR(state->_subtype_str); + Py_CLEAR(state->_binary_str); + Py_CLEAR(state->_scope_str); + Py_CLEAR(state->_inc_str); + Py_CLEAR(state->_time_str); + Py_CLEAR(state->_bid_str); + Py_CLEAR(state->_replace_str); + Py_CLEAR(state->_astimezone_str); + Py_CLEAR(state->_id_str); + Py_CLEAR(state->_dollar_ref_str); + Py_CLEAR(state->_dollar_id_str); + Py_CLEAR(state->_dollar_db_str); + Py_CLEAR(state->_tzinfo_str); + Py_CLEAR(state->_as_doc_str); + Py_CLEAR(state->_utcoffset_str); + Py_CLEAR(state->_from_uuid_str); + Py_CLEAR(state->_as_uuid_str); + Py_CLEAR(state->_from_bid_str); + Py_CLEAR(state->min_datetime); + Py_CLEAR(state->max_datetime); + Py_CLEAR(state->replace_args); + Py_CLEAR(state->replace_kwargs); + return 0; +} + +/* Multi-phase extension module initialization code. + * See https://peps.python.org/pep-0489/. +*/ +static int +_cbson_exec(PyObject *m) +{ + PyObject *c_api_object; + static void *_cbson_API[_cbson_API_POINTER_COUNT]; + + PyDateTime_IMPORT; + if (PyDateTimeAPI == NULL) { + INITERROR; + } + + /* Export C API */ + _cbson_API[_cbson_buffer_write_bytes_INDEX] = (void *) buffer_write_bytes; + _cbson_API[_cbson_write_dict_INDEX] = (void *) write_dict; + _cbson_API[_cbson_write_pair_INDEX] = (void *) write_pair; + _cbson_API[_cbson_decode_and_write_pair_INDEX] = (void *) decode_and_write_pair; + _cbson_API[_cbson_convert_codec_options_INDEX] = (void *) convert_codec_options; + _cbson_API[_cbson_destroy_codec_options_INDEX] = (void *) destroy_codec_options; + _cbson_API[_cbson_buffer_write_double_INDEX] = (void *) buffer_write_double; + _cbson_API[_cbson_buffer_write_int32_INDEX] = (void *) buffer_write_int32; + _cbson_API[_cbson_buffer_write_int64_INDEX] = (void *) buffer_write_int64; + _cbson_API[_cbson_buffer_write_int32_at_position_INDEX] = + (void *) buffer_write_int32_at_position; + _cbson_API[_cbson_downcast_and_check_INDEX] = (void *) _downcast_and_check; + + c_api_object = PyCapsule_New((void *) _cbson_API, "_cbson._C_API", NULL); + if (c_api_object == NULL) + INITERROR; + + /* Import several python objects */ + if (_load_python_objects(m)) { + Py_DECREF(c_api_object); + Py_DECREF(m); + INITERROR; + } + +#if PY_VERSION_HEX >= 0x030D0000 + if (PyModule_Add(m, "_C_API", c_api_object) < 0) { + Py_DECREF(m); + INITERROR; + } +# else + if (PyModule_AddObject(m, "_C_API", c_api_object) < 0) { + Py_DECREF(c_api_object); + Py_DECREF(m); + INITERROR; + } +#endif + + return 0; +} + +static PyModuleDef_Slot _cbson_slots[] = { + {Py_mod_exec, _cbson_exec}, +#if defined(Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED) + {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED}, +#endif +#if PY_VERSION_HEX >= 0x030D0000 + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif + {0, NULL}, +}; + + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_cbson", + NULL, + sizeof(struct module_state), + _CBSONMethods, + _cbson_slots, + _cbson_traverse, + _cbson_clear, + NULL +}; + +PyMODINIT_FUNC +PyInit__cbson(void) +{ + return PyModuleDef_Init(&moduledef); +} diff --git a/.venv/lib/python3.12/site-packages/bson/_cbsonmodule.h b/.venv/lib/python3.12/site-packages/bson/_cbsonmodule.h new file mode 100644 index 0000000..3be2b74 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/_cbsonmodule.h @@ -0,0 +1,181 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "bson-endian.h" + +#ifndef _CBSONMODULE_H +#define _CBSONMODULE_H + +#if defined(WIN32) || defined(_MSC_VER) +/* + * This macro is basically an implementation of asprintf for win32 + * We print to the provided buffer to get the string value as an int. + * USE LL2STR. This is kept only to test LL2STR. + */ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#define INT2STRING(buffer, i) \ + _snprintf_s((buffer), \ + _scprintf("%lld", (i)) + 1, \ + _scprintf("%lld", (i)) + 1, \ + "%lld", \ + (i)) +#define STRCAT(dest, n, src) strcat_s((dest), (n), (src)) +#else +#define INT2STRING(buffer, i) \ + _snprintf((buffer), \ + _scprintf("%lld", (i)) + 1, \ + "%lld", \ + (i)) +#define STRCAT(dest, n, src) strcat((dest), (src)) +#endif +#else +#define INT2STRING(buffer, i) snprintf((buffer), sizeof((buffer)), "%lld", (i)) +#define STRCAT(dest, n, src) strcat((dest), (src)) +#endif + +/* Just enough space in char array to hold LLONG_MIN and null terminator */ +#define BUF_SIZE 21 +/* Converts integer to its string representation in decimal notation. */ +extern int cbson_long_long_to_str(long long int num, char* str, size_t size); +#define LL2STR(buffer, i) cbson_long_long_to_str((i), (buffer), sizeof(buffer)) + +typedef struct type_registry_t { + PyObject* encoder_map; + PyObject* decoder_map; + PyObject* fallback_encoder; + PyObject* registry_obj; + unsigned char is_encoder_empty; + unsigned char is_decoder_empty; + unsigned char has_fallback_encoder; +} type_registry_t; + +typedef struct codec_options_t { + PyObject* document_class; + unsigned char tz_aware; + unsigned char uuid_rep; + char* unicode_decode_error_handler; + PyObject* tzinfo; + type_registry_t type_registry; + unsigned char datetime_conversion; + PyObject* options_obj; + unsigned char is_raw_bson; +} codec_options_t; + +/* C API functions */ +#define _cbson_buffer_write_bytes_INDEX 0 +#define _cbson_buffer_write_bytes_RETURN int +#define _cbson_buffer_write_bytes_PROTO (buffer_t buffer, const char* data, int size) + +#define _cbson_write_dict_INDEX 1 +#define _cbson_write_dict_RETURN int +#define _cbson_write_dict_PROTO (PyObject* self, buffer_t buffer, PyObject* dict, unsigned char check_keys, const codec_options_t* options, unsigned char top_level) + +#define _cbson_write_pair_INDEX 2 +#define _cbson_write_pair_RETURN int +#define _cbson_write_pair_PROTO (PyObject* self, buffer_t buffer, const char* name, int name_length, PyObject* value, unsigned char check_keys, const codec_options_t* options, unsigned char allow_id) + +#define _cbson_decode_and_write_pair_INDEX 3 +#define _cbson_decode_and_write_pair_RETURN int +#define _cbson_decode_and_write_pair_PROTO (PyObject* self, buffer_t buffer, PyObject* key, PyObject* value, unsigned char check_keys, const codec_options_t* options, unsigned char top_level) + +#define _cbson_convert_codec_options_INDEX 4 +#define _cbson_convert_codec_options_RETURN int +#define _cbson_convert_codec_options_PROTO (PyObject* self, PyObject* options_obj, codec_options_t* options) + +#define _cbson_destroy_codec_options_INDEX 5 +#define _cbson_destroy_codec_options_RETURN void +#define _cbson_destroy_codec_options_PROTO (codec_options_t* options) + +#define _cbson_buffer_write_double_INDEX 6 +#define _cbson_buffer_write_double_RETURN int +#define _cbson_buffer_write_double_PROTO (buffer_t buffer, double data) + +#define _cbson_buffer_write_int32_INDEX 7 +#define _cbson_buffer_write_int32_RETURN int +#define _cbson_buffer_write_int32_PROTO (buffer_t buffer, int32_t data) + +#define _cbson_buffer_write_int64_INDEX 8 +#define _cbson_buffer_write_int64_RETURN int +#define _cbson_buffer_write_int64_PROTO (buffer_t buffer, int64_t data) + +#define _cbson_buffer_write_int32_at_position_INDEX 9 +#define _cbson_buffer_write_int32_at_position_RETURN void +#define _cbson_buffer_write_int32_at_position_PROTO (buffer_t buffer, int position, int32_t data) + +#define _cbson_downcast_and_check_INDEX 10 +#define _cbson_downcast_and_check_RETURN int +#define _cbson_downcast_and_check_PROTO (Py_ssize_t size, uint8_t extra) + +/* Total number of C API pointers */ +#define _cbson_API_POINTER_COUNT 11 + +#ifdef _CBSON_MODULE +/* This section is used when compiling _cbsonmodule */ + +static _cbson_buffer_write_bytes_RETURN buffer_write_bytes _cbson_buffer_write_bytes_PROTO; + +static _cbson_write_dict_RETURN write_dict _cbson_write_dict_PROTO; + +static _cbson_write_pair_RETURN write_pair _cbson_write_pair_PROTO; + +static _cbson_decode_and_write_pair_RETURN decode_and_write_pair _cbson_decode_and_write_pair_PROTO; + +static _cbson_convert_codec_options_RETURN convert_codec_options _cbson_convert_codec_options_PROTO; + +static _cbson_destroy_codec_options_RETURN destroy_codec_options _cbson_destroy_codec_options_PROTO; + +static _cbson_buffer_write_double_RETURN buffer_write_double _cbson_buffer_write_double_PROTO; + +static _cbson_buffer_write_int32_RETURN buffer_write_int32 _cbson_buffer_write_int32_PROTO; + +static _cbson_buffer_write_int64_RETURN buffer_write_int64 _cbson_buffer_write_int64_PROTO; + +static _cbson_buffer_write_int32_at_position_RETURN buffer_write_int32_at_position _cbson_buffer_write_int32_at_position_PROTO; + +static _cbson_downcast_and_check_RETURN _downcast_and_check _cbson_downcast_and_check_PROTO; + +#else +/* This section is used in modules that use _cbsonmodule's API */ + +static void **_cbson_API; + +#define buffer_write_bytes (*(_cbson_buffer_write_bytes_RETURN (*)_cbson_buffer_write_bytes_PROTO) _cbson_API[_cbson_buffer_write_bytes_INDEX]) + +#define write_dict (*(_cbson_write_dict_RETURN (*)_cbson_write_dict_PROTO) _cbson_API[_cbson_write_dict_INDEX]) + +#define write_pair (*(_cbson_write_pair_RETURN (*)_cbson_write_pair_PROTO) _cbson_API[_cbson_write_pair_INDEX]) + +#define decode_and_write_pair (*(_cbson_decode_and_write_pair_RETURN (*)_cbson_decode_and_write_pair_PROTO) _cbson_API[_cbson_decode_and_write_pair_INDEX]) + +#define convert_codec_options (*(_cbson_convert_codec_options_RETURN (*)_cbson_convert_codec_options_PROTO) _cbson_API[_cbson_convert_codec_options_INDEX]) + +#define destroy_codec_options (*(_cbson_destroy_codec_options_RETURN (*)_cbson_destroy_codec_options_PROTO) _cbson_API[_cbson_destroy_codec_options_INDEX]) + +#define buffer_write_double (*(_cbson_buffer_write_double_RETURN (*)_cbson_buffer_write_double_PROTO) _cbson_API[_cbson_buffer_write_double_INDEX]) + +#define buffer_write_int32 (*(_cbson_buffer_write_int32_RETURN (*)_cbson_buffer_write_int32_PROTO) _cbson_API[_cbson_buffer_write_int32_INDEX]) + +#define buffer_write_int64 (*(_cbson_buffer_write_int64_RETURN (*)_cbson_buffer_write_int64_PROTO) _cbson_API[_cbson_buffer_write_int64_INDEX]) + +#define buffer_write_int32_at_position (*(_cbson_buffer_write_int32_at_position_RETURN (*)_cbson_buffer_write_int32_at_position_PROTO) _cbson_API[_cbson_buffer_write_int32_at_position_INDEX]) + +#define _downcast_and_check (*(_cbson_downcast_and_check_RETURN (*)_cbson_downcast_and_check_PROTO) _cbson_API[_cbson_downcast_and_check_INDEX]) + +#define _cbson_IMPORT _cbson_API = (void **)PyCapsule_Import("_cbson._C_API", 0) + +#endif + +#endif // _CBSONMODULE_H diff --git a/.venv/lib/python3.12/site-packages/bson/_helpers.py b/.venv/lib/python3.12/site-packages/bson/_helpers.py new file mode 100644 index 0000000..5a47986 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/_helpers.py @@ -0,0 +1,43 @@ +# Copyright 2021-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Setstate and getstate functions for objects with __slots__, allowing +compatibility with default pickling protocol +""" +from __future__ import annotations + +from typing import Any, Mapping + + +def _setstate_slots(self: Any, state: Any) -> None: + for slot, value in state.items(): + setattr(self, slot, value) + + +def _mangle_name(name: str, prefix: str) -> str: + if name.startswith("__"): + prefix = "_" + prefix + else: + prefix = "" + return prefix + name + + +def _getstate_slots(self: Any) -> Mapping[Any, Any]: + prefix = self.__class__.__name__ + ret = {} + for name in self.__slots__: + mangled_name = _mangle_name(name, prefix) + if hasattr(self, mangled_name): + ret[mangled_name] = getattr(self, mangled_name) + return ret diff --git a/.venv/lib/python3.12/site-packages/bson/binary.py b/.venv/lib/python3.12/site-packages/bson/binary.py new file mode 100644 index 0000000..48eb12b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/binary.py @@ -0,0 +1,577 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import struct +import warnings +from enum import Enum +from typing import TYPE_CHECKING, Any, Optional, Sequence, Tuple, Type, Union, overload +from uuid import UUID + +"""Tools for representing BSON binary data. +""" + +BINARY_SUBTYPE = 0 +"""BSON binary subtype for binary data. + +This is the default subtype for binary data. +""" + +FUNCTION_SUBTYPE = 1 +"""BSON binary subtype for functions. +""" + +OLD_BINARY_SUBTYPE = 2 +"""Old BSON binary subtype for binary data. + +This is the old default subtype, the current +default is :data:`BINARY_SUBTYPE`. +""" + +OLD_UUID_SUBTYPE = 3 +"""Old BSON binary subtype for a UUID. + +:class:`uuid.UUID` instances will automatically be encoded +by :mod:`bson` using this subtype when using +:data:`UuidRepresentation.PYTHON_LEGACY`, +:data:`UuidRepresentation.JAVA_LEGACY`, or +:data:`UuidRepresentation.CSHARP_LEGACY`. + +.. versionadded:: 2.1 +""" + +UUID_SUBTYPE = 4 +"""BSON binary subtype for a UUID. + +This is the standard BSON binary subtype for UUIDs. +:class:`uuid.UUID` instances will automatically be encoded +by :mod:`bson` using this subtype when using +:data:`UuidRepresentation.STANDARD`. +""" + + +if TYPE_CHECKING: + from array import array as _array + from mmap import mmap as _mmap + + +class UuidRepresentation: + UNSPECIFIED = 0 + """An unspecified UUID representation. + + When configured, :class:`uuid.UUID` instances will **not** be + automatically encoded to or decoded from :class:`~bson.binary.Binary`. + When encoding a :class:`uuid.UUID` instance, an error will be raised. + To encode a :class:`uuid.UUID` instance with this configuration, it must + be wrapped in the :class:`~bson.binary.Binary` class by the application + code. When decoding a BSON binary field with a UUID subtype, a + :class:`~bson.binary.Binary` instance will be returned instead of a + :class:`uuid.UUID` instance. + + See `unspecified representation details `_ for details. + + .. versionadded:: 3.11 + """ + + STANDARD = UUID_SUBTYPE + """The standard UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from BSON binary, using RFC-4122 byte order with + binary subtype :data:`UUID_SUBTYPE`. + + See `standard representation details `_ for details. + + .. versionadded:: 3.11 + """ + + PYTHON_LEGACY = OLD_UUID_SUBTYPE + """The Python legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from BSON binary, using RFC-4122 byte order with + binary subtype :data:`OLD_UUID_SUBTYPE`. + + See `python legacy representation details `_ for details. + + .. versionadded:: 3.11 + """ + + JAVA_LEGACY = 5 + """The Java legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from BSON binary subtype :data:`OLD_UUID_SUBTYPE`, + using the Java driver's legacy byte order. + + See `Java Legacy UUID `_ for details. + + .. versionadded:: 3.11 + """ + + CSHARP_LEGACY = 6 + """The C#/.net legacy UUID representation. + + :class:`uuid.UUID` instances will automatically be encoded to + and decoded from BSON binary subtype :data:`OLD_UUID_SUBTYPE`, + using the C# driver's legacy byte order. + + See `C# Legacy UUID `_ for details. + + .. versionadded:: 3.11 + """ + + +STANDARD = UuidRepresentation.STANDARD +"""An alias for :data:`UuidRepresentation.STANDARD`. + +.. versionadded:: 3.0 +""" + +PYTHON_LEGACY = UuidRepresentation.PYTHON_LEGACY +"""An alias for :data:`UuidRepresentation.PYTHON_LEGACY`. + +.. versionadded:: 3.0 +""" + +JAVA_LEGACY = UuidRepresentation.JAVA_LEGACY +"""An alias for :data:`UuidRepresentation.JAVA_LEGACY`. + +.. versionchanged:: 3.6 + BSON binary subtype 4 is decoded using RFC-4122 byte order. +.. versionadded:: 2.3 +""" + +CSHARP_LEGACY = UuidRepresentation.CSHARP_LEGACY +"""An alias for :data:`UuidRepresentation.CSHARP_LEGACY`. + +.. versionchanged:: 3.6 + BSON binary subtype 4 is decoded using RFC-4122 byte order. +.. versionadded:: 2.3 +""" + +ALL_UUID_SUBTYPES = (OLD_UUID_SUBTYPE, UUID_SUBTYPE) +ALL_UUID_REPRESENTATIONS = ( + UuidRepresentation.UNSPECIFIED, + UuidRepresentation.STANDARD, + UuidRepresentation.PYTHON_LEGACY, + UuidRepresentation.JAVA_LEGACY, + UuidRepresentation.CSHARP_LEGACY, +) +UUID_REPRESENTATION_NAMES = { + UuidRepresentation.UNSPECIFIED: "UuidRepresentation.UNSPECIFIED", + UuidRepresentation.STANDARD: "UuidRepresentation.STANDARD", + UuidRepresentation.PYTHON_LEGACY: "UuidRepresentation.PYTHON_LEGACY", + UuidRepresentation.JAVA_LEGACY: "UuidRepresentation.JAVA_LEGACY", + UuidRepresentation.CSHARP_LEGACY: "UuidRepresentation.CSHARP_LEGACY", +} + +MD5_SUBTYPE = 5 +"""BSON binary subtype for an MD5 hash. +""" + +COLUMN_SUBTYPE = 7 +"""BSON binary subtype for columns. + +.. versionadded:: 4.0 +""" + +SENSITIVE_SUBTYPE = 8 +"""BSON binary subtype for sensitive data. + +.. versionadded:: 4.5 +""" + + +VECTOR_SUBTYPE = 9 +"""BSON binary subtype for densely packed vector data. + +.. versionadded:: 4.10 +""" + + +USER_DEFINED_SUBTYPE = 128 +"""BSON binary subtype for any user defined structure. +""" + + +class BinaryVectorDtype(Enum): + """Datatypes of vector subtype. + + :param FLOAT32: (0x27) Pack list of :class:`float` as float32 + :param INT8: (0x03) Pack list of :class:`int` in [-128, 127] as signed int8 + :param PACKED_BIT: (0x10) Pack list of :class:`int` in [0, 255] as unsigned uint8 + + The `PACKED_BIT` value represents a special case where vector values themselves + can only be of two values (0 or 1) but these are packed together into groups of 8, + a byte. In Python, these are displayed as ints in range [0, 255] + + Each value is of type bytes with a length of one. + + .. versionadded:: 4.10 + """ + + INT8 = b"\x03" + FLOAT32 = b"\x27" + PACKED_BIT = b"\x10" + + +class BinaryVector: + """Vector of numbers along with metadata for binary interoperability. + .. versionadded:: 4.10 + """ + + __slots__ = ("data", "dtype", "padding") + + def __init__(self, data: Sequence[float | int], dtype: BinaryVectorDtype, padding: int = 0): + """ + :param data: Sequence of numbers representing the mathematical vector. + :param dtype: The data type stored in binary + :param padding: The number of bits in the final byte that are to be ignored + when a vector element's size is less than a byte + and the length of the vector is not a multiple of 8. + """ + self.data = data + self.dtype = dtype + self.padding = padding + + def __repr__(self) -> str: + return f"BinaryVector(dtype={self.dtype}, padding={self.padding}, data={self.data})" + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, BinaryVector): + return False + return ( + self.dtype == other.dtype and self.padding == other.padding and self.data == other.data + ) + + def __len__(self) -> int: + return len(self.data) + + +class Binary(bytes): + """Representation of BSON binary data. + + We want to represent Python strings as the BSON string type. + We need to wrap binary data so that we can tell + the difference between what should be considered binary data and + what should be considered a string when we encode to BSON. + + Subtype 9 provides a space-efficient representation of 1-dimensional vector data. + Its data is prepended with two bytes of metadata. + The first (dtype) describes its data type, such as float32 or int8. + The second (padding) prescribes the number of bits to ignore in the final byte. + This is relevant when the element size of the dtype is not a multiple of 8. + + Raises TypeError if `subtype` is not an instance of :class:`int`. + Raises ValueError if `subtype` is not in [0, 256). + + .. note:: + Instances of Binary with subtype 0 will be decoded directly to :class:`bytes`. + + :param data: the binary data to represent. Can be any bytes-like type + that implements the buffer protocol. + :param subtype: the `binary subtype + `_ + to use + + .. versionchanged:: 3.9 + Support any bytes-like type that implements the buffer protocol. + + .. versionchanged:: 4.10 + Addition of vector subtype. + """ + + _type_marker = 5 + __subtype: int + + def __new__( + cls: Type[Binary], + data: Union[memoryview, bytes, bytearray, _mmap, _array[Any]], + subtype: int = BINARY_SUBTYPE, + ) -> Binary: + if not isinstance(subtype, int): + raise TypeError(f"subtype must be an instance of int, not {type(subtype)}") + if subtype >= 256 or subtype < 0: + raise ValueError("subtype must be contained in [0, 256)") + # Support any type that implements the buffer protocol. + self = bytes.__new__(cls, memoryview(data).tobytes()) + self.__subtype = subtype + return self + + @classmethod + def from_uuid( + cls: Type[Binary], uuid: UUID, uuid_representation: int = UuidRepresentation.STANDARD + ) -> Binary: + """Create a BSON Binary object from a Python UUID. + + Creates a :class:`~bson.binary.Binary` object from a + :class:`uuid.UUID` instance. Assumes that the native + :class:`uuid.UUID` instance uses the byte-order implied by the + provided ``uuid_representation``. + + Raises :exc:`TypeError` if `uuid` is not an instance of + :class:`~uuid.UUID`. + + :param uuid: A :class:`uuid.UUID` instance. + :param uuid_representation: A member of + :class:`~bson.binary.UuidRepresentation`. Default: + :const:`~bson.binary.UuidRepresentation.STANDARD`. + See `UUID representations `_ for details. + + .. versionadded:: 3.11 + """ + if not isinstance(uuid, UUID): + raise TypeError(f"uuid must be an instance of uuid.UUID, not {type(uuid)}") + + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from bson.binary.UuidRepresentation" + ) + + if uuid_representation == UuidRepresentation.UNSPECIFIED: + raise ValueError( + "cannot encode native uuid.UUID with " + "UuidRepresentation.UNSPECIFIED. UUIDs can be manually " + "converted to bson.Binary instances using " + "bson.Binary.from_uuid() or a different UuidRepresentation " + "can be configured. See the documentation for " + "UuidRepresentation for more information." + ) + + subtype = OLD_UUID_SUBTYPE + if uuid_representation == UuidRepresentation.PYTHON_LEGACY: + payload = uuid.bytes + elif uuid_representation == UuidRepresentation.JAVA_LEGACY: + from_uuid = uuid.bytes + payload = from_uuid[0:8][::-1] + from_uuid[8:16][::-1] + elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: + payload = uuid.bytes_le + else: + # uuid_representation == UuidRepresentation.STANDARD + subtype = UUID_SUBTYPE + payload = uuid.bytes + + return cls(payload, subtype) + + def as_uuid(self, uuid_representation: int = UuidRepresentation.STANDARD) -> UUID: + """Create a Python UUID from this BSON Binary object. + + Decodes this binary object as a native :class:`uuid.UUID` instance + with the provided ``uuid_representation``. + + Raises :exc:`ValueError` if this :class:`~bson.binary.Binary` instance + does not contain a UUID. + + :param uuid_representation: A member of + :class:`~bson.binary.UuidRepresentation`. Default: + :const:`~bson.binary.UuidRepresentation.STANDARD`. + See `UUID representations `_ for details. + + .. versionadded:: 3.11 + """ + if self.subtype not in ALL_UUID_SUBTYPES: + raise ValueError(f"cannot decode subtype {self.subtype} as a uuid") + + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from bson.binary.UuidRepresentation" + ) + + if uuid_representation == UuidRepresentation.UNSPECIFIED: + raise ValueError("uuid_representation cannot be UNSPECIFIED") + elif uuid_representation == UuidRepresentation.PYTHON_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes=self) + elif uuid_representation == UuidRepresentation.JAVA_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes=self[0:8][::-1] + self[8:16][::-1]) + elif uuid_representation == UuidRepresentation.CSHARP_LEGACY: + if self.subtype == OLD_UUID_SUBTYPE: + return UUID(bytes_le=self) + else: + # uuid_representation == UuidRepresentation.STANDARD + if self.subtype == UUID_SUBTYPE: + return UUID(bytes=self) + + raise ValueError( + f"cannot decode subtype {self.subtype} to {UUID_REPRESENTATION_NAMES[uuid_representation]}" + ) + + @classmethod + @overload + def from_vector(cls: Type[Binary], vector: BinaryVector) -> Binary: + ... + + @classmethod + @overload + def from_vector( + cls: Type[Binary], + vector: Union[list[int], list[float]], + dtype: BinaryVectorDtype, + padding: int = 0, + ) -> Binary: + ... + + @classmethod + def from_vector( + cls: Type[Binary], + vector: Union[BinaryVector, list[int], list[float]], + dtype: Optional[BinaryVectorDtype] = None, + padding: Optional[int] = None, + ) -> Binary: + """Create a BSON :class:`~bson.binary.Binary` of Vector subtype. + + To interpret the representation of the numbers, a data type must be included. + See :class:`~bson.binary.BinaryVectorDtype` for available types and descriptions. + + The dtype and padding are prepended to the binary data's value. + + :param vector: Either a List of values, or a :class:`~bson.binary.BinaryVector` dataclass. + :param dtype: Data type of the values + :param padding: For fractional bytes, number of bits to ignore at end of vector. + :return: Binary packed data identified by dtype and padding. + + .. versionchanged:: 4.14 + When padding is non-zero, ignored bits should be zero. Raise exception on encoding, warn on decoding. + + .. versionadded:: 4.10 + """ + if isinstance(vector, BinaryVector): + if dtype or padding: + raise ValueError( + "The first argument, vector, has type BinaryVector. " + "dtype or padding cannot be separately defined, but were." + ) + dtype = vector.dtype + padding = vector.padding + vector = vector.data # type: ignore + + padding = 0 if padding is None else padding + if dtype == BinaryVectorDtype.INT8: # pack ints in [-128, 127] as signed int8 + format_str = "b" + if padding: + raise ValueError(f"padding does not apply to {dtype=}") + elif dtype == BinaryVectorDtype.PACKED_BIT: # pack ints in [0, 255] as unsigned uint8 + format_str = "B" + if 0 <= padding > 7: + raise ValueError(f"{padding=}. It must be in [0,1, ..7].") + if padding and not vector: + raise ValueError("Empty vector with non-zero padding.") + elif dtype == BinaryVectorDtype.FLOAT32: # pack floats as float32 + format_str = "f" + if padding: + raise ValueError(f"padding does not apply to {dtype=}") + else: + raise NotImplementedError("%s not yet supported" % dtype) + + metadata = struct.pack(" BinaryVector: + """From the Binary, create a list of numbers, along with dtype and padding. + + :return: BinaryVector + + .. versionadded:: 4.10 + """ + + if self.subtype != VECTOR_SUBTYPE: + raise ValueError(f"Cannot decode subtype {self.subtype} as a vector") + + position = 0 + dtype, padding = struct.unpack_from(" 7 or padding < 0: + raise ValueError(f"Corrupt data. Padding ({padding}) must be between 0 and 7.") + dtype_format = "B" + format_string = f"<{n_values}{dtype_format}" + unpacked_uint8s = list(struct.unpack_from(format_string, self, position)) + if padding and n_values and unpacked_uint8s[-1] & (1 << padding) - 1 != 0: + warnings.warn( + "Vector has a padding P, but bits in the final byte lower than P are non-zero. For pymongo>=5.0, they must be zero.", + DeprecationWarning, + stacklevel=2, + ) + return BinaryVector(unpacked_uint8s, dtype, padding) + + else: + raise NotImplementedError("Binary Vector dtype %s not yet supported" % dtype.name) + + @property + def subtype(self) -> int: + """Subtype of this binary data.""" + return self.__subtype + + def __getnewargs__(self) -> Tuple[bytes, int]: # type: ignore[override] + # Work around http://bugs.python.org/issue7382 + data = super().__getnewargs__()[0] + if not isinstance(data, bytes): + data = data.encode("latin-1") + return data, self.__subtype + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Binary): + return (self.__subtype, bytes(self)) == (other.subtype, bytes(other)) + # We don't return NotImplemented here because if we did then + # Binary("foo") == "foo" would return True, since Binary is a + # subclass of str... + return False + + def __hash__(self) -> int: + return super().__hash__() ^ hash(self.__subtype) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __repr__(self) -> str: + if self.__subtype == SENSITIVE_SUBTYPE: + return f"" + else: + return f"Binary({bytes.__repr__(self)}, {self.__subtype})" diff --git a/.venv/lib/python3.12/site-packages/bson/bson-endian.h b/.venv/lib/python3.12/site-packages/bson/bson-endian.h new file mode 100644 index 0000000..e906b07 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/bson-endian.h @@ -0,0 +1,233 @@ +/* + * Copyright 2013-2016 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef BSON_ENDIAN_H +#define BSON_ENDIAN_H + + +#if defined(__sun) +# include +#endif + + +#ifdef _MSC_VER +# define BSON_INLINE __inline +#else +# include +# define BSON_INLINE __inline__ +#endif + + +#define BSON_BIG_ENDIAN 4321 +#define BSON_LITTLE_ENDIAN 1234 + + +/* WORDS_BIGENDIAN from pyconfig.h / Python.h */ +#ifdef WORDS_BIGENDIAN +# define BSON_BYTE_ORDER BSON_BIG_ENDIAN +#else +# define BSON_BYTE_ORDER BSON_LITTLE_ENDIAN +#endif + + +#if defined(__sun) +# define BSON_UINT16_SWAP_LE_BE(v) BSWAP_16((uint16_t)v) +# define BSON_UINT32_SWAP_LE_BE(v) BSWAP_32((uint32_t)v) +# define BSON_UINT64_SWAP_LE_BE(v) BSWAP_64((uint64_t)v) +#elif defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) && \ + (__clang_major__ >= 3) && (__clang_minor__ >= 1) +# if __has_builtin(__builtin_bswap16) +# define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16(v) +# endif +# if __has_builtin(__builtin_bswap32) +# define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32(v) +# endif +# if __has_builtin(__builtin_bswap64) +# define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64(v) +# endif +#elif defined(__GNUC__) && (__GNUC__ >= 4) +# if __GNUC__ >= 4 && defined (__GNUC_MINOR__) && __GNUC_MINOR__ >= 3 +# define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32 ((uint32_t)v) +# define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64 ((uint64_t)v) +# endif +# if __GNUC__ >= 4 && defined (__GNUC_MINOR__) && __GNUC_MINOR__ >= 8 +# define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16 ((uint32_t)v) +# endif +#endif + + +#ifndef BSON_UINT16_SWAP_LE_BE +# define BSON_UINT16_SWAP_LE_BE(v) __bson_uint16_swap_slow ((uint16_t)v) +#endif + + +#ifndef BSON_UINT32_SWAP_LE_BE +# define BSON_UINT32_SWAP_LE_BE(v) __bson_uint32_swap_slow ((uint32_t)v) +#endif + + +#ifndef BSON_UINT64_SWAP_LE_BE +# define BSON_UINT64_SWAP_LE_BE(v) __bson_uint64_swap_slow ((uint64_t)v) +#endif + + +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN +# define BSON_UINT16_FROM_LE(v) ((uint16_t)v) +# define BSON_UINT16_TO_LE(v) ((uint16_t)v) +# define BSON_UINT16_FROM_BE(v) BSON_UINT16_SWAP_LE_BE (v) +# define BSON_UINT16_TO_BE(v) BSON_UINT16_SWAP_LE_BE (v) +# define BSON_UINT32_FROM_LE(v) ((uint32_t)v) +# define BSON_UINT32_TO_LE(v) ((uint32_t)v) +# define BSON_UINT32_FROM_BE(v) BSON_UINT32_SWAP_LE_BE (v) +# define BSON_UINT32_TO_BE(v) BSON_UINT32_SWAP_LE_BE (v) +# define BSON_UINT64_FROM_LE(v) ((uint64_t)v) +# define BSON_UINT64_TO_LE(v) ((uint64_t)v) +# define BSON_UINT64_FROM_BE(v) BSON_UINT64_SWAP_LE_BE (v) +# define BSON_UINT64_TO_BE(v) BSON_UINT64_SWAP_LE_BE (v) +# define BSON_DOUBLE_FROM_LE(v) ((double)v) +# define BSON_DOUBLE_TO_LE(v) ((double)v) +#elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN +# define BSON_UINT16_FROM_LE(v) BSON_UINT16_SWAP_LE_BE (v) +# define BSON_UINT16_TO_LE(v) BSON_UINT16_SWAP_LE_BE (v) +# define BSON_UINT16_FROM_BE(v) ((uint16_t)v) +# define BSON_UINT16_TO_BE(v) ((uint16_t)v) +# define BSON_UINT32_FROM_LE(v) BSON_UINT32_SWAP_LE_BE (v) +# define BSON_UINT32_TO_LE(v) BSON_UINT32_SWAP_LE_BE (v) +# define BSON_UINT32_FROM_BE(v) ((uint32_t)v) +# define BSON_UINT32_TO_BE(v) ((uint32_t)v) +# define BSON_UINT64_FROM_LE(v) BSON_UINT64_SWAP_LE_BE (v) +# define BSON_UINT64_TO_LE(v) BSON_UINT64_SWAP_LE_BE (v) +# define BSON_UINT64_FROM_BE(v) ((uint64_t)v) +# define BSON_UINT64_TO_BE(v) ((uint64_t)v) +# define BSON_DOUBLE_FROM_LE(v) (__bson_double_swap_slow (v)) +# define BSON_DOUBLE_TO_LE(v) (__bson_double_swap_slow (v)) +#else +# error "The endianness of target architecture is unknown." +#endif + + +/* + *-------------------------------------------------------------------------- + * + * __bson_uint16_swap_slow -- + * + * Fallback endianness conversion for 16-bit integers. + * + * Returns: + * The endian swapped version. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +static BSON_INLINE uint16_t +__bson_uint16_swap_slow (uint16_t v) /* IN */ +{ + return ((v & 0x00FF) << 8) | + ((v & 0xFF00) >> 8); +} + + +/* + *-------------------------------------------------------------------------- + * + * __bson_uint32_swap_slow -- + * + * Fallback endianness conversion for 32-bit integers. + * + * Returns: + * The endian swapped version. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +static BSON_INLINE uint32_t +__bson_uint32_swap_slow (uint32_t v) /* IN */ +{ + return ((v & 0x000000FFU) << 24) | + ((v & 0x0000FF00U) << 8) | + ((v & 0x00FF0000U) >> 8) | + ((v & 0xFF000000U) >> 24); +} + + +/* + *-------------------------------------------------------------------------- + * + * __bson_uint64_swap_slow -- + * + * Fallback endianness conversion for 64-bit integers. + * + * Returns: + * The endian swapped version. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +static BSON_INLINE uint64_t +__bson_uint64_swap_slow (uint64_t v) /* IN */ +{ + return ((v & 0x00000000000000FFULL) << 56) | + ((v & 0x000000000000FF00ULL) << 40) | + ((v & 0x0000000000FF0000ULL) << 24) | + ((v & 0x00000000FF000000ULL) << 8) | + ((v & 0x000000FF00000000ULL) >> 8) | + ((v & 0x0000FF0000000000ULL) >> 24) | + ((v & 0x00FF000000000000ULL) >> 40) | + ((v & 0xFF00000000000000ULL) >> 56); +} + + +/* + *-------------------------------------------------------------------------- + * + * __bson_double_swap_slow -- + * + * Fallback endianness conversion for double floating point. + * + * Returns: + * The endian swapped version. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + + +static BSON_INLINE double +__bson_double_swap_slow (double v) /* IN */ +{ + uint64_t uv; + + memcpy(&uv, &v, sizeof(v)); + uv = BSON_UINT64_SWAP_LE_BE(uv); + memcpy(&v, &uv, sizeof(v)); + + return v; +} + + +#endif /* BSON_ENDIAN_H */ diff --git a/.venv/lib/python3.12/site-packages/bson/buffer.c b/.venv/lib/python3.12/site-packages/bson/buffer.c new file mode 100644 index 0000000..cc75202 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/buffer.c @@ -0,0 +1,157 @@ +/* + * Copyright 2009-2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Include Python.h so we can set Python's error indicator. */ +#define PY_SSIZE_T_CLEAN +#include "Python.h" + +#include +#include + +#include "buffer.h" + +#define INITIAL_BUFFER_SIZE 256 + +struct buffer { + char* buffer; + int size; + int position; +}; + +/* Set Python's error indicator to MemoryError. + * Called after allocation failures. */ +static void set_memory_error(void) { + PyErr_NoMemory(); +} + +/* Allocate and return a new buffer. + * Return NULL and sets MemoryError on allocation failure. */ +buffer_t pymongo_buffer_new(void) { + buffer_t buffer; + buffer = (buffer_t)malloc(sizeof(struct buffer)); + if (buffer == NULL) { + set_memory_error(); + return NULL; + } + + buffer->size = INITIAL_BUFFER_SIZE; + buffer->position = 0; + buffer->buffer = (char*)malloc(sizeof(char) * INITIAL_BUFFER_SIZE); + if (buffer->buffer == NULL) { + free(buffer); + set_memory_error(); + return NULL; + } + + return buffer; +} + +/* Free the memory allocated for `buffer`. + * Return non-zero on failure. */ +int pymongo_buffer_free(buffer_t buffer) { + if (buffer == NULL) { + return 1; + } + /* Buffer will be NULL when buffer_grow fails. */ + if (buffer->buffer != NULL) { + free(buffer->buffer); + } + free(buffer); + return 0; +} + +/* Grow `buffer` to at least `min_length`. + * Return non-zero and sets MemoryError on allocation failure. */ +static int buffer_grow(buffer_t buffer, int min_length) { + int old_size = 0; + int size = buffer->size; + char* old_buffer = buffer->buffer; + if (size >= min_length) { + return 0; + } + while (size < min_length) { + old_size = size; + size *= 2; + if (size <= old_size) { + /* Size did not increase. Could be an overflow + * or size < 1. Just go with min_length. */ + size = min_length; + } + } + buffer->buffer = (char*)realloc(buffer->buffer, sizeof(char) * size); + if (buffer->buffer == NULL) { + free(old_buffer); + set_memory_error(); + return 1; + } + buffer->size = size; + return 0; +} + +/* Assure that `buffer` has at least `size` free bytes (and grow if needed). + * Return non-zero and sets MemoryError on allocation failure. + * Return non-zero and sets ValueError if `size` would exceed 2GiB. */ +static int buffer_assure_space(buffer_t buffer, int size) { + int new_size = buffer->position + size; + /* Check for overflow. */ + if (new_size < buffer->position) { + PyErr_SetString(PyExc_ValueError, + "Document would overflow BSON size limit"); + return 1; + } + + if (new_size <= buffer->size) { + return 0; + } + return buffer_grow(buffer, new_size); +} + +/* Save `size` bytes from the current position in `buffer` (and grow if needed). + * Return offset for writing, or -1 on failure. + * Sets MemoryError or ValueError on failure. */ +buffer_position pymongo_buffer_save_space(buffer_t buffer, int size) { + int position = buffer->position; + if (buffer_assure_space(buffer, size) != 0) { + return -1; + } + buffer->position += size; + return position; +} + +/* Write `size` bytes from `data` to `buffer` (and grow if needed). + * Return non-zero on failure. + * Sets MemoryError or ValueError on failure. */ +int pymongo_buffer_write(buffer_t buffer, const char* data, int size) { + if (buffer_assure_space(buffer, size) != 0) { + return 1; + } + + memcpy(buffer->buffer + buffer->position, data, size); + buffer->position += size; + return 0; +} + +int pymongo_buffer_get_position(buffer_t buffer) { + return buffer->position; +} + +char* pymongo_buffer_get_buffer(buffer_t buffer) { + return buffer->buffer; +} + +void pymongo_buffer_update_position(buffer_t buffer, buffer_position new_position) { + buffer->position = new_position; +} diff --git a/.venv/lib/python3.12/site-packages/bson/buffer.h b/.venv/lib/python3.12/site-packages/bson/buffer.h new file mode 100644 index 0000000..a78e34e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/buffer.h @@ -0,0 +1,51 @@ +/* + * Copyright 2009-2015 MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BUFFER_H +#define BUFFER_H + +/* Note: if any of these functions return a failure condition then the buffer + * has already been freed. */ + +/* A buffer */ +typedef struct buffer* buffer_t; +/* A position in the buffer */ +typedef int buffer_position; + +/* Allocate and return a new buffer. + * Return NULL on allocation failure. */ +buffer_t pymongo_buffer_new(void); + +/* Free the memory allocated for `buffer`. + * Return non-zero on failure. */ +int pymongo_buffer_free(buffer_t buffer); + +/* Save `size` bytes from the current position in `buffer` (and grow if needed). + * Return offset for writing, or -1 on allocation failure. */ +buffer_position pymongo_buffer_save_space(buffer_t buffer, int size); + +/* Write `size` bytes from `data` to `buffer` (and grow if needed). + * Return non-zero on allocation failure. */ +int pymongo_buffer_write(buffer_t buffer, const char* data, int size); + +/* Getters for the internals of a buffer_t. + * Should try to avoid using these as much as possible + * since they break the abstraction. */ +buffer_position pymongo_buffer_get_position(buffer_t buffer); +char* pymongo_buffer_get_buffer(buffer_t buffer); +void pymongo_buffer_update_position(buffer_t buffer, buffer_position new_position); + +#endif diff --git a/.venv/lib/python3.12/site-packages/bson/code.py b/.venv/lib/python3.12/site-packages/bson/code.py new file mode 100644 index 0000000..f0523b2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/code.py @@ -0,0 +1,100 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing JavaScript code in BSON.""" +from __future__ import annotations + +from collections.abc import Mapping as _Mapping +from typing import Any, Mapping, Optional, Type, Union + + +class Code(str): + """BSON's JavaScript code type. + + Raises :class:`TypeError` if `code` is not an instance of + :class:`str` or `scope` is not ``None`` or an instance + of :class:`dict`. + + Scope variables can be set by passing a dictionary as the `scope` + argument or by using keyword arguments. If a variable is set as a + keyword argument it will override any setting for that variable in + the `scope` dictionary. + + :param code: A string containing JavaScript code to be evaluated or another + instance of Code. In the latter case, the scope of `code` becomes this + Code's :attr:`scope`. + :param scope: dictionary representing the scope in which + `code` should be evaluated - a mapping from identifiers (as + strings) to values. Defaults to ``None``. This is applied after any + scope associated with a given `code` above. + :param kwargs: scope variables can also be passed as + keyword arguments. These are applied after `scope` and `code`. + + .. versionchanged:: 3.4 + The default value for :attr:`scope` is ``None`` instead of ``{}``. + + """ + + _type_marker = 13 + __scope: Union[Mapping[str, Any], None] + + def __new__( + cls: Type[Code], + code: Union[str, Code], + scope: Optional[Mapping[str, Any]] = None, + **kwargs: Any, + ) -> Code: + if not isinstance(code, str): + raise TypeError(f"code must be an instance of str, not {type(code)}") + + self = str.__new__(cls, code) + + try: + self.__scope = code.scope # type: ignore + except AttributeError: + self.__scope = None + + if scope is not None: + if not isinstance(scope, _Mapping): + raise TypeError(f"scope must be an instance of dict, not {type(scope)}") + if self.__scope is not None: + self.__scope.update(scope) # type: ignore + else: + self.__scope = scope + + if kwargs: + if self.__scope is not None: + self.__scope.update(kwargs) # type: ignore + else: + self.__scope = kwargs + + return self + + @property + def scope(self) -> Optional[Mapping[str, Any]]: + """Scope dictionary for this instance or ``None``.""" + return self.__scope + + def __repr__(self) -> str: + return f"Code({str.__repr__(self)}, {self.__scope!r})" + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Code): + return (self.__scope, str(self)) == (other.__scope, str(other)) + return False + + __hash__: Any = None + + def __ne__(self, other: Any) -> bool: + return not self == other diff --git a/.venv/lib/python3.12/site-packages/bson/codec_options.py b/.venv/lib/python3.12/site-packages/bson/codec_options.py new file mode 100644 index 0000000..add5416 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/codec_options.py @@ -0,0 +1,521 @@ +# Copyright 2014-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for specifying BSON codec options.""" +from __future__ import annotations + +import abc +import datetime +import enum +from collections.abc import MutableMapping as _MutableMapping +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Generic, + Iterable, + Mapping, + NamedTuple, + Optional, + Tuple, + Type, + Union, + cast, +) + +from bson.binary import ( + ALL_UUID_REPRESENTATIONS, + UUID_REPRESENTATION_NAMES, + UuidRepresentation, +) +from bson.typings import _DocumentType + +_RAW_BSON_DOCUMENT_MARKER = 101 + + +def _raw_document_class(document_class: Any) -> bool: + """Determine if a document_class is a RawBSONDocument class.""" + marker = getattr(document_class, "_type_marker", None) + return marker == _RAW_BSON_DOCUMENT_MARKER + + +class TypeEncoder(abc.ABC): + """Base class for defining type codec classes which describe how a + custom type can be transformed to one of the types BSON understands. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding. + + See `encode data with type codecs `_ documentation for an example. + """ + + @abc.abstractproperty + def python_type(self) -> Any: + """The Python type to be converted into something serializable.""" + + @abc.abstractmethod + def transform_python(self, value: Any) -> Any: + """Convert the given Python object into something serializable.""" + + +class TypeDecoder(abc.ABC): + """Base class for defining type codec classes which describe how a + BSON type can be transformed to a custom type. + + Codec classes must implement the ``bson_type`` attribute, and the + ``transform_bson`` method to support decoding. + + See `encode data with type codecs `_ documentation for an example. + """ + + @abc.abstractproperty + def bson_type(self) -> Any: + """The BSON type to be converted into our own type.""" + + @abc.abstractmethod + def transform_bson(self, value: Any) -> Any: + """Convert the given BSON value into our own type.""" + + +class TypeCodec(TypeEncoder, TypeDecoder): + """Base class for defining type codec classes which describe how a + custom type can be transformed to/from one of the types :mod:`bson` + can already encode/decode. + + Codec classes must implement the ``python_type`` attribute, and the + ``transform_python`` method to support encoding, as well as the + ``bson_type`` attribute, and the ``transform_bson`` method to support + decoding. + + See `encode data with type codecs `_ documentation for an example. + """ + + +_Codec = Union[TypeEncoder, TypeDecoder, TypeCodec] +_Fallback = Callable[[Any], Any] + + +class TypeRegistry: + """Encapsulates type codecs used in encoding and / or decoding BSON, as + well as the fallback encoder. Type registries cannot be modified after + instantiation. + + ``TypeRegistry`` can be initialized with an iterable of type codecs, and + a callable for the fallback encoder:: + + >>> from bson.codec_options import TypeRegistry + >>> type_registry = TypeRegistry([Codec1, Codec2, Codec3, ...], + ... fallback_encoder) + + See `add codec to the type registry `_ documentation for an example. + + :param type_codecs: iterable of type codec instances. If + ``type_codecs`` contains multiple codecs that transform a single + python or BSON type, the transformation specified by the type codec + occurring last prevails. A TypeError will be raised if one or more + type codecs modify the encoding behavior of a built-in :mod:`bson` + type. + :param fallback_encoder: callable that accepts a single, + unencodable python value and transforms it into a type that + :mod:`bson` can encode. See `define a fallback encoder `_ + documentation for an example. + """ + + def __init__( + self, + type_codecs: Optional[Iterable[_Codec]] = None, + fallback_encoder: Optional[_Fallback] = None, + ) -> None: + self.__type_codecs = list(type_codecs or []) + self._fallback_encoder = fallback_encoder + self._encoder_map: dict[Any, Any] = {} + self._decoder_map: dict[Any, Any] = {} + + if self._fallback_encoder is not None: + if not callable(fallback_encoder): + raise TypeError("fallback_encoder %r is not a callable" % (fallback_encoder)) + + for codec in self.__type_codecs: + is_valid_codec = False + if isinstance(codec, TypeEncoder): + self._validate_type_encoder(codec) + is_valid_codec = True + self._encoder_map[codec.python_type] = codec.transform_python + if isinstance(codec, TypeDecoder): + is_valid_codec = True + self._decoder_map[codec.bson_type] = codec.transform_bson + if not is_valid_codec: + raise TypeError( + f"Expected an instance of {TypeEncoder.__name__}, {TypeDecoder.__name__}, or {TypeCodec.__name__}, got {codec!r} instead" + ) + + @property + def codecs(self) -> list[TypeEncoder | TypeDecoder | TypeCodec]: + """The list of type codecs in this registry.""" + return self.__type_codecs + + @property + def fallback_encoder(self) -> Optional[_Fallback]: + """The fallback encoder in this registry.""" + return self._fallback_encoder + + def _validate_type_encoder(self, codec: _Codec) -> None: + from bson import _BUILT_IN_TYPES + + for pytype in _BUILT_IN_TYPES: + if issubclass(cast(TypeCodec, codec).python_type, pytype): + err_msg = ( + "TypeEncoders cannot change how built-in types are " + f"encoded (encoder {codec} transforms type {pytype})" + ) + raise TypeError(err_msg) + + def __repr__(self) -> str: + return "{}(type_codecs={!r}, fallback_encoder={!r})".format( + self.__class__.__name__, + self.__type_codecs, + self._fallback_encoder, + ) + + def __eq__(self, other: Any) -> Any: + if not isinstance(other, type(self)): + return NotImplemented + return ( + (self._decoder_map == other._decoder_map) + and (self._encoder_map == other._encoder_map) + and (self._fallback_encoder == other._fallback_encoder) + ) + + +class DatetimeConversion(int, enum.Enum): + """Options for decoding BSON datetimes.""" + + DATETIME = 1 + """Decode a BSON UTC datetime as a :class:`datetime.datetime`. + + BSON UTC datetimes that cannot be represented as a + :class:`~datetime.datetime` will raise an :class:`OverflowError` + or a :class:`ValueError`. + + .. versionadded 4.3 + """ + + DATETIME_CLAMP = 2 + """Decode a BSON UTC datetime as a :class:`datetime.datetime`, clamping + to :attr:`~datetime.datetime.min` and :attr:`~datetime.datetime.max`. + + .. versionadded 4.3 + """ + + DATETIME_MS = 3 + """Decode a BSON UTC datetime as a :class:`~bson.datetime_ms.DatetimeMS` + object. + + .. versionadded 4.3 + """ + + DATETIME_AUTO = 4 + """Decode a BSON UTC datetime as a :class:`datetime.datetime` if possible, + and a :class:`~bson.datetime_ms.DatetimeMS` if not. + + .. versionadded 4.3 + """ + + +class _BaseCodecOptions(NamedTuple): + document_class: Type[Mapping[str, Any]] + tz_aware: bool + uuid_representation: int + unicode_decode_error_handler: str + tzinfo: Optional[datetime.tzinfo] + type_registry: TypeRegistry + datetime_conversion: Optional[DatetimeConversion] + + +if TYPE_CHECKING: + + class CodecOptions(Tuple[_DocumentType], Generic[_DocumentType]): + document_class: Type[_DocumentType] + tz_aware: bool + uuid_representation: int + unicode_decode_error_handler: Optional[str] + tzinfo: Optional[datetime.tzinfo] + type_registry: TypeRegistry + datetime_conversion: Optional[int] + + def __new__( + cls: Type[CodecOptions[_DocumentType]], + document_class: Optional[Type[_DocumentType]] = ..., + tz_aware: bool = ..., + uuid_representation: Optional[int] = ..., + unicode_decode_error_handler: Optional[str] = ..., + tzinfo: Optional[datetime.tzinfo] = ..., + type_registry: Optional[TypeRegistry] = ..., + datetime_conversion: Optional[int] = ..., + ) -> CodecOptions[_DocumentType]: + ... + + # CodecOptions API + def with_options(self, **kwargs: Any) -> CodecOptions[Any]: + ... + + def _arguments_repr(self) -> str: + ... + + def _options_dict(self) -> dict[Any, Any]: + ... + + # NamedTuple API + @classmethod + def _make(cls, obj: Iterable[Any]) -> CodecOptions[_DocumentType]: + ... + + def _asdict(self) -> dict[str, Any]: + ... + + def _replace(self, **kwargs: Any) -> CodecOptions[_DocumentType]: + ... + + _source: str + _fields: Tuple[str] + +else: + + class CodecOptions(_BaseCodecOptions): + """Encapsulates options used encoding and / or decoding BSON.""" + + def __init__(self, *args, **kwargs): + """Encapsulates options used encoding and / or decoding BSON. + + The `document_class` option is used to define a custom type for use + decoding BSON documents. Access to the underlying raw BSON bytes for + a document is available using the :class:`~bson.raw_bson.RawBSONDocument` + type:: + + >>> from bson.raw_bson import RawBSONDocument + >>> from bson.codec_options import CodecOptions + >>> codec_options = CodecOptions(document_class=RawBSONDocument) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc.raw + '\\x16\\x00\\x00\\x00\\x07_id\\x00[0\\x165\\x91\\x10\\xea\\x14\\xe8\\xc5\\x8b\\x93\\x00' + + The document class can be any type that inherits from + :class:`~collections.abc.MutableMapping`:: + + >>> class AttributeDict(dict): + ... # A dict that supports attribute access. + ... def __getattr__(self, key): + ... return self[key] + ... def __setattr__(self, key, value): + ... self[key] = value + ... + >>> codec_options = CodecOptions(document_class=AttributeDict) + >>> coll = db.get_collection('test', codec_options=codec_options) + >>> doc = coll.find_one() + >>> doc._id + ObjectId('5b3016359110ea14e8c58b93') + + See `Dates and Times `_ for examples using the `tz_aware` and + `tzinfo` options. + + See `UUID `_ for examples using the `uuid_representation` + option. + + :param document_class: BSON documents returned in queries will be decoded + to an instance of this class. Must be a subclass of + :class:`~collections.abc.MutableMapping`. Defaults to :class:`dict`. + :param tz_aware: If ``True``, BSON datetimes will be decoded to timezone + aware instances of :class:`~datetime.datetime`. Otherwise they will be + naive. Defaults to ``False``. + :param uuid_representation: The BSON representation to use when encoding + and decoding instances of :class:`~uuid.UUID`. Defaults to + :data:`~bson.binary.UuidRepresentation.UNSPECIFIED`. New + applications should consider setting this to + :data:`~bson.binary.UuidRepresentation.STANDARD` for cross language + compatibility. See `UUID representations `_ for details. + :param unicode_decode_error_handler: The error handler to apply when + a Unicode-related error occurs during BSON decoding that would + otherwise raise :exc:`UnicodeDecodeError`. Valid options include + 'strict', 'replace', 'backslashreplace', 'surrogateescape', and + 'ignore'. Defaults to 'strict'. + :param tzinfo: A :class:`~datetime.tzinfo` subclass that specifies the + timezone to/from which :class:`~datetime.datetime` objects should be + encoded/decoded. + :param type_registry: Instance of :class:`TypeRegistry` used to customize + encoding and decoding behavior. + :param datetime_conversion: Specifies how UTC datetimes should be decoded + within BSON. Valid options include 'datetime_ms' to return as a + DatetimeMS, 'datetime' to return as a datetime.datetime and + raising a ValueError for out-of-range values, 'datetime_auto' to + return DatetimeMS objects when the underlying datetime is + out-of-range and 'datetime_clamp' to clamp to the minimum and + maximum possible datetimes. Defaults to 'datetime'. + + .. versionchanged:: 4.0 + The default for `uuid_representation` was changed from + :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to + :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + + .. versionadded:: 3.8 + `type_registry` attribute. + + .. warning:: Care must be taken when changing + `unicode_decode_error_handler` from its default value ('strict'). + The 'replace' and 'ignore' modes should not be used when documents + retrieved from the server will be modified in the client application + and stored back to the server. + """ + super().__init__() + + def __new__( + cls: Type[CodecOptions], + document_class: Optional[Type[Mapping[str, Any]]] = None, + tz_aware: bool = False, + uuid_representation: Optional[int] = UuidRepresentation.UNSPECIFIED, + unicode_decode_error_handler: str = "strict", + tzinfo: Optional[datetime.tzinfo] = None, + type_registry: Optional[TypeRegistry] = None, + datetime_conversion: Optional[DatetimeConversion] = DatetimeConversion.DATETIME, + ) -> CodecOptions: + doc_class = document_class or dict + # issubclass can raise TypeError for generic aliases like SON[str, Any]. + # In that case we can use the base class for the comparison. + is_mapping = False + try: + is_mapping = issubclass(doc_class, _MutableMapping) + except TypeError: + if hasattr(doc_class, "__origin__"): + is_mapping = issubclass(doc_class.__origin__, _MutableMapping) + if not (is_mapping or _raw_document_class(doc_class)): + raise TypeError( + "document_class must be dict, bson.son.SON, " + "bson.raw_bson.RawBSONDocument, or a " + "subclass of collections.abc.MutableMapping" + ) + if not isinstance(tz_aware, bool): + raise TypeError(f"tz_aware must be True or False, was: tz_aware={tz_aware}") + if uuid_representation not in ALL_UUID_REPRESENTATIONS: + raise ValueError( + "uuid_representation must be a value from bson.binary.UuidRepresentation" + ) + if not isinstance(unicode_decode_error_handler, str): + raise ValueError( + f"unicode_decode_error_handler must be a string, not {type(unicode_decode_error_handler)}" + ) + if tzinfo is not None: + if not isinstance(tzinfo, datetime.tzinfo): + raise TypeError( + f"tzinfo must be an instance of datetime.tzinfo, not {type(tzinfo)}" + ) + if not tz_aware: + raise ValueError("cannot specify tzinfo without also setting tz_aware=True") + + type_registry = type_registry or TypeRegistry() + + if not isinstance(type_registry, TypeRegistry): + raise TypeError( + f"type_registry must be an instance of TypeRegistry, not {type(type_registry)}" + ) + + return tuple.__new__( + cls, + ( + doc_class, + tz_aware, + uuid_representation, + unicode_decode_error_handler, + tzinfo, + type_registry, + datetime_conversion, + ), + ) + + def _arguments_repr(self) -> str: + """Representation of the arguments used to create this object.""" + document_class_repr = ( + "dict" if self.document_class is dict else repr(self.document_class) + ) + + uuid_rep_repr = UUID_REPRESENTATION_NAMES.get( + self.uuid_representation, self.uuid_representation + ) + + return ( + "document_class={}, tz_aware={!r}, uuid_representation={}, " + "unicode_decode_error_handler={!r}, tzinfo={!r}, " + "type_registry={!r}, datetime_conversion={!s}".format( + document_class_repr, + self.tz_aware, + uuid_rep_repr, + self.unicode_decode_error_handler, + self.tzinfo, + self.type_registry, + self.datetime_conversion, + ) + ) + + def _options_dict(self) -> dict[str, Any]: + """Dictionary of the arguments used to create this object.""" + # TODO: PYTHON-2442 use _asdict() instead + return { + "document_class": self.document_class, + "tz_aware": self.tz_aware, + "uuid_representation": self.uuid_representation, + "unicode_decode_error_handler": self.unicode_decode_error_handler, + "tzinfo": self.tzinfo, + "type_registry": self.type_registry, + "datetime_conversion": self.datetime_conversion, + } + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._arguments_repr()})" + + def with_options(self, **kwargs: Any) -> CodecOptions: + """Make a copy of this CodecOptions, overriding some options:: + + >>> from bson.codec_options import DEFAULT_CODEC_OPTIONS + >>> DEFAULT_CODEC_OPTIONS.tz_aware + False + >>> options = DEFAULT_CODEC_OPTIONS.with_options(tz_aware=True) + >>> options.tz_aware + True + + .. versionadded:: 3.5 + """ + opts = self._options_dict() + opts.update(kwargs) + return CodecOptions(**opts) + + +DEFAULT_CODEC_OPTIONS: CodecOptions[dict[str, Any]] = CodecOptions() + + +def _parse_codec_options(options: Any) -> CodecOptions[Any]: + """Parse BSON codec options.""" + kwargs = {} + for k in set(options) & { + "document_class", + "tz_aware", + "uuidrepresentation", + "unicode_decode_error_handler", + "tzinfo", + "type_registry", + "datetime_conversion", + }: + if k == "uuidrepresentation": + kwargs["uuid_representation"] = options[k] + else: + kwargs[k] = options[k] + return CodecOptions(**kwargs) diff --git a/.venv/lib/python3.12/site-packages/bson/datetime_ms.py b/.venv/lib/python3.12/site-packages/bson/datetime_ms.py new file mode 100644 index 0000000..2047bd3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/datetime_ms.py @@ -0,0 +1,182 @@ +# Copyright 2022-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You +# may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""Tools for representing the BSON datetime type. + +.. versionadded:: 4.3 +""" +from __future__ import annotations + +import calendar +import datetime +from typing import Any, Union, cast + +from bson.codec_options import DEFAULT_CODEC_OPTIONS, CodecOptions, DatetimeConversion +from bson.errors import InvalidBSON +from bson.tz_util import utc + +EPOCH_AWARE = datetime.datetime.fromtimestamp(0, utc) +EPOCH_NAIVE = EPOCH_AWARE.replace(tzinfo=None) +_DATETIME_ERROR_SUGGESTION = ( + "(Consider Using CodecOptions(datetime_conversion=DATETIME_AUTO)" + " or MongoClient(datetime_conversion='DATETIME_AUTO'))." + " See: https://www.mongodb.com/docs/languages/python/pymongo-driver/current/data-formats/dates-and-times/#handling-out-of-range-datetimes" +) + + +class DatetimeMS: + """Represents a BSON UTC datetime.""" + + __slots__ = ("_value",) + + def __init__(self, value: Union[int, datetime.datetime]): + """Represents a BSON UTC datetime. + + BSON UTC datetimes are defined as an int64 of milliseconds since the + Unix epoch. The principal use of DatetimeMS is to represent + datetimes outside the range of the Python builtin + :class:`~datetime.datetime` class when + encoding/decoding BSON. + + To decode UTC datetimes as a ``DatetimeMS``, `datetime_conversion` in + :class:`~bson.codec_options.CodecOptions` must be set to 'datetime_ms' or + 'datetime_auto'. See `handling out of range datetimes `_ for + details. + + :param value: An instance of :class:`datetime.datetime` to be + represented as milliseconds since the Unix epoch, or int of + milliseconds since the Unix epoch. + """ + if isinstance(value, int): + if not (-(2**63) <= value <= 2**63 - 1): + raise OverflowError("Must be a 64-bit integer of milliseconds") + self._value = value + elif isinstance(value, datetime.datetime): + self._value = _datetime_to_millis(value) + else: + raise TypeError(f"{type(value)} is not a valid type for DatetimeMS") + + def __hash__(self) -> int: + return hash(self._value) + + def __repr__(self) -> str: + return type(self).__name__ + "(" + str(self._value) + ")" + + def __lt__(self, other: Union[DatetimeMS, int]) -> bool: + return self._value < other + + def __le__(self, other: Union[DatetimeMS, int]) -> bool: + return self._value <= other + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DatetimeMS): + return self._value == other._value + return False + + def __ne__(self, other: Any) -> bool: + if isinstance(other, DatetimeMS): + return self._value != other._value + return True + + def __gt__(self, other: Union[DatetimeMS, int]) -> bool: + return self._value > other + + def __ge__(self, other: Union[DatetimeMS, int]) -> bool: + return self._value >= other + + _type_marker = 9 + + def as_datetime( + self, codec_options: CodecOptions[Any] = DEFAULT_CODEC_OPTIONS + ) -> datetime.datetime: + """Create a Python :class:`~datetime.datetime` from this DatetimeMS object. + + :param codec_options: A CodecOptions instance for specifying how the + resulting DatetimeMS object will be formatted using ``tz_aware`` + and ``tz_info``. Defaults to + :const:`~bson.codec_options.DEFAULT_CODEC_OPTIONS`. + """ + return cast(datetime.datetime, _millis_to_datetime(self._value, codec_options)) + + def __int__(self) -> int: + return self._value + + +def _datetime_to_millis(dtm: datetime.datetime) -> int: + """Convert datetime to milliseconds since epoch UTC.""" + if dtm.utcoffset() is not None: + dtm = dtm - dtm.utcoffset() # type: ignore + return int(calendar.timegm(dtm.timetuple()) * 1000 + dtm.microsecond // 1000) + + +_MIN_UTC = datetime.datetime.min.replace(tzinfo=utc) +_MAX_UTC = datetime.datetime.max.replace(tzinfo=utc) +_MIN_UTC_MS = _datetime_to_millis(_MIN_UTC) +_MAX_UTC_MS = _datetime_to_millis(_MAX_UTC) + + +# Inclusive min and max for timezones. +def _min_datetime_ms(tz: datetime.tzinfo = utc) -> int: + delta = tz.utcoffset(_MIN_UTC) + if delta is not None: + offset_millis = (delta.days * 86400 + delta.seconds) * 1000 + delta.microseconds // 1000 + else: + offset_millis = 0 + return max(_MIN_UTC_MS, _MIN_UTC_MS - offset_millis) + + +def _max_datetime_ms(tz: datetime.tzinfo = utc) -> int: + delta = tz.utcoffset(_MAX_UTC) + if delta is not None: + offset_millis = (delta.days * 86400 + delta.seconds) * 1000 + delta.microseconds // 1000 + else: + offset_millis = 0 + return min(_MAX_UTC_MS, _MAX_UTC_MS - offset_millis) + + +def _millis_to_datetime( + millis: int, opts: CodecOptions[Any] +) -> Union[datetime.datetime, DatetimeMS]: + """Convert milliseconds since epoch UTC to datetime.""" + if ( + opts.datetime_conversion == DatetimeConversion.DATETIME + or opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP + or opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO + ): + tz = opts.tzinfo or utc + if opts.datetime_conversion == DatetimeConversion.DATETIME_CLAMP: + millis = max(_min_datetime_ms(tz), min(millis, _max_datetime_ms(tz))) + elif opts.datetime_conversion == DatetimeConversion.DATETIME_AUTO: + if not (_min_datetime_ms(tz) <= millis <= _max_datetime_ms(tz)): + return DatetimeMS(millis) + + diff = ((millis % 1000) + 1000) % 1000 + seconds = (millis - diff) // 1000 + micros = diff * 1000 + + try: + if opts.tz_aware: + dt = EPOCH_AWARE + datetime.timedelta(seconds=seconds, microseconds=micros) + if opts.tzinfo: + dt = dt.astimezone(tz) + return dt + else: + return EPOCH_NAIVE + datetime.timedelta(seconds=seconds, microseconds=micros) + except ArithmeticError as err: + raise InvalidBSON(f"{err} {_DATETIME_ERROR_SUGGESTION}") from err + + elif opts.datetime_conversion == DatetimeConversion.DATETIME_MS: + return DatetimeMS(millis) + else: + raise ValueError("datetime_conversion must be an element of DatetimeConversion") diff --git a/.venv/lib/python3.12/site-packages/bson/dbref.py b/.venv/lib/python3.12/site-packages/bson/dbref.py new file mode 100644 index 0000000..40bdb73 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/dbref.py @@ -0,0 +1,133 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for manipulating DBRefs (references to MongoDB documents).""" +from __future__ import annotations + +from copy import deepcopy +from typing import Any, Mapping, Optional + +from bson._helpers import _getstate_slots, _setstate_slots +from bson.son import SON + + +class DBRef: + """A reference to a document stored in MongoDB.""" + + __slots__ = "__collection", "__id", "__database", "__kwargs" + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + # DBRef isn't actually a BSON "type" so this number was arbitrarily chosen. + _type_marker = 100 + + def __init__( + self, + collection: str, + id: Any, + database: Optional[str] = None, + _extra: Optional[Mapping[str, Any]] = None, + **kwargs: Any, + ) -> None: + """Initialize a new :class:`DBRef`. + + Raises :class:`TypeError` if `collection` or `database` is not + an instance of :class:`str`. `database` is optional and allows + references to documents to work across databases. Any additional + keyword arguments will create additional fields in the resultant + embedded document. + + :param collection: name of the collection the document is stored in + :param id: the value of the document's ``"_id"`` field + :param database: name of the database to reference + :param kwargs: additional keyword arguments will + create additional, custom fields + + .. seealso:: The MongoDB documentation on `dbrefs `_. + """ + if not isinstance(collection, str): + raise TypeError(f"collection must be an instance of str, not {type(collection)}") + if database is not None and not isinstance(database, str): + raise TypeError(f"database must be an instance of str, not {type(database)}") + + self.__collection = collection + self.__id = id + self.__database = database + kwargs.update(_extra or {}) + self.__kwargs = kwargs + + @property + def collection(self) -> str: + """Get the name of this DBRef's collection.""" + return self.__collection + + @property + def id(self) -> Any: + """Get this DBRef's _id.""" + return self.__id + + @property + def database(self) -> Optional[str]: + """Get the name of this DBRef's database. + + Returns None if this DBRef doesn't specify a database. + """ + return self.__database + + def __getattr__(self, key: Any) -> Any: + try: + return self.__kwargs[key] + except KeyError: + raise AttributeError(key) from None + + def as_doc(self) -> SON[str, Any]: + """Get the SON document representation of this DBRef. + + Generally not needed by application developers + """ + doc = SON([("$ref", self.collection), ("$id", self.id)]) + if self.database is not None: + doc["$db"] = self.database + doc.update(self.__kwargs) + return doc + + def __repr__(self) -> str: + extra = "".join([f", {k}={v!r}" for k, v in self.__kwargs.items()]) + if self.database is None: + return f"DBRef({self.collection!r}, {self.id!r}{extra})" + return f"DBRef({self.collection!r}, {self.id!r}, {self.database!r}{extra})" + + def __eq__(self, other: Any) -> bool: + if isinstance(other, DBRef): + us = (self.__database, self.__collection, self.__id, self.__kwargs) + them = (other.__database, other.__collection, other.__id, other.__kwargs) + return us == them + return NotImplemented + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __hash__(self) -> int: + """Get a hash value for this :class:`DBRef`.""" + return hash( + (self.__collection, self.__id, self.__database, tuple(sorted(self.__kwargs.items()))) + ) + + def __deepcopy__(self, memo: Any) -> DBRef: + """Support function for `copy.deepcopy()`.""" + return DBRef( + deepcopy(self.__collection, memo), + deepcopy(self.__id, memo), + deepcopy(self.__database, memo), + deepcopy(self.__kwargs, memo), + ) diff --git a/.venv/lib/python3.12/site-packages/bson/decimal128.py b/.venv/lib/python3.12/site-packages/bson/decimal128.py new file mode 100644 index 0000000..7480f94 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/decimal128.py @@ -0,0 +1,351 @@ +# Copyright 2016-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with the BSON decimal128 type. + +.. versionadded:: 3.4 +""" +from __future__ import annotations + +import decimal +import struct +from decimal import Decimal +from typing import Any, Sequence, Tuple, Type, Union + +from bson.codec_options import TypeDecoder, TypeEncoder + +_PACK_64 = struct.Struct(" Type[Decimal]: + return Decimal + + def transform_python(self, value: Any) -> Decimal128: + return Decimal128(value) + + +class DecimalDecoder(TypeDecoder): + """Converts BSON :class:`Decimal128` to Python :class:`decimal.Decimal`. + + For example:: + opts = CodecOptions(type_registry=TypeRegistry([DecimalDecoder()])) + bson.decode(data, codec_options=opts) + + .. versionadded:: 4.15 + """ + + @property + def bson_type(self) -> Type[Decimal128]: + return Decimal128 + + def transform_bson(self, value: Any) -> decimal.Decimal: + return value.to_decimal() + + +def create_decimal128_context() -> decimal.Context: + """Returns an instance of :class:`decimal.Context` appropriate + for working with IEEE-754 128-bit decimal floating point values. + """ + opts = _CTX_OPTIONS.copy() + opts["traps"] = [] + return decimal.Context(**opts) # type: ignore + + +def _decimal_to_128(value: _VALUE_OPTIONS) -> Tuple[int, int]: + """Converts a decimal.Decimal to BID (high bits, low bits). + + :param value: An instance of decimal.Decimal + """ + with decimal.localcontext(_DEC128_CTX) as ctx: + value = ctx.create_decimal(value) + + if value.is_infinite(): + return _NINF if value.is_signed() else _PINF + + sign, digits, exponent = value.as_tuple() + + if value.is_nan(): + if digits: + raise ValueError("NaN with debug payload is not supported") + if value.is_snan(): + return _NSNAN if value.is_signed() else _PSNAN + return _NNAN if value.is_signed() else _PNAN + + significand = int("".join([str(digit) for digit in digits])) + bit_length = significand.bit_length() + + high = 0 + low = 0 + for i in range(min(64, bit_length)): + if significand & (1 << i): + low |= 1 << i + + for i in range(64, bit_length): + if significand & (1 << i): + high |= 1 << (i - 64) + + biased_exponent = exponent + _EXPONENT_BIAS # type: ignore[operator] + + if high >> 49 == 1: + high = high & 0x7FFFFFFFFFFF + high |= _EXPONENT_MASK + high |= (biased_exponent & 0x3FFF) << 47 + else: + high |= biased_exponent << 49 + + if sign: + high |= _SIGN + + return high, low + + +class Decimal128: + """BSON Decimal128 type:: + + >>> Decimal128(Decimal("0.0005")) + Decimal128('0.0005') + >>> Decimal128("0.0005") + Decimal128('0.0005') + >>> Decimal128((3474527112516337664, 5)) + Decimal128('0.0005') + + :param value: An instance of :class:`decimal.Decimal`, string, or tuple of + (high bits, low bits) from Binary Integer Decimal (BID) format. + + .. note:: :class:`~Decimal128` uses an instance of :class:`decimal.Context` + configured for IEEE-754 Decimal128 when validating parameters. + Signals like :class:`decimal.InvalidOperation`, :class:`decimal.Inexact`, + and :class:`decimal.Overflow` are trapped and raised as exceptions:: + + >>> Decimal128(".13.1") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.InvalidOperation: [] + >>> + >>> Decimal128("1E-6177") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Inexact: [] + >>> + >>> Decimal128("1E6145") + Traceback (most recent call last): + File "", line 1, in + ... + decimal.Overflow: [, ] + + To ensure the result of a calculation can always be stored as BSON + Decimal128 use the context returned by + :func:`create_decimal128_context`:: + + >>> import decimal + >>> decimal128_ctx = create_decimal128_context() + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal(".13.3")) + ... + Decimal128('NaN') + >>> + >>> with decimal.localcontext(decimal128_ctx) as ctx: + ... Decimal128(ctx.create_decimal("1E-6177")) + ... + Decimal128('0E-6176') + >>> + >>> with decimal.localcontext(DECIMAL128_CTX) as ctx: + ... Decimal128(ctx.create_decimal("1E6145")) + ... + Decimal128('Infinity') + + To match the behavior of MongoDB's Decimal128 implementation + str(Decimal(value)) may not match str(Decimal128(value)) for NaN values:: + + >>> Decimal128(Decimal('NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-NaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('sNaN')) + Decimal128('NaN') + >>> Decimal128(Decimal('-sNaN')) + Decimal128('NaN') + + However, :meth:`~Decimal128.to_decimal` will return the exact value:: + + >>> Decimal128(Decimal('NaN')).to_decimal() + Decimal('NaN') + >>> Decimal128(Decimal('-NaN')).to_decimal() + Decimal('-NaN') + >>> Decimal128(Decimal('sNaN')).to_decimal() + Decimal('sNaN') + >>> Decimal128(Decimal('-sNaN')).to_decimal() + Decimal('-sNaN') + + Two instances of :class:`Decimal128` compare equal if their Binary + Integer Decimal encodings are equal:: + + >>> Decimal128('NaN') == Decimal128('NaN') + True + >>> Decimal128('NaN').bid == Decimal128('NaN').bid + True + + This differs from :class:`decimal.Decimal` comparisons for NaN:: + + >>> Decimal('NaN') == Decimal('NaN') + False + """ + + __slots__ = ("__high", "__low") + + _type_marker = 19 + + def __init__(self, value: _VALUE_OPTIONS) -> None: + if isinstance(value, (str, decimal.Decimal)): + self.__high, self.__low = _decimal_to_128(value) + elif isinstance(value, (list, tuple)): + if len(value) != 2: + raise ValueError( + "Invalid size for creation of Decimal128 " + "from list or tuple. Must have exactly 2 " + "elements." + ) + self.__high, self.__low = value + else: + raise TypeError(f"Cannot convert {value!r} to Decimal128") + + def to_decimal(self) -> decimal.Decimal: + """Returns an instance of :class:`decimal.Decimal` for this + :class:`Decimal128`. + """ + high = self.__high + low = self.__low + sign = 1 if (high & _SIGN) else 0 + + if (high & _SNAN) == _SNAN: + return decimal.Decimal((sign, (), "N")) # type: ignore + elif (high & _NAN) == _NAN: + return decimal.Decimal((sign, (), "n")) # type: ignore + elif (high & _INF) == _INF: + return decimal.Decimal((sign, (), "F")) # type: ignore + + if (high & _EXPONENT_MASK) == _EXPONENT_MASK: + exponent = ((high & 0x1FFFE00000000000) >> 47) - _EXPONENT_BIAS + return decimal.Decimal((sign, (0,), exponent)) + else: + exponent = ((high & 0x7FFF800000000000) >> 49) - _EXPONENT_BIAS + + arr = bytearray(15) + mask = 0x00000000000000FF + for i in range(14, 6, -1): + arr[i] = (low & mask) >> ((14 - i) << 3) + mask = mask << 8 + + mask = 0x00000000000000FF + for i in range(6, 0, -1): + arr[i] = (high & mask) >> ((6 - i) << 3) + mask = mask << 8 + + mask = 0x0001000000000000 + arr[0] = (high & mask) >> 48 + + # cdecimal only accepts a tuple for digits. + digits = tuple(int(digit) for digit in str(int.from_bytes(arr, "big"))) + + with decimal.localcontext(_DEC128_CTX) as ctx: + return ctx.create_decimal((sign, digits, exponent)) + + @classmethod + def from_bid(cls: Type[Decimal128], value: bytes) -> Decimal128: + """Create an instance of :class:`Decimal128` from Binary Integer + Decimal string. + + :param value: 16 byte string (128-bit IEEE 754-2008 decimal floating + point in Binary Integer Decimal (BID) format). + """ + if not isinstance(value, bytes): + raise TypeError(f"value must be an instance of bytes, not {type(value)}") + if len(value) != 16: + raise ValueError("value must be exactly 16 bytes") + return cls((_UNPACK_64(value[8:])[0], _UNPACK_64(value[:8])[0])) # type: ignore + + @property + def bid(self) -> bytes: + """The Binary Integer Decimal (BID) encoding of this instance.""" + return _PACK_64(self.__low) + _PACK_64(self.__high) + + def __str__(self) -> str: + dec = self.to_decimal() + if dec.is_nan(): + # Required by the drivers spec to match MongoDB behavior. + return "NaN" + return str(dec) + + def __repr__(self) -> str: + return f"Decimal128('{self!s}')" + + def __setstate__(self, value: Tuple[int, int]) -> None: + self.__high, self.__low = value + + def __getstate__(self) -> Tuple[int, int]: + return self.__high, self.__low + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Decimal128): + return self.bid == other.bid + return NotImplemented + + def __ne__(self, other: Any) -> bool: + return not self == other diff --git a/.venv/lib/python3.12/site-packages/bson/errors.py b/.venv/lib/python3.12/site-packages/bson/errors.py new file mode 100644 index 0000000..a3699e7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/errors.py @@ -0,0 +1,36 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Exceptions raised by the BSON package.""" +from __future__ import annotations + + +class BSONError(Exception): + """Base class for all BSON exceptions.""" + + +class InvalidBSON(BSONError): + """Raised when trying to create a BSON object from invalid data.""" + + +class InvalidStringData(BSONError): + """Raised when trying to encode a string containing non-UTF8 data.""" + + +class InvalidDocument(BSONError): + """Raised when trying to create a BSON object from an invalid document.""" + + +class InvalidId(BSONError): + """Raised when trying to create an ObjectId from invalid data.""" diff --git a/.venv/lib/python3.12/site-packages/bson/int64.py b/.venv/lib/python3.12/site-packages/bson/int64.py new file mode 100644 index 0000000..5846504 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/int64.py @@ -0,0 +1,39 @@ +# Copyright 2014-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A BSON wrapper for long (int in python3)""" +from __future__ import annotations + +from typing import Any + + +class Int64(int): + """Representation of the BSON int64 type. + + This is necessary because every integral number is an :class:`int` in + Python 3. Small integral numbers are encoded to BSON int32 by default, + but Int64 numbers will always be encoded to BSON int64. + + :param value: the numeric value to represent + """ + + __slots__ = () + + _type_marker = 18 + + def __getstate__(self) -> Any: + return {} + + def __setstate__(self, state: Any) -> None: + pass diff --git a/.venv/lib/python3.12/site-packages/bson/json_util.py b/.venv/lib/python3.12/site-packages/bson/json_util.py new file mode 100644 index 0000000..8151226 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/json_util.py @@ -0,0 +1,1164 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for using Python's :mod:`json` module with BSON documents. + +This module provides two helper methods `dumps` and `loads` that wrap the +native :mod:`json` methods and provide explicit BSON conversion to and from +JSON. :class:`~bson.json_util.JSONOptions` provides a way to control how JSON +is emitted and parsed, with the default being the Relaxed Extended JSON format. +:mod:`~bson.json_util` can also generate Canonical or legacy `Extended JSON`_ +when :const:`CANONICAL_JSON_OPTIONS` or :const:`LEGACY_JSON_OPTIONS` is +provided, respectively. + +.. _Extended JSON: https://github.com/mongodb/specifications/blob/master/source/extended-json/extended-json.md + +Example usage (deserialization): + +.. doctest:: + + >>> from bson.json_util import loads + >>> loads( + ... '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$scope": {}, "$code": "function x() { return 1; }"}}, {"bin": {"$type": "80", "$binary": "AQIDBA=="}}]' + ... ) + [{'foo': [1, 2]}, {'bar': {'hello': 'world'}}, {'code': Code('function x() { return 1; }', {})}, {'bin': Binary(b'...', 128)}] + +Example usage with :const:`RELAXED_JSON_OPTIONS` (the default): + +.. doctest:: + + >>> from bson import Binary, Code + >>> from bson.json_util import dumps + >>> dumps( + ... [ + ... {"foo": [1, 2]}, + ... {"bar": {"hello": "world"}}, + ... {"code": Code("function x() { return 1; }")}, + ... {"bin": Binary(b"\x01\x02\x03\x04")}, + ... ] + ... ) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`CANONICAL_JSON_OPTIONS`): + +.. doctest:: + + >>> from bson import Binary, Code + >>> from bson.json_util import dumps, CANONICAL_JSON_OPTIONS + >>> dumps( + ... [ + ... {"foo": [1, 2]}, + ... {"bar": {"hello": "world"}}, + ... {"code": Code("function x() { return 1; }")}, + ... {"bin": Binary(b"\x01\x02\x03\x04")}, + ... ], + ... json_options=CANONICAL_JSON_OPTIONS, + ... ) + '[{"foo": [{"$numberInt": "1"}, {"$numberInt": "2"}]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }"}}, {"bin": {"$binary": {"base64": "AQIDBA==", "subType": "00"}}}]' + +Example usage (with :const:`LEGACY_JSON_OPTIONS`): + +.. doctest:: + + >>> from bson import Binary, Code + >>> from bson.json_util import dumps, LEGACY_JSON_OPTIONS + >>> dumps( + ... [ + ... {"foo": [1, 2]}, + ... {"bar": {"hello": "world"}}, + ... {"code": Code("function x() { return 1; }", {})}, + ... {"bin": Binary(b"\x01\x02\x03\x04")}, + ... ], + ... json_options=LEGACY_JSON_OPTIONS, + ... ) + '[{"foo": [1, 2]}, {"bar": {"hello": "world"}}, {"code": {"$code": "function x() { return 1; }", "$scope": {}}}, {"bin": {"$binary": "AQIDBA==", "$type": "00"}}]' + +Alternatively, you can manually pass the `default` to :func:`json.dumps`. +It won't handle :class:`~bson.binary.Binary` and :class:`~bson.code.Code` +instances (as they are extended strings you can't provide custom defaults), +but it will be faster as there is less recursion. + +.. note:: + If your application does not need the flexibility offered by + :class:`JSONOptions` and spends a large amount of time in the `json_util` + module, look to + `python-bsonjs `_ for a nice + performance improvement. `python-bsonjs` is a fast BSON to MongoDB + Extended JSON converter for Python built on top of + `libbson `_. `python-bsonjs` works best + with PyMongo when using :class:`~bson.raw_bson.RawBSONDocument`. +""" +from __future__ import annotations + +import base64 +import datetime +import json +import math +import re +import uuid +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Mapping, + MutableMapping, + Optional, + Sequence, + Tuple, + Type, + Union, + cast, +) + +from bson.binary import ALL_UUID_SUBTYPES, UUID_SUBTYPE, Binary, UuidRepresentation +from bson.code import Code +from bson.codec_options import CodecOptions, DatetimeConversion +from bson.datetime_ms import ( + _MAX_UTC_MS, + EPOCH_AWARE, + DatetimeMS, + _datetime_to_millis, + _millis_to_datetime, +) +from bson.dbref import DBRef +from bson.decimal128 import Decimal128 +from bson.int64 import Int64 +from bson.max_key import MaxKey +from bson.min_key import MinKey +from bson.objectid import ObjectId +from bson.regex import Regex +from bson.son import RE_TYPE +from bson.timestamp import Timestamp +from bson.tz_util import utc + +_RE_OPT_TABLE = { + "i": re.I, + "l": re.L, + "m": re.M, + "s": re.S, + "u": re.U, + "x": re.X, +} + + +class DatetimeRepresentation: + LEGACY = 0 + """Legacy MongoDB Extended JSON datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": }`, where `dateAsMilliseconds` is + a 64-bit signed integer giving the number of milliseconds since the Unix + epoch UTC. This was the default encoding before PyMongo version 3.4. + + .. versionadded:: 3.4 + """ + + NUMBERLONG = 1 + """NumberLong datetime representation. + + :class:`datetime.datetime` instances will be encoded to JSON in the + format `{"$date": {"$numberLong": ""}}`, + where `dateAsMilliseconds` is the string representation of a 64-bit signed + integer giving the number of milliseconds since the Unix epoch UTC. + + .. versionadded:: 3.4 + """ + + ISO8601 = 2 + """ISO-8601 datetime representation. + + :class:`datetime.datetime` instances greater than or equal to the Unix + epoch UTC will be encoded to JSON in the format `{"$date": ""}`. + :class:`datetime.datetime` instances before the Unix epoch UTC will be + encoded as if the datetime representation is + :const:`~DatetimeRepresentation.NUMBERLONG`. + + .. versionadded:: 3.4 + """ + + +class JSONMode: + LEGACY = 0 + """Legacy Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces PyMongo's legacy + non-standard JSON output. Consider using + :const:`~bson.json_util.JSONMode.RELAXED` or + :const:`~bson.json_util.JSONMode.CANONICAL` instead. + + .. versionadded:: 3.5 + """ + + RELAXED = 1 + """Relaxed Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Relaxed Extended JSON, + a mostly JSON-like format. Consider using this for things like a web API, + where one is sending a document (or a projection of a document) that only + uses ordinary JSON type primitives. In particular, the ``int``, + :class:`~bson.int64.Int64`, and ``float`` numeric types are represented in + the native JSON number format. This output is also the most human readable + and is useful for debugging and documentation. + + .. seealso:: The specification for Relaxed `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + CANONICAL = 2 + """Canonical Extended JSON representation. + + In this mode, :func:`~bson.json_util.dumps` produces Canonical Extended + JSON, a type preserving format. Consider using this for things like + testing, where one has to precisely specify expected types in JSON. In + particular, the ``int``, :class:`~bson.int64.Int64`, and ``float`` numeric + types are encoded with type wrappers. + + .. seealso:: The specification for Canonical `Extended JSON`_. + + .. versionadded:: 3.5 + """ + + +if TYPE_CHECKING: + _BASE_CLASS = CodecOptions[MutableMapping[str, Any]] +else: + _BASE_CLASS = CodecOptions + +_INT32_MAX = 2**31 + + +class JSONOptions(_BASE_CLASS): + json_mode: int + strict_number_long: bool + datetime_representation: int + strict_uuid: bool + document_class: Type[MutableMapping[str, Any]] + + def __init__(self, *args: Any, **kwargs: Any): + """Encapsulates JSON options for :func:`dumps` and :func:`loads`. + + :param strict_number_long: If ``True``, :class:`~bson.int64.Int64` objects + are encoded to MongoDB Extended JSON's *Strict mode* type + `NumberLong`, ie ``'{"$numberLong": "" }'``. Otherwise they + will be encoded as an `int`. Defaults to ``False``. + :param datetime_representation: The representation to use when encoding + instances of :class:`datetime.datetime`. Defaults to + :const:`~DatetimeRepresentation.LEGACY`. + :param strict_uuid: If ``True``, :class:`uuid.UUID` object are encoded to + MongoDB Extended JSON's *Strict mode* type `Binary`. Otherwise it + will be encoded as ``'{"$uuid": "" }'``. Defaults to ``False``. + :param json_mode: The :class:`JSONMode` to use when encoding BSON types to + Extended JSON. Defaults to :const:`~JSONMode.LEGACY`. + :param document_class: BSON documents returned by :func:`loads` will be + decoded to an instance of this class. Must be a subclass of + :class:`collections.MutableMapping`. Defaults to :class:`dict`. + :param uuid_representation: The :class:`~bson.binary.UuidRepresentation` + to use when encoding and decoding instances of :class:`uuid.UUID`. + Defaults to :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + :param tz_aware: If ``True``, MongoDB Extended JSON's *Strict mode* type + `Date` will be decoded to timezone aware instances of + :class:`datetime.datetime`. Otherwise they will be naive. Defaults + to ``False``. + :param tzinfo: A :class:`datetime.tzinfo` subclass that specifies the + timezone from which :class:`~datetime.datetime` objects should be + decoded. Defaults to :const:`~bson.tz_util.utc`. + :param datetime_conversion: Specifies how UTC datetimes should be decoded + within BSON. Valid options include 'datetime_ms' to return as a + DatetimeMS, 'datetime' to return as a datetime.datetime and + raising a ValueError for out-of-range values, 'datetime_auto' to + return DatetimeMS objects when the underlying datetime is + out-of-range and 'datetime_clamp' to clamp to the minimum and + maximum possible datetimes. Defaults to 'datetime'. See + `handling out of range datetimes `_ for details. + :param args: arguments to :class:`~bson.codec_options.CodecOptions` + :param kwargs: arguments to :class:`~bson.codec_options.CodecOptions` + + .. seealso:: The specification for Relaxed and Canonical `Extended JSON`_. + + .. versionchanged:: 4.0 + The default for `json_mode` was changed from :const:`JSONMode.LEGACY` + to :const:`JSONMode.RELAXED`. + The default for `uuid_representation` was changed from + :const:`~bson.binary.UuidRepresentation.PYTHON_LEGACY` to + :const:`~bson.binary.UuidRepresentation.UNSPECIFIED`. + + .. versionchanged:: 3.5 + Accepts the optional parameter `json_mode`. + + .. versionchanged:: 4.0 + Changed default value of `tz_aware` to False. + """ + super().__init__() + + def __new__( + cls: Type[JSONOptions], + strict_number_long: Optional[bool] = None, + datetime_representation: Optional[int] = None, + strict_uuid: Optional[bool] = None, + json_mode: int = JSONMode.RELAXED, + *args: Any, + **kwargs: Any, + ) -> JSONOptions: + kwargs["tz_aware"] = kwargs.get("tz_aware", False) + if kwargs["tz_aware"]: + kwargs["tzinfo"] = kwargs.get("tzinfo", utc) + if datetime_representation not in ( + DatetimeRepresentation.LEGACY, + DatetimeRepresentation.NUMBERLONG, + DatetimeRepresentation.ISO8601, + None, + ): + raise ValueError( + "JSONOptions.datetime_representation must be one of LEGACY, " + "NUMBERLONG, or ISO8601 from DatetimeRepresentation." + ) + self = cast(JSONOptions, super().__new__(cls, *args, **kwargs)) + if json_mode not in (JSONMode.LEGACY, JSONMode.RELAXED, JSONMode.CANONICAL): + raise ValueError( + "JSONOptions.json_mode must be one of LEGACY, RELAXED, " + "or CANONICAL from JSONMode." + ) + self.json_mode = json_mode + if self.json_mode == JSONMode.RELAXED: + if strict_number_long: + raise ValueError("Cannot specify strict_number_long=True with JSONMode.RELAXED") + if datetime_representation not in (None, DatetimeRepresentation.ISO8601): + raise ValueError( + "datetime_representation must be DatetimeRepresentation." + "ISO8601 or omitted with JSONMode.RELAXED" + ) + if strict_uuid not in (None, True): + raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.ISO8601 + self.strict_uuid = True + elif self.json_mode == JSONMode.CANONICAL: + if strict_number_long not in (None, True): + raise ValueError("Cannot specify strict_number_long=False with JSONMode.RELAXED") + if datetime_representation not in (None, DatetimeRepresentation.NUMBERLONG): + raise ValueError( + "datetime_representation must be DatetimeRepresentation." + "NUMBERLONG or omitted with JSONMode.RELAXED" + ) + if strict_uuid not in (None, True): + raise ValueError("Cannot specify strict_uuid=False with JSONMode.RELAXED") + self.strict_number_long = True + self.datetime_representation = DatetimeRepresentation.NUMBERLONG + self.strict_uuid = True + else: # JSONMode.LEGACY + self.strict_number_long = False + self.datetime_representation = DatetimeRepresentation.LEGACY + self.strict_uuid = False + if strict_number_long is not None: + self.strict_number_long = strict_number_long + if datetime_representation is not None: + self.datetime_representation = datetime_representation + if strict_uuid is not None: + self.strict_uuid = strict_uuid + return self + + def _arguments_repr(self) -> str: + return ( + "strict_number_long={!r}, " + "datetime_representation={!r}, " + "strict_uuid={!r}, json_mode={!r}, {}".format( + self.strict_number_long, + self.datetime_representation, + self.strict_uuid, + self.json_mode, + super()._arguments_repr(), + ) + ) + + def _options_dict(self) -> dict[Any, Any]: + # TODO: PYTHON-2442 use _asdict() instead + options_dict = super()._options_dict() + options_dict.update( + { + "strict_number_long": self.strict_number_long, + "datetime_representation": self.datetime_representation, + "strict_uuid": self.strict_uuid, + "json_mode": self.json_mode, + } + ) + return options_dict + + def with_options(self, **kwargs: Any) -> JSONOptions: + """ + Make a copy of this JSONOptions, overriding some options:: + + >>> from bson.json_util import CANONICAL_JSON_OPTIONS + >>> CANONICAL_JSON_OPTIONS.tz_aware + True + >>> json_options = CANONICAL_JSON_OPTIONS.with_options(tz_aware=False, tzinfo=None) + >>> json_options.tz_aware + False + + .. versionadded:: 3.12 + """ + opts = self._options_dict() + for opt in ("strict_number_long", "datetime_representation", "strict_uuid", "json_mode"): + opts[opt] = kwargs.get(opt, getattr(self, opt)) + opts.update(kwargs) + return JSONOptions(**opts) + + +LEGACY_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.LEGACY) +""":class:`JSONOptions` for encoding to PyMongo's legacy JSON format. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.LEGACY`. + +.. versionadded:: 3.5 +""" + +CANONICAL_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.CANONICAL) +""":class:`JSONOptions` for Canonical Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.CANONICAL`. + +.. versionadded:: 3.5 +""" + +RELAXED_JSON_OPTIONS: JSONOptions = JSONOptions(json_mode=JSONMode.RELAXED) +""":class:`JSONOptions` for Relaxed Extended JSON. + +.. seealso:: The documentation for :const:`bson.json_util.JSONMode.RELAXED`. + +.. versionadded:: 3.5 +""" + +DEFAULT_JSON_OPTIONS: JSONOptions = RELAXED_JSON_OPTIONS +"""The default :class:`JSONOptions` for JSON encoding/decoding. + +The same as :const:`RELAXED_JSON_OPTIONS`. + +.. versionchanged:: 4.0 + Changed from :const:`LEGACY_JSON_OPTIONS` to + :const:`RELAXED_JSON_OPTIONS`. + +.. versionadded:: 3.4 +""" + + +def dumps(obj: Any, *args: Any, **kwargs: Any) -> str: + """Helper function that wraps :func:`json.dumps`. + + Recursive function that handles all BSON types including + :class:`~bson.binary.Binary` and :class:`~bson.code.Code`. + + :param json_options: A :class:`JSONOptions` instance used to modify the + encoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 4.0 + Now outputs MongoDB Relaxed Extended JSON by default (using + :const:`DEFAULT_JSON_OPTIONS`). + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + return json.dumps(_json_convert(obj, json_options), *args, **kwargs) + + +def loads(s: Union[str, bytes, bytearray], *args: Any, **kwargs: Any) -> Any: + """Helper function that wraps :func:`json.loads`. + + Automatically passes the object_hook for BSON type conversion. + + Raises ``TypeError``, ``ValueError``, ``KeyError``, or + :exc:`~bson.errors.InvalidId` on invalid MongoDB Extended JSON. + + :param json_options: A :class:`JSONOptions` instance used to modify the + decoding of MongoDB Extended JSON types. Defaults to + :const:`DEFAULT_JSON_OPTIONS`. + + .. versionchanged:: 4.0 + Now loads :class:`datetime.datetime` instances as naive by default. To + load timezone aware instances utilize the `json_options` parameter. + See :ref:`tz_aware_default_change` for an example. + + .. versionchanged:: 3.5 + Parses Relaxed and Canonical Extended JSON as well as PyMongo's legacy + format. Now raises ``TypeError`` or ``ValueError`` when parsing JSON + type wrappers with values of the wrong type or any extra keys. + + .. versionchanged:: 3.4 + Accepts optional parameter `json_options`. See :class:`JSONOptions`. + """ + json_options = kwargs.pop("json_options", DEFAULT_JSON_OPTIONS) + # Execution time optimization if json_options.document_class is dict + if json_options.document_class is dict: + kwargs["object_hook"] = lambda obj: object_hook(obj, json_options) + else: + kwargs["object_pairs_hook"] = lambda pairs: object_pairs_hook(pairs, json_options) + return json.loads(s, *args, **kwargs) + + +def _json_convert(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: + """Recursive helper method that converts BSON types so they can be + converted into json. + """ + if hasattr(obj, "items"): + return {k: _json_convert(v, json_options) for k, v in obj.items()} + elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)): + return [_json_convert(v, json_options) for v in obj] + try: + return default(obj, json_options) + except TypeError: + return obj + + +def object_pairs_hook( + pairs: Sequence[Tuple[str, Any]], json_options: JSONOptions = DEFAULT_JSON_OPTIONS +) -> Any: + return object_hook(json_options.document_class(pairs), json_options) # type:ignore[call-arg] + + +def object_hook(dct: Mapping[str, Any], json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: + match = None + for k in dct: + if k in _PARSERS_SET: + match = k + break + if match: + return _PARSERS[match](dct, json_options) + return dct + + +def _parse_legacy_regex(doc: Any, dummy0: Any) -> Any: + pattern = doc["$regex"] + # Check if this is the $regex query operator. + if not isinstance(pattern, (str, bytes)): + return doc + flags = 0 + # PyMongo always adds $options but some other tools may not. + for opt in doc.get("$options", ""): + flags |= _RE_OPT_TABLE.get(opt, 0) + return Regex(pattern, flags) + + +def _parse_legacy_uuid(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + """Decode a JSON legacy $uuid to Python UUID.""" + if len(doc) != 1: + raise TypeError(f"Bad $uuid, extra field(s): {doc}") + if not isinstance(doc["$uuid"], str): + raise TypeError(f"$uuid must be a string: {doc}") + if json_options.uuid_representation == UuidRepresentation.UNSPECIFIED: + return Binary.from_uuid(uuid.UUID(doc["$uuid"])) + else: + return uuid.UUID(doc["$uuid"]) + + +def _binary_or_uuid(data: Any, subtype: int, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + # special handling for UUID + if subtype in ALL_UUID_SUBTYPES: + uuid_representation = json_options.uuid_representation + binary_value = Binary(data, subtype) + if uuid_representation == UuidRepresentation.UNSPECIFIED: + return binary_value + if subtype == UUID_SUBTYPE: + # Legacy behavior: use STANDARD with binary subtype 4. + uuid_representation = UuidRepresentation.STANDARD + elif uuid_representation == UuidRepresentation.STANDARD: + # subtype == OLD_UUID_SUBTYPE + # Legacy behavior: STANDARD is the same as PYTHON_LEGACY. + uuid_representation = UuidRepresentation.PYTHON_LEGACY + return binary_value.as_uuid(uuid_representation) + + if subtype == 0: + return cast(uuid.UUID, data) + return Binary(data, subtype) + + +def _parse_legacy_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + if isinstance(doc["$type"], int): + doc["$type"] = "%02x" % doc["$type"] + subtype = int(doc["$type"], 16) + if subtype >= 0xFFFFFF80: # Handle mongoexport values + subtype = int(doc["$type"][6:], 16) + data = base64.b64decode(doc["$binary"].encode()) + return _binary_or_uuid(data, subtype, json_options) + + +def _parse_canonical_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + binary = doc["$binary"] + b64 = binary["base64"] + subtype = binary["subType"] + if not isinstance(b64, str): + raise TypeError(f"$binary base64 must be a string: {doc}") + if not isinstance(subtype, str) or len(subtype) > 2: + raise TypeError(f"$binary subType must be a string at most 2 characters: {doc}") + if len(binary) != 2: + raise TypeError(f'$binary must include only "base64" and "subType" components: {doc}') + + data = base64.b64decode(b64.encode()) + return _binary_or_uuid(data, int(subtype, 16), json_options) + + +def _parse_canonical_datetime( + doc: Any, json_options: JSONOptions +) -> Union[datetime.datetime, DatetimeMS]: + """Decode a JSON datetime to python datetime.datetime.""" + dtm = doc["$date"] + if len(doc) != 1: + raise TypeError(f"Bad $date, extra field(s): {doc}") + # mongoexport 2.6 and newer + if isinstance(dtm, str): + try: + # Parse offset + if dtm[-1] == "Z": + dt = dtm[:-1] + offset = "Z" + elif dtm[-6] in ("+", "-") and dtm[-3] == ":": + # (+|-)HH:MM + dt = dtm[:-6] + offset = dtm[-6:] + elif dtm[-5] in ("+", "-"): + # (+|-)HHMM + dt = dtm[:-5] + offset = dtm[-5:] + elif dtm[-3] in ("+", "-"): + # (+|-)HH + dt = dtm[:-3] + offset = dtm[-3:] + else: + dt = dtm + offset = "" + except IndexError as exc: + raise ValueError(f"time data {dtm!r} does not match ISO-8601 datetime format") from exc + + # Parse the optional factional seconds portion. + dot_index = dt.rfind(".") + microsecond = 0 + if dot_index != -1: + microsecond = int(float(dt[dot_index:]) * 1000000) + dt = dt[:dot_index] + + aware = datetime.datetime.strptime(dt, "%Y-%m-%dT%H:%M:%S").replace( + microsecond=microsecond, tzinfo=utc + ) + + if offset and offset != "Z": + if len(offset) == 6: + hours, minutes = offset[1:].split(":") + secs = int(hours) * 3600 + int(minutes) * 60 + elif len(offset) == 5: + secs = int(offset[1:3]) * 3600 + int(offset[3:]) * 60 + elif len(offset) == 3: + secs = int(offset[1:3]) * 3600 + if offset[0] == "-": + secs *= -1 + aware = aware - datetime.timedelta(seconds=secs) + + if json_options.tz_aware: + if json_options.tzinfo: + aware = aware.astimezone(json_options.tzinfo) + if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS: + return DatetimeMS(aware) + return aware + else: + aware_tzinfo_none = aware.replace(tzinfo=None) + if json_options.datetime_conversion == DatetimeConversion.DATETIME_MS: + return DatetimeMS(aware_tzinfo_none) + return aware_tzinfo_none + return _millis_to_datetime(int(dtm), cast("CodecOptions[Any]", json_options)) + + +def _parse_canonical_oid(doc: Any, dummy0: Any) -> ObjectId: + """Decode a JSON ObjectId to bson.objectid.ObjectId.""" + if len(doc) != 1: + raise TypeError(f"Bad $oid, extra field(s): {doc}") + return ObjectId(doc["$oid"]) + + +def _parse_canonical_symbol(doc: Any, dummy0: Any) -> str: + """Decode a JSON symbol to Python string.""" + symbol = doc["$symbol"] + if len(doc) != 1: + raise TypeError(f"Bad $symbol, extra field(s): {doc}") + return str(symbol) + + +def _parse_canonical_code(doc: Any, dummy0: Any) -> Code: + """Decode a JSON code to bson.code.Code.""" + for key in doc: + if key not in ("$code", "$scope"): + raise TypeError(f"Bad $code, extra field(s): {doc}") + return Code(doc["$code"], scope=doc.get("$scope")) + + +def _parse_canonical_regex(doc: Any, dummy0: Any) -> Regex[str]: + """Decode a JSON regex to bson.regex.Regex.""" + regex = doc["$regularExpression"] + if len(doc) != 1: + raise TypeError(f"Bad $regularExpression, extra field(s): {doc}") + if len(regex) != 2: + raise TypeError( + f'Bad $regularExpression must include only "pattern and "options" components: {doc}' + ) + opts = regex["options"] + if not isinstance(opts, str): + raise TypeError( + "Bad $regularExpression options, options must be string, was type %s" % (type(opts)) + ) + return Regex(regex["pattern"], opts) + + +def _parse_canonical_dbref(doc: Any, dummy0: Any) -> Any: + """Decode a JSON DBRef to bson.dbref.DBRef.""" + if ( + isinstance(doc.get("$ref"), str) + and "$id" in doc + and isinstance(doc.get("$db"), (str, type(None))) + ): + return DBRef(doc.pop("$ref"), doc.pop("$id"), database=doc.pop("$db", None), **doc) + return doc + + +def _parse_canonical_dbpointer(doc: Any, dummy0: Any) -> Any: + """Decode a JSON (deprecated) DBPointer to bson.dbref.DBRef.""" + dbref = doc["$dbPointer"] + if len(doc) != 1: + raise TypeError(f"Bad $dbPointer, extra field(s): {doc}") + if isinstance(dbref, DBRef): + dbref_doc = dbref.as_doc() + # DBPointer must not contain $db in its value. + if dbref.database is not None: + raise TypeError(f"Bad $dbPointer, extra field $db: {dbref_doc}") + if not isinstance(dbref.id, ObjectId): + raise TypeError(f"Bad $dbPointer, $id must be an ObjectId: {dbref_doc}") + if len(dbref_doc) != 2: + raise TypeError(f"Bad $dbPointer, extra field(s) in DBRef: {dbref_doc}") + return dbref + else: + raise TypeError(f"Bad $dbPointer, expected a DBRef: {doc}") + + +def _parse_canonical_int32(doc: Any, dummy0: Any) -> int: + """Decode a JSON int32 to python int.""" + i_str = doc["$numberInt"] + if len(doc) != 1: + raise TypeError(f"Bad $numberInt, extra field(s): {doc}") + if not isinstance(i_str, str): + raise TypeError(f"$numberInt must be string: {doc}") + return int(i_str) + + +def _parse_canonical_int64(doc: Any, dummy0: Any) -> Int64: + """Decode a JSON int64 to bson.int64.Int64.""" + l_str = doc["$numberLong"] + if len(doc) != 1: + raise TypeError(f"Bad $numberLong, extra field(s): {doc}") + return Int64(l_str) + + +def _parse_canonical_double(doc: Any, dummy0: Any) -> float: + """Decode a JSON double to python float.""" + d_str = doc["$numberDouble"] + if len(doc) != 1: + raise TypeError(f"Bad $numberDouble, extra field(s): {doc}") + if not isinstance(d_str, str): + raise TypeError(f"$numberDouble must be string: {doc}") + return float(d_str) + + +def _parse_canonical_decimal128(doc: Any, dummy0: Any) -> Decimal128: + """Decode a JSON decimal128 to bson.decimal128.Decimal128.""" + d_str = doc["$numberDecimal"] + if len(doc) != 1: + raise TypeError(f"Bad $numberDecimal, extra field(s): {doc}") + if not isinstance(d_str, str): + raise TypeError(f"$numberDecimal must be string: {doc}") + return Decimal128(d_str) + + +def _parse_canonical_minkey(doc: Any, dummy0: Any) -> MinKey: + """Decode a JSON MinKey to bson.min_key.MinKey.""" + if type(doc["$minKey"]) is not int or doc["$minKey"] != 1: # noqa: E721 + raise TypeError(f"$minKey value must be 1: {doc}") + if len(doc) != 1: + raise TypeError(f"Bad $minKey, extra field(s): {doc}") + return MinKey() + + +def _parse_canonical_maxkey(doc: Any, dummy0: Any) -> MaxKey: + """Decode a JSON MaxKey to bson.max_key.MaxKey.""" + if type(doc["$maxKey"]) is not int or doc["$maxKey"] != 1: # noqa: E721 + raise TypeError("$maxKey value must be 1: %s", (doc,)) + if len(doc) != 1: + raise TypeError(f"Bad $minKey, extra field(s): {doc}") + return MaxKey() + + +def _parse_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUID]: + if "$type" in doc: + return _parse_legacy_binary(doc, json_options) + else: + return _parse_canonical_binary(doc, json_options) + + +def _parse_timestamp(doc: Any, dummy0: Any) -> Timestamp: + tsp = doc["$timestamp"] + return Timestamp(tsp["t"], tsp["i"]) + + +_PARSERS: dict[str, Callable[[Any, JSONOptions], Any]] = { + "$oid": _parse_canonical_oid, + "$ref": _parse_canonical_dbref, + "$date": _parse_canonical_datetime, + "$regex": _parse_legacy_regex, + "$minKey": _parse_canonical_minkey, + "$maxKey": _parse_canonical_maxkey, + "$binary": _parse_binary, + "$code": _parse_canonical_code, + "$uuid": _parse_legacy_uuid, + "$undefined": lambda _, _1: None, + "$numberLong": _parse_canonical_int64, + "$timestamp": _parse_timestamp, + "$numberDecimal": _parse_canonical_decimal128, + "$dbPointer": _parse_canonical_dbpointer, + "$regularExpression": _parse_canonical_regex, + "$symbol": _parse_canonical_symbol, + "$numberInt": _parse_canonical_int32, + "$numberDouble": _parse_canonical_double, +} +_PARSERS_SET = set(_PARSERS) + + +def _encode_binary(data: bytes, subtype: int, json_options: JSONOptions) -> Any: + if json_options.json_mode == JSONMode.LEGACY: + return {"$binary": base64.b64encode(data).decode(), "$type": "%02x" % subtype} + return {"$binary": {"base64": base64.b64encode(data).decode(), "subType": "%02x" % subtype}} + + +def _encode_datetimems(obj: Any, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + if ( + json_options.datetime_representation == DatetimeRepresentation.ISO8601 + and 0 <= int(obj) <= _MAX_UTC_MS + ): + return _encode_datetime(obj.as_datetime(), json_options) + elif json_options.datetime_representation == DatetimeRepresentation.LEGACY: + return {"$date": int(obj)} + return {"$date": {"$numberLong": str(int(obj))}} + + +def _encode_code(obj: Code, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + if obj.scope is None: + return {"$code": str(obj)} + else: + return {"$code": str(obj), "$scope": _json_convert(obj.scope, json_options)} + + +def _encode_int64(obj: Int64, json_options: JSONOptions) -> Any: + if json_options.strict_number_long: + return {"$numberLong": str(obj)} + else: + return int(obj) + + +def _encode_noop(obj: Any, dummy0: Any) -> Any: + return obj + + +def _encode_regex(obj: Any, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + flags = "" + if obj.flags & re.IGNORECASE: + flags += "i" + if obj.flags & re.LOCALE: + flags += "l" + if obj.flags & re.MULTILINE: + flags += "m" + if obj.flags & re.DOTALL: + flags += "s" + if obj.flags & re.UNICODE: + flags += "u" + if obj.flags & re.VERBOSE: + flags += "x" + if isinstance(obj.pattern, str): + pattern = obj.pattern + else: + pattern = obj.pattern.decode("utf-8") + if json_options.json_mode == JSONMode.LEGACY: + return {"$regex": pattern, "$options": flags} + return {"$regularExpression": {"pattern": pattern, "options": flags}} + + +def _encode_int(obj: int, json_options: JSONOptions) -> Any: + if json_options.json_mode == JSONMode.CANONICAL: + if -_INT32_MAX <= obj < _INT32_MAX: + return {"$numberInt": str(obj)} + return {"$numberLong": str(obj)} + return obj + + +def _encode_float(obj: float, json_options: JSONOptions) -> Any: + if json_options.json_mode != JSONMode.LEGACY: + if math.isnan(obj): + return {"$numberDouble": "NaN"} + elif math.isinf(obj): + representation = "Infinity" if obj > 0 else "-Infinity" + return {"$numberDouble": representation} + elif json_options.json_mode == JSONMode.CANONICAL: + # repr() will return the shortest string guaranteed to produce the + # original value, when float() is called on it. + return {"$numberDouble": str(repr(obj))} + return obj + + +def _encode_datetime(obj: datetime.datetime, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + if json_options.datetime_representation == DatetimeRepresentation.ISO8601: + if not obj.tzinfo: + obj = obj.replace(tzinfo=utc) + assert obj.tzinfo is not None + if obj >= EPOCH_AWARE: + off = obj.tzinfo.utcoffset(obj) + if (off.days, off.seconds, off.microseconds) == (0, 0, 0): # type: ignore + tz_string = "Z" + else: + tz_string = obj.strftime("%z") + millis = int(obj.microsecond / 1000) + fracsecs = ".%03d" % (millis,) if millis else "" + return { + "$date": "{}{}{}".format(obj.strftime("%Y-%m-%dT%H:%M:%S"), fracsecs, tz_string) + } + + millis = _datetime_to_millis(obj) + if json_options.datetime_representation == DatetimeRepresentation.LEGACY: + return {"$date": millis} + return {"$date": {"$numberLong": str(millis)}} + + +def _encode_bytes(obj: bytes, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + return _encode_binary(obj, 0, json_options) + + +def _encode_binary_obj(obj: Binary, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + return _encode_binary(obj, obj.subtype, json_options) + + +def _encode_uuid(obj: uuid.UUID, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + if json_options.strict_uuid: + binval = Binary.from_uuid(obj, uuid_representation=json_options.uuid_representation) + return _encode_binary(binval, binval.subtype, json_options) + else: + return {"$uuid": obj.hex} + + +def _encode_objectid(obj: ObjectId, dummy0: Any) -> dict: # type: ignore[type-arg] + return {"$oid": str(obj)} + + +def _encode_timestamp(obj: Timestamp, dummy0: Any) -> dict: # type: ignore[type-arg] + return {"$timestamp": {"t": obj.time, "i": obj.inc}} + + +def _encode_decimal128(obj: Timestamp, dummy0: Any) -> dict: # type: ignore[type-arg] + return {"$numberDecimal": str(obj)} + + +def _encode_dbref(obj: DBRef, json_options: JSONOptions) -> dict: # type: ignore[type-arg] + return _json_convert(obj.as_doc(), json_options=json_options) + + +def _encode_minkey(dummy0: Any, dummy1: Any) -> dict: # type: ignore[type-arg] + return {"$minKey": 1} + + +def _encode_maxkey(dummy0: Any, dummy1: Any) -> dict: # type: ignore[type-arg] + return {"$maxKey": 1} + + +# Encoders for BSON types +# Each encoder function's signature is: +# - obj: a Python data type, e.g. a Python int for _encode_int +# - json_options: a JSONOptions +_ENCODERS: dict[Type, Callable[[Any, JSONOptions], Any]] = { # type: ignore[type-arg] + bool: _encode_noop, + bytes: _encode_bytes, + datetime.datetime: _encode_datetime, + DatetimeMS: _encode_datetimems, + float: _encode_float, + int: _encode_int, + str: _encode_noop, + type(None): _encode_noop, + uuid.UUID: _encode_uuid, + Binary: _encode_binary_obj, + Int64: _encode_int64, + Code: _encode_code, + DBRef: _encode_dbref, + MaxKey: _encode_maxkey, + MinKey: _encode_minkey, + ObjectId: _encode_objectid, + Regex: _encode_regex, + RE_TYPE: _encode_regex, + Timestamp: _encode_timestamp, + Decimal128: _encode_decimal128, +} + +# Map each _type_marker to its encoder for faster lookup. +_MARKERS: dict[int, Callable[[Any, JSONOptions], Any]] = {} +for _typ in _ENCODERS: + if hasattr(_typ, "_type_marker"): + _MARKERS[_typ._type_marker] = _ENCODERS[_typ] + +_BUILT_IN_TYPES = tuple(t for t in _ENCODERS) + + +def default(obj: Any, json_options: JSONOptions = DEFAULT_JSON_OPTIONS) -> Any: + # First see if the type is already cached. KeyError will only ever + # happen once per subtype. + try: + return _ENCODERS[type(obj)](obj, json_options) + except KeyError: + pass + + # Second, fall back to trying _type_marker. This has to be done + # before the loop below since users could subclass one of our + # custom types that subclasses a python built-in (e.g. Binary) + if hasattr(obj, "_type_marker"): + marker = obj._type_marker + if marker in _MARKERS: + func = _MARKERS[marker] + # Cache this type for faster subsequent lookup. + _ENCODERS[type(obj)] = func + return func(obj, json_options) + + # Third, test each base type. This will only happen once for + # a subtype of a supported base type. + for base in _BUILT_IN_TYPES: + if isinstance(obj, base): + func = _ENCODERS[base] + # Cache this type for faster subsequent lookup. + _ENCODERS[type(obj)] = func + return func(obj, json_options) + + raise TypeError("%r is not JSON serializable" % obj) + + +def _get_str_size(obj: Any) -> int: + return len(obj) + + +def _get_datetime_size(obj: datetime.datetime) -> int: + return 5 + len(str(obj.time())) + + +def _get_regex_size(obj: Regex) -> int: # type: ignore[type-arg] + return 18 + len(obj.pattern) + + +def _get_dbref_size(obj: DBRef) -> int: + return 34 + len(obj.collection) + + +_CONSTANT_SIZE_TABLE: dict[Any, int] = { + ObjectId: 28, + int: 11, + Int64: 11, + Decimal128: 11, + Timestamp: 14, + MinKey: 8, + MaxKey: 8, +} + +_VARIABLE_SIZE_TABLE: dict[Any, Callable[[Any], int]] = { + str: _get_str_size, + bytes: _get_str_size, + datetime.datetime: _get_datetime_size, + Regex: _get_regex_size, + DBRef: _get_dbref_size, +} + + +def get_size(obj: Any, max_size: int, current_size: int = 0) -> int: + """Recursively finds size of objects""" + if current_size >= max_size: + return current_size + + obj_type = type(obj) + + # Check to see if the obj has a constant size estimate + try: + return _CONSTANT_SIZE_TABLE[obj_type] + except KeyError: + pass + + # Check to see if the obj has a variable but simple size estimate + try: + return _VARIABLE_SIZE_TABLE[obj_type](obj) + except KeyError: + pass + + # Special cases that require recursion + if obj_type == Code: + if obj.scope: + current_size += ( + 5 + get_size(obj.scope, max_size, current_size) + len(obj) - len(obj.scope) + ) + else: + current_size += 5 + len(obj) + elif obj_type == dict: + for k, v in obj.items(): + current_size += get_size(k, max_size, current_size) + current_size += get_size(v, max_size, current_size) + if current_size >= max_size: + return current_size + elif hasattr(obj, "__iter__"): + for i in obj: + current_size += get_size(i, max_size, current_size) + if current_size >= max_size: + return current_size + return current_size + + +def _truncate_documents(obj: Any, max_length: int) -> Tuple[Any, int]: + """Recursively truncate documents as needed to fit inside max_length characters.""" + if max_length <= 0: + return None, 0 + remaining = max_length + if hasattr(obj, "items"): + truncated: Any = {} + for k, v in obj.items(): + truncated_v, remaining = _truncate_documents(v, remaining) + if truncated_v: + truncated[k] = truncated_v + if remaining <= 0: + break + return truncated, remaining + elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes)): + truncated: Any = [] # type:ignore[no-redef] + for v in obj: + truncated_v, remaining = _truncate_documents(v, remaining) + if truncated_v: + truncated.append(truncated_v) + if remaining <= 0: + break + return truncated, remaining + else: + return _truncate(obj, remaining) + + +def _truncate(obj: Any, remaining: int) -> Tuple[Any, int]: + size = get_size(obj, remaining) + + if size <= remaining: + return obj, remaining - size + else: + try: + truncated = obj[:remaining] + except TypeError: + truncated = obj + return truncated, remaining - size diff --git a/.venv/lib/python3.12/site-packages/bson/max_key.py b/.venv/lib/python3.12/site-packages/bson/max_key.py new file mode 100644 index 0000000..445e12f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/max_key.py @@ -0,0 +1,56 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MaxKey type.""" +from __future__ import annotations + +from typing import Any + + +class MaxKey: + """MongoDB internal MaxKey type.""" + + __slots__ = () + + _type_marker = 127 + + def __getstate__(self) -> Any: + return {} + + def __setstate__(self, state: Any) -> None: + pass + + def __eq__(self, other: Any) -> bool: + return isinstance(other, MaxKey) + + def __hash__(self) -> int: + return hash(self._type_marker) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __le__(self, other: Any) -> bool: + return isinstance(other, MaxKey) + + def __lt__(self, dummy: Any) -> bool: + return False + + def __ge__(self, dummy: Any) -> bool: + return True + + def __gt__(self, other: Any) -> bool: + return not isinstance(other, MaxKey) + + def __repr__(self) -> str: + return "MaxKey()" diff --git a/.venv/lib/python3.12/site-packages/bson/min_key.py b/.venv/lib/python3.12/site-packages/bson/min_key.py new file mode 100644 index 0000000..37828dc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/min_key.py @@ -0,0 +1,56 @@ +# Copyright 2010-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Representation for the MongoDB internal MinKey type.""" +from __future__ import annotations + +from typing import Any + + +class MinKey: + """MongoDB internal MinKey type.""" + + __slots__ = () + + _type_marker = 255 + + def __getstate__(self) -> Any: + return {} + + def __setstate__(self, state: Any) -> None: + pass + + def __eq__(self, other: Any) -> bool: + return isinstance(other, MinKey) + + def __hash__(self) -> int: + return hash(self._type_marker) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __le__(self, dummy: Any) -> bool: + return True + + def __lt__(self, other: Any) -> bool: + return not isinstance(other, MinKey) + + def __ge__(self, other: Any) -> bool: + return isinstance(other, MinKey) + + def __gt__(self, dummy: Any) -> bool: + return False + + def __repr__(self) -> str: + return "MinKey()" diff --git a/.venv/lib/python3.12/site-packages/bson/objectid.py b/.venv/lib/python3.12/site-packages/bson/objectid.py new file mode 100644 index 0000000..970c4e5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/objectid.py @@ -0,0 +1,274 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for working with MongoDB ObjectIds.""" +from __future__ import annotations + +import binascii +import datetime +import os +import struct +import threading +import time +from random import SystemRandom +from typing import Any, NoReturn, Optional, Type, Union + +from bson.datetime_ms import _datetime_to_millis +from bson.errors import InvalidId +from bson.tz_util import utc + +_MAX_COUNTER_VALUE = 0xFFFFFF +_PACK_INT = struct.Struct(">I").pack +_PACK_INT_RANDOM = struct.Struct(">I5s").pack +_UNPACK_INT = struct.Struct(">I").unpack + + +def _raise_invalid_id(oid: str) -> NoReturn: + raise InvalidId( + "%r is not a valid ObjectId, it must be a 12-byte input" + " or a 24-character hex string" % oid + ) + + +def _random_bytes() -> bytes: + """Get the 5-byte random field of an ObjectId.""" + return os.urandom(5) + + +class ObjectId: + """A MongoDB ObjectId.""" + + _pid = os.getpid() + + _inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE) + _inc_lock = threading.Lock() + + __random = _random_bytes() + + __slots__ = ("__id",) + + _type_marker = 7 + + def __init__(self, oid: Optional[Union[str, ObjectId, bytes]] = None) -> None: + """Initialize a new ObjectId. + + An ObjectId is a 12-byte unique identifier consisting of: + + - a 4-byte value representing the seconds since the Unix epoch, + - a 5-byte random value, + - a 3-byte counter, starting with a random value. + + By default, ``ObjectId()`` creates a new unique identifier. The + optional parameter `oid` can be an :class:`ObjectId`, or any 12 + :class:`bytes`. + + For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId + specification but they are acceptable input:: + + >>> ObjectId(b'foo-bar-quux') + ObjectId('666f6f2d6261722d71757578') + + `oid` can also be a :class:`str` of 24 hex digits:: + + >>> ObjectId('0123456789ab0123456789ab') + ObjectId('0123456789ab0123456789ab') + + Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor + 24 hex digits, or :class:`TypeError` if `oid` is not an accepted type. + + :param oid: a valid ObjectId. + + .. seealso:: The MongoDB documentation on `ObjectIds `_. + + .. versionchanged:: 3.8 + :class:`~bson.objectid.ObjectId` now implements the `ObjectID + specification version 0.2 + `_. + """ + if oid is None: + self.__generate() + elif isinstance(oid, bytes) and len(oid) == 12: + self.__id = oid + else: + self.__validate(oid) + + @classmethod + def from_datetime(cls: Type[ObjectId], generation_time: datetime.datetime) -> ObjectId: + """Create a dummy ObjectId instance with a specific generation time. + + This method is useful for doing range queries on a field + containing :class:`ObjectId` instances. + + .. warning:: + It is not safe to insert a document containing an ObjectId + generated using this method. This method deliberately + eliminates the uniqueness guarantee that ObjectIds + generally provide. ObjectIds generated with this method + should be used exclusively in queries. + + `generation_time` will be converted to UTC. Naive datetime + instances will be treated as though they already contain UTC. + + An example using this helper to get documents where ``"_id"`` + was generated before January 1, 2010 would be: + + >>> gen_time = datetime.datetime(2010, 1, 1) + >>> dummy_id = ObjectId.from_datetime(gen_time) + >>> result = collection.find({"_id": {"$lt": dummy_id}}) + + :param generation_time: :class:`~datetime.datetime` to be used + as the generation time for the resulting ObjectId. + """ + oid = ( + _PACK_INT(_datetime_to_millis(generation_time) // 1000) + + b"\x00\x00\x00\x00\x00\x00\x00\x00" + ) + return cls(oid) + + @classmethod + def is_valid(cls: Type[ObjectId], oid: Any) -> bool: + """Checks if a `oid` string is valid or not. + + :param oid: the object id to validate + + .. versionadded:: 2.3 + """ + if not oid: + return False + + try: + ObjectId(oid) + return True + except (InvalidId, TypeError): + return False + + @classmethod + def _random(cls) -> bytes: + """Generate a 5-byte random number once per process.""" + pid = os.getpid() + if pid != cls._pid: + cls._pid = pid + cls.__random = _random_bytes() + return cls.__random + + def __generate(self) -> None: + """Generate a new value for this ObjectId.""" + with ObjectId._inc_lock: + inc = ObjectId._inc + ObjectId._inc = (inc + 1) % (_MAX_COUNTER_VALUE + 1) + + # 4 bytes current time, 5 bytes random, 3 bytes inc. + self.__id = _PACK_INT_RANDOM(int(time.time()), ObjectId._random()) + _PACK_INT(inc)[1:4] + + def __validate(self, oid: Any) -> None: + """Validate and use the given id for this ObjectId. + + Raises TypeError if id is not an instance of :class:`str`, + :class:`bytes`, or ObjectId. Raises InvalidId if it is not a + valid ObjectId. + + :param oid: a valid ObjectId + """ + if isinstance(oid, ObjectId): + self.__id = oid.binary + elif isinstance(oid, str): + if len(oid) == 24: + try: + self.__id = bytes.fromhex(oid) + except (TypeError, ValueError): + _raise_invalid_id(oid) + else: + _raise_invalid_id(oid) + else: + raise TypeError(f"id must be an instance of (bytes, str, ObjectId), not {type(oid)}") + + @property + def binary(self) -> bytes: + """12-byte binary representation of this ObjectId.""" + return self.__id + + @property + def generation_time(self) -> datetime.datetime: + """A :class:`datetime.datetime` instance representing the time of + generation for this :class:`ObjectId`. + + The :class:`datetime.datetime` is timezone aware, and + represents the generation time in UTC. It is precise to the + second. + """ + timestamp = _UNPACK_INT(self.__id[0:4])[0] + return datetime.datetime.fromtimestamp(timestamp, utc) + + def __getstate__(self) -> bytes: + """Return value of object for pickling. + needed explicitly because __slots__() defined. + """ + return self.__id + + def __setstate__(self, value: Any) -> None: + """Explicit state set from pickling""" + # Provide backwards compatibility with OIDs + # pickled with pymongo-1.9 or older. + if isinstance(value, dict): + oid = value["_ObjectId__id"] + else: + oid = value + # ObjectIds pickled in python 2.x used `str` for __id. + # In python 3.x this has to be converted to `bytes` + # by encoding latin-1. + if isinstance(oid, str): + self.__id = oid.encode("latin-1") + else: + self.__id = oid + + def __str__(self) -> str: + return binascii.hexlify(self.__id).decode() + + def __repr__(self) -> str: + return f"ObjectId('{self!s}')" + + def __eq__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id == other.binary + return NotImplemented + + def __ne__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id != other.binary + return NotImplemented + + def __lt__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id < other.binary + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id <= other.binary + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id > other.binary + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, ObjectId): + return self.__id >= other.binary + return NotImplemented + + def __hash__(self) -> int: + """Get a hash value for this :class:`ObjectId`.""" + return hash(self.__id) diff --git a/.venv/lib/python3.12/site-packages/bson/py.typed b/.venv/lib/python3.12/site-packages/bson/py.typed new file mode 100644 index 0000000..0f40570 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/py.typed @@ -0,0 +1,2 @@ +# PEP-561 Support File. +# "Package maintainers who wish to support type checking of their code MUST add a marker file named py.typed to their package supporting typing". diff --git a/.venv/lib/python3.12/site-packages/bson/raw_bson.py b/.venv/lib/python3.12/site-packages/bson/raw_bson.py new file mode 100644 index 0000000..9ead076 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/raw_bson.py @@ -0,0 +1,200 @@ +# Copyright 2015-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing raw BSON documents. + +Inserting and Retrieving RawBSONDocuments +========================================= + +Example: Moving a document between different databases/collections + +.. doctest:: + + >>> import bson + >>> from pymongo import MongoClient + >>> from bson.raw_bson import RawBSONDocument + >>> client = MongoClient(document_class=RawBSONDocument) + >>> client.drop_database("db") + >>> client.drop_database("replica_db") + >>> db = client.db + >>> result = db.test.insert_many( + ... [{"_id": 1, "a": 1}, {"_id": 2, "b": 1}, {"_id": 3, "c": 1}, {"_id": 4, "d": 1}] + ... ) + >>> replica_db = client.replica_db + >>> for doc in db.test.find(): + ... print(f"raw document: {doc.raw}") + ... print(f"decoded document: {bson.decode(doc.raw)}") + ... result = replica_db.test.insert_one(doc) + ... + raw document: b'...' + decoded document: {'_id': 1, 'a': 1} + raw document: b'...' + decoded document: {'_id': 2, 'b': 1} + raw document: b'...' + decoded document: {'_id': 3, 'c': 1} + raw document: b'...' + decoded document: {'_id': 4, 'd': 1} + +For use cases like moving documents across different databases or writing binary +blobs to disk, using raw BSON documents provides better speed and avoids the +overhead of decoding or encoding BSON. +""" +from __future__ import annotations + +from typing import Any, ItemsView, Iterator, Mapping, Optional + +from bson import _get_object_size, _raw_to_dict +from bson.codec_options import _RAW_BSON_DOCUMENT_MARKER, CodecOptions +from bson.codec_options import DEFAULT_CODEC_OPTIONS as DEFAULT + + +def _inflate_bson( + bson_bytes: bytes | memoryview, + codec_options: CodecOptions[RawBSONDocument], + raw_array: bool = False, +) -> dict[str, Any]: + """Inflates the top level fields of a BSON document. + + :param bson_bytes: the BSON bytes that compose this document + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. + """ + return _raw_to_dict(bson_bytes, 4, len(bson_bytes) - 1, codec_options, {}, raw_array=raw_array) + + +class RawBSONDocument(Mapping[str, Any]): + """Representation for a MongoDB document that provides access to the raw + BSON bytes that compose it. + + Only when a field is accessed or modified within the document does + RawBSONDocument decode its bytes. + """ + + __slots__ = ("__raw", "__inflated_doc", "__codec_options") + _type_marker = _RAW_BSON_DOCUMENT_MARKER + __codec_options: CodecOptions[RawBSONDocument] + + def __init__( + self, + bson_bytes: bytes | memoryview, + codec_options: Optional[CodecOptions[RawBSONDocument]] = None, + ) -> None: + """Create a new :class:`RawBSONDocument` + + :class:`RawBSONDocument` is a representation of a BSON document that + provides access to the underlying raw BSON bytes. Only when a field is + accessed or modified within the document does RawBSONDocument decode + its bytes. + + :class:`RawBSONDocument` implements the ``Mapping`` abstract base + class from the standard library so it can be used like a read-only + ``dict``:: + + >>> from bson import encode + >>> raw_doc = RawBSONDocument(encode({'_id': 'my_doc'})) + >>> raw_doc.raw + b'...' + >>> raw_doc['_id'] + 'my_doc' + + :param bson_bytes: the BSON bytes that compose this document + :param codec_options: An instance of + :class:`~bson.codec_options.CodecOptions` whose ``document_class`` + must be :class:`RawBSONDocument`. The default is + :attr:`DEFAULT_RAW_BSON_OPTIONS`. + + .. versionchanged:: 3.8 + :class:`RawBSONDocument` now validates that the ``bson_bytes`` + passed in represent a single bson document. + + .. versionchanged:: 3.5 + If a :class:`~bson.codec_options.CodecOptions` is passed in, its + `document_class` must be :class:`RawBSONDocument`. + """ + self.__raw = bson_bytes + self.__inflated_doc: Optional[Mapping[str, Any]] = None + # Can't default codec_options to DEFAULT_RAW_BSON_OPTIONS in signature, + # it refers to this class RawBSONDocument. + if codec_options is None: + codec_options = DEFAULT_RAW_BSON_OPTIONS + elif not issubclass(codec_options.document_class, RawBSONDocument): + raise TypeError( + "RawBSONDocument cannot use CodecOptions with document " + f"class {codec_options.document_class}" + ) + self.__codec_options = codec_options + # Validate the bson object size. + _get_object_size(bson_bytes, 0, len(bson_bytes)) + + @property + def raw(self) -> bytes | memoryview: + """The raw BSON bytes composing this document.""" + return self.__raw + + def items(self) -> ItemsView[str, Any]: + """Lazily decode and iterate elements in this document.""" + return self.__inflated.items() + + @property + def __inflated(self) -> Mapping[str, Any]: + if self.__inflated_doc is None: + # We already validated the object's size when this document was + # created, so no need to do that again. + self.__inflated_doc = self._inflate_bson(self.__raw, self.__codec_options) + return self.__inflated_doc + + @staticmethod + def _inflate_bson( + bson_bytes: bytes | memoryview, codec_options: CodecOptions[RawBSONDocument] + ) -> Mapping[str, Any]: + return _inflate_bson(bson_bytes, codec_options) + + def __getitem__(self, item: str) -> Any: + return self.__inflated[item] + + def __iter__(self) -> Iterator[str]: + return iter(self.__inflated) + + def __len__(self) -> int: + return len(self.__inflated) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, RawBSONDocument): + return self.__raw == other.raw + return NotImplemented + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.raw!r}, codec_options={self.__codec_options!r})" + + +class _RawArrayBSONDocument(RawBSONDocument): + """A RawBSONDocument that only expands sub-documents and arrays when accessed.""" + + @staticmethod + def _inflate_bson( + bson_bytes: bytes | memoryview, codec_options: CodecOptions[RawBSONDocument] + ) -> Mapping[str, Any]: + return _inflate_bson(bson_bytes, codec_options, raw_array=True) + + +DEFAULT_RAW_BSON_OPTIONS: CodecOptions[RawBSONDocument] = DEFAULT.with_options( + document_class=RawBSONDocument +) +_RAW_ARRAY_BSON_OPTIONS: CodecOptions[_RawArrayBSONDocument] = DEFAULT.with_options( + document_class=_RawArrayBSONDocument +) +"""The default :class:`~bson.codec_options.CodecOptions` for +:class:`RawBSONDocument`. +""" diff --git a/.venv/lib/python3.12/site-packages/bson/regex.py b/.venv/lib/python3.12/site-packages/bson/regex.py new file mode 100644 index 0000000..60cff4f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/regex.py @@ -0,0 +1,133 @@ +# Copyright 2013-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB regular expressions.""" +from __future__ import annotations + +import re +from typing import Any, Generic, Pattern, Type, TypeVar, Union + +from bson._helpers import _getstate_slots, _setstate_slots +from bson.son import RE_TYPE + + +def str_flags_to_int(str_flags: str) -> int: + flags = 0 + if "i" in str_flags: + flags |= re.IGNORECASE + if "l" in str_flags: + flags |= re.LOCALE + if "m" in str_flags: + flags |= re.MULTILINE + if "s" in str_flags: + flags |= re.DOTALL + if "u" in str_flags: + flags |= re.UNICODE + if "x" in str_flags: + flags |= re.VERBOSE + + return flags + + +_T = TypeVar("_T", str, bytes) + + +class Regex(Generic[_T]): + """BSON regular expression data.""" + + __slots__ = ("pattern", "flags") + + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + + _type_marker = 11 + + @classmethod + def from_native(cls: Type[Regex[Any]], regex: Pattern[_T]) -> Regex[_T]: + """Convert a Python regular expression into a ``Regex`` instance. + + Note that in Python 3, a regular expression compiled from a + :class:`str` has the ``re.UNICODE`` flag set. If it is undesirable + to store this flag in a BSON regular expression, unset it first:: + + >>> pattern = re.compile('.*') + >>> regex = Regex.from_native(pattern) + >>> regex.flags ^= re.UNICODE + >>> db.collection.insert_one({'pattern': regex}) + + :param regex: A regular expression object from ``re.compile()``. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. + + .. _PCRE: http://www.pcre.org/ + """ + if not isinstance(regex, RE_TYPE): + raise TypeError("regex must be a compiled regular expression, not %s" % type(regex)) + + return Regex(regex.pattern, regex.flags) + + def __init__(self, pattern: _T, flags: Union[str, int] = 0) -> None: + """BSON regular expression data. + + This class is useful to store and retrieve regular expressions that are + incompatible with Python's regular expression dialect. + + :param pattern: string + :param flags: an integer bitmask, or a string of flag + characters like "im" for IGNORECASE and MULTILINE + """ + if not isinstance(pattern, (str, bytes)): + raise TypeError("pattern must be a string, not %s" % type(pattern)) + self.pattern: _T = pattern + + if isinstance(flags, str): + self.flags = str_flags_to_int(flags) + elif isinstance(flags, int): + self.flags = flags + else: + raise TypeError("flags must be a string or int, not %s" % type(flags)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Regex): + return self.pattern == other.pattern and self.flags == other.flags + else: + return NotImplemented + + __hash__ = None # type: ignore + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __repr__(self) -> str: + return f"Regex({self.pattern!r}, {self.flags!r})" + + def try_compile(self) -> Pattern[_T]: + """Compile this :class:`Regex` as a Python regular expression. + + .. warning:: + Python regular expressions use a different syntax and different + set of flags than MongoDB, which uses `PCRE`_. A regular + expression retrieved from the server may not compile in + Python, or may match a different set of strings in Python than + when used in a MongoDB query. :meth:`try_compile()` may raise + :exc:`re.error`. + + .. _PCRE: http://www.pcre.org/ + """ + return re.compile(self.pattern, self.flags) diff --git a/.venv/lib/python3.12/site-packages/bson/son.py b/.venv/lib/python3.12/site-packages/bson/son.py new file mode 100644 index 0000000..8fd4f95 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/son.py @@ -0,0 +1,211 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for creating and manipulating SON, the Serialized Ocument Notation. + +Regular dictionaries can be used instead of SON objects, but not when the order +of keys is important. A SON object can be used just like a normal Python +dictionary. +""" +from __future__ import annotations + +import copy +import re +from collections.abc import Mapping as _Mapping +from typing import ( + Any, + Dict, + Iterable, + Iterator, + Mapping, + Optional, + Pattern, + Tuple, + Type, + TypeVar, + Union, + cast, +) + +# This sort of sucks, but seems to be as good as it gets... +# This is essentially the same as re._pattern_type +RE_TYPE: Type[Pattern[Any]] = type(re.compile("")) + +_Key = TypeVar("_Key") +_Value = TypeVar("_Value") +_T = TypeVar("_T") + + +class SON(Dict[_Key, _Value]): + """SON data. + + A subclass of dict that maintains ordering of keys and provides a + few extra niceties for dealing with SON. SON provides an API + similar to collections.OrderedDict. + """ + + __keys: list[Any] + + def __init__( + self, + data: Optional[Union[Mapping[_Key, _Value], Iterable[Tuple[_Key, _Value]]]] = None, + **kwargs: Any, + ) -> None: + self.__keys = [] + dict.__init__(self) + self.update(data) + self.update(kwargs) + + def __new__(cls: Type[SON[_Key, _Value]], *args: Any, **kwargs: Any) -> SON[_Key, _Value]: + instance = super().__new__(cls, *args, **kwargs) + instance.__keys = [] + return instance + + def __repr__(self) -> str: + result = [] + for key in self.__keys: + result.append(f"({key!r}, {self[key]!r})") + return "SON([%s])" % ", ".join(result) + + def __setitem__(self, key: _Key, value: _Value) -> None: + if key not in self.__keys: + self.__keys.append(key) + dict.__setitem__(self, key, value) + + def __delitem__(self, key: _Key) -> None: + self.__keys.remove(key) + dict.__delitem__(self, key) + + def copy(self) -> SON[_Key, _Value]: + other: SON[_Key, _Value] = SON() + other.update(self) + return other + + # TODO this is all from UserDict.DictMixin. it could probably be made more + # efficient. + # second level definitions support higher levels + def __iter__(self) -> Iterator[_Key]: + yield from self.__keys + + def has_key(self, key: _Key) -> bool: + return key in self.__keys + + def iterkeys(self) -> Iterator[_Key]: + return self.__iter__() + + # fourth level uses definitions from lower levels + def itervalues(self) -> Iterator[_Value]: + for _, v in self.items(): + yield v + + def values(self) -> list[_Value]: # type: ignore[override] + return [v for _, v in self.items()] + + def clear(self) -> None: + self.__keys = [] + super().clear() + + def setdefault(self, key: _Key, default: _Value) -> _Value: + try: + return self[key] + except KeyError: + self[key] = default + return default + + def pop(self, key: _Key, *args: Union[_Value, _T]) -> Union[_Value, _T]: + if len(args) > 1: + raise TypeError("pop expected at most 2 arguments, got " + repr(1 + len(args))) + try: + value = self[key] + except KeyError: + if args: + return args[0] + raise + del self[key] + return value + + def popitem(self) -> Tuple[_Key, _Value]: + try: + k, v = next(iter(self.items())) + except StopIteration: + raise KeyError("container is empty") from None + del self[k] + return (k, v) + + def update(self, other: Optional[Any] = None, **kwargs: _Value) -> None: + # Make progressively weaker assumptions about "other" + if other is None: + pass + elif hasattr(other, "items"): + for k, v in other.items(): + self[k] = v + elif hasattr(other, "keys"): + for k in other: + self[k] = other[k] + else: + for k, v in other: + self[k] = v + if kwargs: + self.update(kwargs) + + def get( # type: ignore[override] + self, key: _Key, default: Optional[Union[_Value, _T]] = None + ) -> Union[_Value, _T, None]: + try: + return self[key] + except KeyError: + return default + + def __eq__(self, other: Any) -> bool: + """Comparison to another SON is order-sensitive while comparison to a + regular dictionary is order-insensitive. + """ + if isinstance(other, SON): + return len(self) == len(other) and list(self.items()) == list(other.items()) + return cast(bool, self.to_dict() == other) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __len__(self) -> int: + return len(self.__keys) + + def to_dict(self) -> dict[_Key, _Value]: + """Convert a SON document to a normal Python dictionary instance. + + This is trickier than just *dict(...)* because it needs to be + recursive. + """ + + def transform_value(value: Any) -> Any: + if isinstance(value, list): + return [transform_value(v) for v in value] + elif isinstance(value, _Mapping): + return {k: transform_value(v) for k, v in value.items()} + else: + return value + + return cast("dict[_Key, _Value]", transform_value(dict(self))) + + def __deepcopy__(self, memo: dict[int, SON[_Key, _Value]]) -> SON[_Key, _Value]: + out: SON[_Key, _Value] = SON() + val_id = id(self) + if val_id in memo: + return memo[val_id] + memo[val_id] = out + for k, v in self.items(): + if not isinstance(v, RE_TYPE): + v = copy.deepcopy(v, memo) # noqa: PLW2901 + out[k] = v + return out diff --git a/.venv/lib/python3.12/site-packages/bson/time64.c b/.venv/lib/python3.12/site-packages/bson/time64.c new file mode 100644 index 0000000..a21fbb9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/time64.c @@ -0,0 +1,781 @@ +/* + +Copyright (c) 2007-2010 Michael G Schwern + +This software originally derived from Paul Sheer's pivotal_gmtime_r.c. + +The MIT License: + +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. + +*/ + +/* + +Programmers who have available to them 64-bit time values as a 'long +long' type can use cbson_localtime64_r() and cbson_gmtime64_r() which correctly +converts the time even on 32-bit systems. Whether you have 64-bit time +values will depend on the operating system. + +cbson_localtime64_r() is a 64-bit equivalent of localtime_r(). + +cbson_gmtime64_r() is a 64-bit equivalent of gmtime_r(). + +*/ + +#ifdef _MSC_VER + #define _CRT_SECURE_NO_WARNINGS +#endif + +/* Including Python.h fixes issues with interpreters built with -std=c99. */ +#define PY_SSIZE_T_CLEAN +#include "Python.h" + +#include +#include "time64.h" +#include "time64_limits.h" + + +/* Spec says except for stftime() and the _r() functions, these + all return static memory. Stabbings! */ +static struct TM Static_Return_Date; + +static const int days_in_month[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, +}; + +static const int julian_days_by_month[2][12] = { + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, + {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, +}; + +static const int length_of_year[2] = { 365, 366 }; + +/* Some numbers relating to the gregorian cycle */ +static const Year years_in_gregorian_cycle = 400; +#define days_in_gregorian_cycle ((365 * 400) + 100 - 4 + 1) +static const Time64_T seconds_in_gregorian_cycle = days_in_gregorian_cycle * 60LL * 60LL * 24LL; + +/* Year range we can trust the time functions with */ +#define MAX_SAFE_YEAR 2037 +#define MIN_SAFE_YEAR 1971 + +/* 28 year Julian calendar cycle */ +#define SOLAR_CYCLE_LENGTH 28 + +/* Year cycle from MAX_SAFE_YEAR down. */ +static const int safe_years_high[SOLAR_CYCLE_LENGTH] = { + 2016, 2017, 2018, 2019, + 2020, 2021, 2022, 2023, + 2024, 2025, 2026, 2027, + 2028, 2029, 2030, 2031, + 2032, 2033, 2034, 2035, + 2036, 2037, 2010, 2011, + 2012, 2013, 2014, 2015 +}; + +/* Year cycle from MIN_SAFE_YEAR up */ +static const int safe_years_low[SOLAR_CYCLE_LENGTH] = { + 1996, 1997, 1998, 1971, + 1972, 1973, 1974, 1975, + 1976, 1977, 1978, 1979, + 1980, 1981, 1982, 1983, + 1984, 1985, 1986, 1987, + 1988, 1989, 1990, 1991, + 1992, 1993, 1994, 1995, +}; + +/* Let's assume people are going to be looking for dates in the future. + Let's provide some cheats so you can skip ahead. + This has a 4x speed boost when near 2008. +*/ +/* Number of days since epoch on Jan 1st, 2008 GMT */ +#define CHEAT_DAYS (1199145600 / 24 / 60 / 60) +#define CHEAT_YEARS 108 + +#define IS_LEAP(n) ((!(((n) + 1900) % 400) || (!(((n) + 1900) % 4) && (((n) + 1900) % 100))) != 0) +#define _TIME64_WRAP(a,b,m) ((a) = ((a) < 0 ) ? ((b)--, (a) + (m)) : (a)) + +#ifdef USE_SYSTEM_LOCALTIME +# define SHOULD_USE_SYSTEM_LOCALTIME(a) ( \ + (a) <= SYSTEM_LOCALTIME_MAX && \ + (a) >= SYSTEM_LOCALTIME_MIN \ +) +#else +# define SHOULD_USE_SYSTEM_LOCALTIME(a) (0) +#endif + +#ifdef USE_SYSTEM_GMTIME +# define SHOULD_USE_SYSTEM_GMTIME(a) ( \ + (a) <= SYSTEM_GMTIME_MAX && \ + (a) >= SYSTEM_GMTIME_MIN \ +) +#else +# define SHOULD_USE_SYSTEM_GMTIME(a) (0) +#endif + +/* Multi varadic macros are a C99 thing, alas */ +#ifdef TIME_64_DEBUG +# define TIME64_TRACE(format) (fprintf(stderr, format)) +# define TIME64_TRACE1(format, var1) (fprintf(stderr, format, var1)) +# define TIME64_TRACE2(format, var1, var2) (fprintf(stderr, format, var1, var2)) +# define TIME64_TRACE3(format, var1, var2, var3) (fprintf(stderr, format, var1, var2, var3)) +#else +# define TIME64_TRACE(format) ((void)0) +# define TIME64_TRACE1(format, var1) ((void)0) +# define TIME64_TRACE2(format, var1, var2) ((void)0) +# define TIME64_TRACE3(format, var1, var2, var3) ((void)0) +#endif + + +static int is_exception_century(Year year) +{ + int is_exception = ((year % 100 == 0) && !(year % 400 == 0)); + TIME64_TRACE1("# is_exception_century: %s\n", is_exception ? "yes" : "no"); + + return(is_exception); +} + + +/* Compare two dates. + The result is like cmp. + Ignores things like gmtoffset and dst +*/ +int cbson_cmp_date( const struct TM* left, const struct tm* right ) { + if( left->tm_year > right->tm_year ) + return 1; + else if( left->tm_year < right->tm_year ) + return -1; + + if( left->tm_mon > right->tm_mon ) + return 1; + else if( left->tm_mon < right->tm_mon ) + return -1; + + if( left->tm_mday > right->tm_mday ) + return 1; + else if( left->tm_mday < right->tm_mday ) + return -1; + + if( left->tm_hour > right->tm_hour ) + return 1; + else if( left->tm_hour < right->tm_hour ) + return -1; + + if( left->tm_min > right->tm_min ) + return 1; + else if( left->tm_min < right->tm_min ) + return -1; + + if( left->tm_sec > right->tm_sec ) + return 1; + else if( left->tm_sec < right->tm_sec ) + return -1; + + return 0; +} + + +/* Check if a date is safely inside a range. + The intention is to check if its a few days inside. +*/ +int cbson_date_in_safe_range( const struct TM* date, const struct tm* min, const struct tm* max ) { + if( cbson_cmp_date(date, min) == -1 ) + return 0; + + if( cbson_cmp_date(date, max) == 1 ) + return 0; + + return 1; +} + + +/* timegm() is not in the C or POSIX spec, but it is such a useful + extension I would be remiss in leaving it out. Also I need it + for cbson_localtime64() +*/ +Time64_T cbson_timegm64(const struct TM *date) { + Time64_T days = 0; + Time64_T seconds = 0; + Year year; + Year orig_year = (Year)date->tm_year; + int cycles = 0; + + if( orig_year > 100 ) { + cycles = (int)((orig_year - 100) / 400); + orig_year -= cycles * 400; + days += (Time64_T)cycles * days_in_gregorian_cycle; + } + else if( orig_year < -300 ) { + cycles = (int)((orig_year - 100) / 400); + orig_year -= cycles * 400; + days += (Time64_T)cycles * days_in_gregorian_cycle; + } + TIME64_TRACE3("# timegm/ cycles: %d, days: %lld, orig_year: %lld\n", cycles, days, orig_year); + + if( orig_year > 70 ) { + year = 70; + while( year < orig_year ) { + days += length_of_year[IS_LEAP(year)]; + year++; + } + } + else if ( orig_year < 70 ) { + year = 69; + do { + days -= length_of_year[IS_LEAP(year)]; + year--; + } while( year >= orig_year ); + } + + days += julian_days_by_month[IS_LEAP(orig_year)][date->tm_mon]; + days += date->tm_mday - 1; + + seconds = days * 60 * 60 * 24; + + seconds += date->tm_hour * 60 * 60; + seconds += date->tm_min * 60; + seconds += date->tm_sec; + + return(seconds); +} + + +#ifndef NDEBUG +static int check_tm(struct TM *tm) +{ + /* Don't forget leap seconds */ + assert(tm->tm_sec >= 0); + assert(tm->tm_sec <= 61); + + assert(tm->tm_min >= 0); + assert(tm->tm_min <= 59); + + assert(tm->tm_hour >= 0); + assert(tm->tm_hour <= 23); + + assert(tm->tm_mday >= 1); + assert(tm->tm_mday <= days_in_month[IS_LEAP(tm->tm_year)][tm->tm_mon]); + + assert(tm->tm_mon >= 0); + assert(tm->tm_mon <= 11); + + assert(tm->tm_wday >= 0); + assert(tm->tm_wday <= 6); + + assert(tm->tm_yday >= 0); + assert(tm->tm_yday <= length_of_year[IS_LEAP(tm->tm_year)]); + +#ifdef HAS_TM_TM_GMTOFF + assert(tm->tm_gmtoff >= -24 * 60 * 60); + assert(tm->tm_gmtoff <= 24 * 60 * 60); +#endif + + return 1; +} +#endif + + +/* The exceptional centuries without leap years cause the cycle to + shift by 16 +*/ +static Year cycle_offset(Year year) +{ + const Year start_year = 2000; + Year year_diff = year - start_year; + Year exceptions; + + if( year > start_year ) + year_diff--; + + exceptions = year_diff / 100; + exceptions -= year_diff / 400; + + TIME64_TRACE3("# year: %lld, exceptions: %lld, year_diff: %lld\n", + year, exceptions, year_diff); + + return exceptions * 16; +} + +/* For a given year after 2038, pick the latest possible matching + year in the 28 year calendar cycle. + + A matching year... + 1) Starts on the same day of the week. + 2) Has the same leap year status. + + This is so the calendars match up. + + Also the previous year must match. When doing Jan 1st you might + wind up on Dec 31st the previous year when doing a -UTC time zone. + + Finally, the next year must have the same start day of week. This + is for Dec 31st with a +UTC time zone. + It doesn't need the same leap year status since we only care about + January 1st. +*/ +static int safe_year(const Year year) +{ + int safe_year = 0; + Year year_cycle; + + if( year >= MIN_SAFE_YEAR && year <= MAX_SAFE_YEAR ) { + return (int)year; + } + + year_cycle = year + cycle_offset(year); + + /* safe_years_low is off from safe_years_high by 8 years */ + if( year < MIN_SAFE_YEAR ) + year_cycle -= 8; + + /* Change non-leap xx00 years to an equivalent */ + if( is_exception_century(year) ) + year_cycle += 11; + + /* Also xx01 years, since the previous year will be wrong */ + if( is_exception_century(year - 1) ) + year_cycle += 17; + + year_cycle %= SOLAR_CYCLE_LENGTH; + if( year_cycle < 0 ) + year_cycle = SOLAR_CYCLE_LENGTH + year_cycle; + + assert( year_cycle >= 0 ); + assert( year_cycle < SOLAR_CYCLE_LENGTH ); + if( year < MIN_SAFE_YEAR ) + safe_year = safe_years_low[year_cycle]; + else if( year > MAX_SAFE_YEAR ) + safe_year = safe_years_high[year_cycle]; + else + assert(0); + + TIME64_TRACE3("# year: %lld, year_cycle: %lld, safe_year: %d\n", + year, year_cycle, safe_year); + + assert(safe_year <= MAX_SAFE_YEAR && safe_year >= MIN_SAFE_YEAR); + + return safe_year; +} + + +void pymongo_copy_tm_to_TM64(const struct tm *src, struct TM *dest) { + if( src == NULL ) { + memset(dest, 0, sizeof(*dest)); + } + else { +# ifdef USE_TM64 + dest->tm_sec = src->tm_sec; + dest->tm_min = src->tm_min; + dest->tm_hour = src->tm_hour; + dest->tm_mday = src->tm_mday; + dest->tm_mon = src->tm_mon; + dest->tm_year = (Year)src->tm_year; + dest->tm_wday = src->tm_wday; + dest->tm_yday = src->tm_yday; + dest->tm_isdst = src->tm_isdst; + +# ifdef HAS_TM_TM_GMTOFF + dest->tm_gmtoff = src->tm_gmtoff; +# endif + +# ifdef HAS_TM_TM_ZONE + dest->tm_zone = src->tm_zone; +# endif + +# else + /* They're the same type */ + memcpy(dest, src, sizeof(*dest)); +# endif + } +} + + +void cbson_copy_TM64_to_tm(const struct TM *src, struct tm *dest) { + if( src == NULL ) { + memset(dest, 0, sizeof(*dest)); + } + else { +# ifdef USE_TM64 + dest->tm_sec = src->tm_sec; + dest->tm_min = src->tm_min; + dest->tm_hour = src->tm_hour; + dest->tm_mday = src->tm_mday; + dest->tm_mon = src->tm_mon; + dest->tm_year = (int)src->tm_year; + dest->tm_wday = src->tm_wday; + dest->tm_yday = src->tm_yday; + dest->tm_isdst = src->tm_isdst; + +# ifdef HAS_TM_TM_GMTOFF + dest->tm_gmtoff = src->tm_gmtoff; +# endif + +# ifdef HAS_TM_TM_ZONE + dest->tm_zone = src->tm_zone; +# endif + +# else + /* They're the same type */ + memcpy(dest, src, sizeof(*dest)); +# endif + } +} + + +/* Simulate localtime_r() to the best of our ability */ +struct tm * cbson_fake_localtime_r(const time_t *time, struct tm *result) { + const struct tm *static_result = localtime(time); + + assert(result != NULL); + + if( static_result == NULL ) { + memset(result, 0, sizeof(*result)); + return NULL; + } + else { + memcpy(result, static_result, sizeof(*result)); + return result; + } +} + + +/* Simulate gmtime_r() to the best of our ability */ +struct tm * cbson_fake_gmtime_r(const time_t *time, struct tm *result) { + const struct tm *static_result = gmtime(time); + + assert(result != NULL); + + if( static_result == NULL ) { + memset(result, 0, sizeof(*result)); + return NULL; + } + else { + memcpy(result, static_result, sizeof(*result)); + return result; + } +} + + +static Time64_T seconds_between_years(Year left_year, Year right_year) { + int increment = (left_year > right_year) ? 1 : -1; + Time64_T seconds = 0; + int cycles; + + if( left_year > 2400 ) { + cycles = (int)((left_year - 2400) / 400); + left_year -= cycles * 400; + seconds += cycles * seconds_in_gregorian_cycle; + } + else if( left_year < 1600 ) { + cycles = (int)((left_year - 1600) / 400); + left_year += cycles * 400; + seconds += cycles * seconds_in_gregorian_cycle; + } + + while( left_year != right_year ) { + seconds += length_of_year[IS_LEAP(right_year - 1900)] * 60 * 60 * 24; + right_year += increment; + } + + return seconds * increment; +} + + +Time64_T cbson_mktime64(const struct TM *input_date) { + struct tm safe_date; + struct TM date; + Time64_T time; + Year year = input_date->tm_year + 1900; + + if( cbson_date_in_safe_range(input_date, &SYSTEM_MKTIME_MIN, &SYSTEM_MKTIME_MAX) ) + { + cbson_copy_TM64_to_tm(input_date, &safe_date); + return (Time64_T)mktime(&safe_date); + } + + /* Have to make the year safe in date else it won't fit in safe_date */ + date = *input_date; + date.tm_year = safe_year(year) - 1900; + cbson_copy_TM64_to_tm(&date, &safe_date); + + time = (Time64_T)mktime(&safe_date); + + time += seconds_between_years(year, (Year)(safe_date.tm_year + 1900)); + + return time; +} + + +/* Because I think mktime() is a crappy name */ +Time64_T timelocal64(const struct TM *date) { + return cbson_mktime64(date); +} + + +struct TM *cbson_gmtime64_r (const Time64_T *in_time, struct TM *p) +{ + int v_tm_sec, v_tm_min, v_tm_hour, v_tm_mon, v_tm_wday; + Time64_T v_tm_tday; + int leap; + Time64_T m; + Time64_T time = *in_time; + Year year = 70; + int cycles = 0; + + assert(p != NULL); + +#ifdef USE_SYSTEM_GMTIME + /* Use the system gmtime() if time_t is small enough */ + if( SHOULD_USE_SYSTEM_GMTIME(*in_time) ) { + time_t safe_time = (time_t)*in_time; + struct tm safe_date; + GMTIME_R(&safe_time, &safe_date); + + pymongo_copy_tm_to_TM64(&safe_date, p); + assert(check_tm(p)); + + return p; + } +#endif + +#ifdef HAS_TM_TM_GMTOFF + p->tm_gmtoff = 0; +#endif + p->tm_isdst = 0; + +#ifdef HAS_TM_TM_ZONE + p->tm_zone = "UTC"; +#endif + + v_tm_sec = (int)(time % 60); + time /= 60; + v_tm_min = (int)(time % 60); + time /= 60; + v_tm_hour = (int)(time % 24); + time /= 24; + v_tm_tday = time; + + _TIME64_WRAP (v_tm_sec, v_tm_min, 60); + _TIME64_WRAP (v_tm_min, v_tm_hour, 60); + _TIME64_WRAP (v_tm_hour, v_tm_tday, 24); + + v_tm_wday = (int)((v_tm_tday + 4) % 7); + if (v_tm_wday < 0) + v_tm_wday += 7; + m = v_tm_tday; + + if (m >= CHEAT_DAYS) { + year = CHEAT_YEARS; + m -= CHEAT_DAYS; + } + + if (m >= 0) { + /* Gregorian cycles, this is huge optimization for distant times */ + cycles = (int)(m / (Time64_T) days_in_gregorian_cycle); + if( cycles ) { + m -= (cycles * (Time64_T) days_in_gregorian_cycle); + year += (cycles * years_in_gregorian_cycle); + } + + /* Years */ + leap = IS_LEAP (year); + while (m >= (Time64_T) length_of_year[leap]) { + m -= (Time64_T) length_of_year[leap]; + year++; + leap = IS_LEAP (year); + } + + /* Months */ + v_tm_mon = 0; + while (m >= (Time64_T) days_in_month[leap][v_tm_mon]) { + m -= (Time64_T) days_in_month[leap][v_tm_mon]; + v_tm_mon++; + } + } else { + year--; + + /* Gregorian cycles */ + cycles = (int)((m / (Time64_T) days_in_gregorian_cycle) + 1); + if( cycles ) { + m -= (cycles * (Time64_T) days_in_gregorian_cycle); + year += (cycles * years_in_gregorian_cycle); + } + + /* Years */ + leap = IS_LEAP (year); + while (m < (Time64_T) -length_of_year[leap]) { + m += (Time64_T) length_of_year[leap]; + year--; + leap = IS_LEAP (year); + } + + /* Months */ + v_tm_mon = 11; + while (m < (Time64_T) -days_in_month[leap][v_tm_mon]) { + m += (Time64_T) days_in_month[leap][v_tm_mon]; + v_tm_mon--; + } + m += (Time64_T) days_in_month[leap][v_tm_mon]; + } + + p->tm_year = (int)year; + if( p->tm_year != year ) { +#ifdef EOVERFLOW + errno = EOVERFLOW; +#endif + return NULL; + } + + /* At this point m is less than a year so casting to an int is safe */ + p->tm_mday = (int) m + 1; + p->tm_yday = julian_days_by_month[leap][v_tm_mon] + (int)m; + p->tm_sec = v_tm_sec; + p->tm_min = v_tm_min; + p->tm_hour = v_tm_hour; + p->tm_mon = v_tm_mon; + p->tm_wday = v_tm_wday; + + assert(check_tm(p)); + + return p; +} + + +struct TM *cbson_localtime64_r (const Time64_T *time, struct TM *local_tm) +{ + time_t safe_time; + struct tm safe_date; + struct TM gm_tm; + Year orig_year; + int month_diff; + + assert(local_tm != NULL); + +#ifdef USE_SYSTEM_LOCALTIME + /* Use the system localtime() if time_t is small enough */ + if( SHOULD_USE_SYSTEM_LOCALTIME(*time) ) { + safe_time = (time_t)*time; + + TIME64_TRACE1("Using system localtime for %lld\n", *time); + + LOCALTIME_R(&safe_time, &safe_date); + + pymongo_copy_tm_to_TM64(&safe_date, local_tm); + assert(check_tm(local_tm)); + + return local_tm; + } +#endif + + if( cbson_gmtime64_r(time, &gm_tm) == NULL ) { + TIME64_TRACE1("cbson_gmtime64_r returned null for %lld\n", *time); + return NULL; + } + + orig_year = gm_tm.tm_year; + + if (gm_tm.tm_year > (2037 - 1900) || + gm_tm.tm_year < (1970 - 1900) + ) + { + TIME64_TRACE1("Mapping tm_year %lld to safe_year\n", (Year)gm_tm.tm_year); + gm_tm.tm_year = safe_year((Year)(gm_tm.tm_year + 1900)) - 1900; + } + + safe_time = (time_t)cbson_timegm64(&gm_tm); + if( LOCALTIME_R(&safe_time, &safe_date) == NULL ) { + TIME64_TRACE1("localtime_r(%d) returned NULL\n", (int)safe_time); + return NULL; + } + + pymongo_copy_tm_to_TM64(&safe_date, local_tm); + + local_tm->tm_year = (int)orig_year; + if( local_tm->tm_year != orig_year ) { + TIME64_TRACE2("tm_year overflow: tm_year %lld, orig_year %lld\n", + (Year)local_tm->tm_year, (Year)orig_year); + +#ifdef EOVERFLOW + errno = EOVERFLOW; +#endif + return NULL; + } + + + month_diff = local_tm->tm_mon - gm_tm.tm_mon; + + /* When localtime is Dec 31st previous year and + gmtime is Jan 1st next year. + */ + if( month_diff == 11 ) { + local_tm->tm_year--; + } + + /* When localtime is Jan 1st, next year and + gmtime is Dec 31st, previous year. + */ + if( month_diff == -11 ) { + local_tm->tm_year++; + } + + /* GMT is Jan 1st, xx01 year, but localtime is still Dec 31st + in a non-leap xx00. There is one point in the cycle + we can't account for which the safe xx00 year is a leap + year. So we need to correct for Dec 31st coming out as + the 366th day of the year. + */ + if( !IS_LEAP(local_tm->tm_year) && local_tm->tm_yday == 365 ) + local_tm->tm_yday--; + + assert(check_tm(local_tm)); + + return local_tm; +} + + +int cbson_valid_tm_wday( const struct TM* date ) { + if( 0 <= date->tm_wday && date->tm_wday <= 6 ) + return 1; + else + return 0; +} + +int cbson_valid_tm_mon( const struct TM* date ) { + if( 0 <= date->tm_mon && date->tm_mon <= 11 ) + return 1; + else + return 0; +} + + +/* Non-thread safe versions of the above */ +struct TM *cbson_localtime64(const Time64_T *time) { +#ifdef _MSC_VER + _tzset(); +#else + tzset(); +#endif + return cbson_localtime64_r(time, &Static_Return_Date); +} + +struct TM *cbson_gmtime64(const Time64_T *time) { + return cbson_gmtime64_r(time, &Static_Return_Date); +} diff --git a/.venv/lib/python3.12/site-packages/bson/time64.h b/.venv/lib/python3.12/site-packages/bson/time64.h new file mode 100644 index 0000000..6321eb3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/time64.h @@ -0,0 +1,67 @@ +#ifndef TIME64_H +# define TIME64_H + +#include +#include "time64_config.h" + +/* Set our custom types */ +typedef INT_64_T Int64; +typedef Int64 Time64_T; +typedef Int64 Year; + + +/* A copy of the tm struct but with a 64 bit year */ +struct TM64 { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + Year tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + +#ifdef HAS_TM_TM_GMTOFF + long tm_gmtoff; +#endif + +#ifdef HAS_TM_TM_ZONE + char *tm_zone; +#endif +}; + + +/* Decide which tm struct to use */ +#ifdef USE_TM64 +#define TM TM64 +#else +#define TM tm +#endif + + +/* Declare public functions */ +struct TM *cbson_gmtime64_r (const Time64_T *, struct TM *); +struct TM *cbson_localtime64_r (const Time64_T *, struct TM *); +struct TM *cbson_gmtime64 (const Time64_T *); +struct TM *cbson_localtime64 (const Time64_T *); + +Time64_T cbson_timegm64 (const struct TM *); +Time64_T cbson_mktime64 (const struct TM *); +Time64_T timelocal64 (const struct TM *); + + +/* Not everyone has gm/localtime_r(), provide a replacement */ +#ifdef HAS_LOCALTIME_R +# define LOCALTIME_R(clock, result) localtime_r(clock, result) +#else +# define LOCALTIME_R(clock, result) cbson_fake_localtime_r(clock, result) +#endif +#ifdef HAS_GMTIME_R +# define GMTIME_R(clock, result) gmtime_r(clock, result) +#else +# define GMTIME_R(clock, result) cbson_fake_gmtime_r(clock, result) +#endif + + +#endif diff --git a/.venv/lib/python3.12/site-packages/bson/time64_config.h b/.venv/lib/python3.12/site-packages/bson/time64_config.h new file mode 100644 index 0000000..9d4c111 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/time64_config.h @@ -0,0 +1,78 @@ +/* Configuration + ------------- + Define as appropriate for your system. + Sensible defaults provided. +*/ + + +#ifndef TIME64_CONFIG_H +# define TIME64_CONFIG_H + +/* Debugging + TIME_64_DEBUG + Define if you want debugging messages +*/ +/* #define TIME_64_DEBUG */ + + +/* INT_64_T + A 64 bit integer type to use to store time and others. + Must be defined. +*/ +#define INT_64_T long long + + +/* USE_TM64 + Should we use a 64 bit safe replacement for tm? This will + let you go past year 2 billion but the struct will be incompatible + with tm. Conversion functions will be provided. +*/ +/* #define USE_TM64 */ + + +/* Availability of system functions. + + HAS_GMTIME_R + Define if your system has gmtime_r() + + HAS_LOCALTIME_R + Define if your system has localtime_r() + + HAS_TIMEGM + Define if your system has timegm(), a GNU extension. +*/ +#if !defined(WIN32) && !defined(_MSC_VER) +#define HAS_GMTIME_R +#define HAS_LOCALTIME_R +#endif +/* #define HAS_TIMEGM */ + + +/* Details of non-standard tm struct elements. + + HAS_TM_TM_GMTOFF + True if your tm struct has a "tm_gmtoff" element. + A BSD extension. + + HAS_TM_TM_ZONE + True if your tm struct has a "tm_zone" element. + A BSD extension. +*/ +/* #define HAS_TM_TM_GMTOFF */ +/* #define HAS_TM_TM_ZONE */ + + +/* USE_SYSTEM_LOCALTIME + USE_SYSTEM_GMTIME + USE_SYSTEM_MKTIME + USE_SYSTEM_TIMEGM + Should we use the system functions if the time is inside their range? + Your system localtime() is probably more accurate, but our gmtime() is + fast and safe. +*/ +#define USE_SYSTEM_LOCALTIME +/* #define USE_SYSTEM_GMTIME */ +#define USE_SYSTEM_MKTIME +/* #define USE_SYSTEM_TIMEGM */ + +#endif /* TIME64_CONFIG_H */ diff --git a/.venv/lib/python3.12/site-packages/bson/time64_limits.h b/.venv/lib/python3.12/site-packages/bson/time64_limits.h new file mode 100644 index 0000000..1d30607 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/time64_limits.h @@ -0,0 +1,95 @@ +/* + Maximum and minimum inputs your system's respective time functions + can correctly handle. time64.h will use your system functions if + the input falls inside these ranges and corresponding USE_SYSTEM_* + constant is defined. +*/ + +#ifndef TIME64_LIMITS_H +#define TIME64_LIMITS_H + +/* Max/min for localtime() */ +#define SYSTEM_LOCALTIME_MAX 2147483647 +#define SYSTEM_LOCALTIME_MIN -2147483647-1 + +/* Max/min for gmtime() */ +#define SYSTEM_GMTIME_MAX 2147483647 +#define SYSTEM_GMTIME_MIN -2147483647-1 + +/* Max/min for mktime() */ +static const struct tm SYSTEM_MKTIME_MAX = { + 7, + 14, + 19, + 18, + 0, + 138, + 1, + 17, + 0 +#ifdef HAS_TM_TM_GMTOFF + ,-28800 +#endif +#ifdef HAS_TM_TM_ZONE + ,"PST" +#endif +}; + +static const struct tm SYSTEM_MKTIME_MIN = { + 52, + 45, + 12, + 13, + 11, + 1, + 5, + 346, + 0 +#ifdef HAS_TM_TM_GMTOFF + ,-28800 +#endif +#ifdef HAS_TM_TM_ZONE + ,"PST" +#endif +}; + +/* Max/min for timegm() */ +#ifdef HAS_TIMEGM +static const struct tm SYSTEM_TIMEGM_MAX = { + 7, + 14, + 3, + 19, + 0, + 138, + 2, + 18, + 0 + #ifdef HAS_TM_TM_GMTOFF + ,0 + #endif + #ifdef HAS_TM_TM_ZONE + ,"UTC" + #endif +}; + +static const struct tm SYSTEM_TIMEGM_MIN = { + 52, + 45, + 20, + 13, + 11, + 1, + 5, + 346, + 0 + #ifdef HAS_TM_TM_GMTOFF + ,0 + #endif + #ifdef HAS_TM_TM_ZONE + ,"UTC" + #endif +}; +#endif /* HAS_TIMEGM */ + +#endif /* TIME64_LIMITS_H */ diff --git a/.venv/lib/python3.12/site-packages/bson/timestamp.py b/.venv/lib/python3.12/site-packages/bson/timestamp.py new file mode 100644 index 0000000..949bd7b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/timestamp.py @@ -0,0 +1,123 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing MongoDB internal Timestamps.""" +from __future__ import annotations + +import calendar +import datetime +from typing import Any, Union + +from bson._helpers import _getstate_slots, _setstate_slots +from bson.tz_util import utc + +UPPERBOUND = 4294967296 + + +class Timestamp: + """MongoDB internal timestamps used in the opLog.""" + + __slots__ = ("__time", "__inc") + + __getstate__ = _getstate_slots + __setstate__ = _setstate_slots + + _type_marker = 17 + + def __init__(self, time: Union[datetime.datetime, int], inc: int) -> None: + """Create a new :class:`Timestamp`. + + This class is only for use with the MongoDB opLog. If you need + to store a regular timestamp, please use a + :class:`~datetime.datetime`. + + Raises :class:`TypeError` if `time` is not an instance of + :class: `int` or :class:`~datetime.datetime`, or `inc` is not + an instance of :class:`int`. Raises :class:`ValueError` if + `time` or `inc` is not in [0, 2**32). + + :param time: time in seconds since epoch UTC, or a naive UTC + :class:`~datetime.datetime`, or an aware + :class:`~datetime.datetime` + :param inc: the incrementing counter + """ + if isinstance(time, datetime.datetime): + offset = time.utcoffset() + if offset is not None: + time = time - offset + time = int(calendar.timegm(time.timetuple())) + if not isinstance(time, int): + raise TypeError(f"time must be an instance of int, not {type(time)}") + if not isinstance(inc, int): + raise TypeError(f"inc must be an instance of int, not {type(inc)}") + if not 0 <= time < UPPERBOUND: + raise ValueError("time must be contained in [0, 2**32)") + if not 0 <= inc < UPPERBOUND: + raise ValueError("inc must be contained in [0, 2**32)") + + self.__time = time + self.__inc = inc + + @property + def time(self) -> int: + """Get the time portion of this :class:`Timestamp`.""" + return self.__time + + @property + def inc(self) -> int: + """Get the inc portion of this :class:`Timestamp`.""" + return self.__inc + + def __eq__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return self.__time == other.time and self.__inc == other.inc + else: + return NotImplemented + + def __hash__(self) -> int: + return hash(self.time) ^ hash(self.inc) + + def __ne__(self, other: Any) -> bool: + return not self == other + + def __lt__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) < (other.time, other.inc) + return NotImplemented + + def __le__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) <= (other.time, other.inc) + return NotImplemented + + def __gt__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) > (other.time, other.inc) + return NotImplemented + + def __ge__(self, other: Any) -> bool: + if isinstance(other, Timestamp): + return (self.time, self.inc) >= (other.time, other.inc) + return NotImplemented + + def __repr__(self) -> str: + return f"Timestamp({self.__time}, {self.__inc})" + + def as_datetime(self) -> datetime.datetime: + """Return a :class:`~datetime.datetime` instance corresponding + to the time portion of this :class:`Timestamp`. + + The returned datetime's timezone is UTC. + """ + return datetime.datetime.fromtimestamp(self.__time, utc) diff --git a/.venv/lib/python3.12/site-packages/bson/typings.py b/.venv/lib/python3.12/site-packages/bson/typings.py new file mode 100644 index 0000000..5913860 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/typings.py @@ -0,0 +1,31 @@ +# Copyright 2023-Present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Type aliases used by bson""" +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Mapping, MutableMapping, TypeVar, Union + +if TYPE_CHECKING: + from array import array + from mmap import mmap + + from bson.raw_bson import RawBSONDocument + + +# Common Shared Types. +_DocumentOut = Union[MutableMapping[str, Any], "RawBSONDocument"] +_DocumentType = TypeVar("_DocumentType", bound=Mapping[str, Any]) +_DocumentTypeArg = TypeVar("_DocumentTypeArg", bound=Mapping[str, Any]) +_ReadableBuffer = Union[bytes, memoryview, bytearray, "mmap", "array"] # type: ignore[type-arg] diff --git a/.venv/lib/python3.12/site-packages/bson/tz_util.py b/.venv/lib/python3.12/site-packages/bson/tz_util.py new file mode 100644 index 0000000..4d31c04 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/bson/tz_util.py @@ -0,0 +1,56 @@ +# Copyright 2010-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Timezone related utilities for BSON.""" +from __future__ import annotations + +from datetime import datetime, timedelta, tzinfo +from typing import Optional, Tuple, Union + +ZERO: timedelta = timedelta(0) + + +class FixedOffset(tzinfo): + """Fixed offset timezone, in minutes east from UTC. + + Implementation based from the Python `standard library documentation + `_. + Defining __getinitargs__ enables pickling / copying. + """ + + def __init__(self, offset: Union[float, timedelta], name: str) -> None: + if isinstance(offset, timedelta): + self.__offset = offset + else: + self.__offset = timedelta(minutes=offset) + self.__name = name + + def __getinitargs__(self) -> Tuple[timedelta, str]: + return self.__offset, self.__name + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.__offset!r}, {self.__name!r})" + + def utcoffset(self, dt: Optional[datetime]) -> timedelta: + return self.__offset + + def tzname(self, dt: Optional[datetime]) -> str: + return self.__name + + def dst(self, dt: Optional[datetime]) -> timedelta: + return ZERO + + +utc: FixedOffset = FixedOffset(0, "UTC") +"""Fixed offset timezone representing UTC.""" diff --git a/.venv/lib/python3.12/site-packages/dns/__init__.py b/.venv/lib/python3.12/site-packages/dns/__init__.py new file mode 100644 index 0000000..d30fd74 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/__init__.py @@ -0,0 +1,72 @@ +# 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", + "btree", + "btreezone", + "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 new file mode 100644 index 0000000000000000000000000000000000000000..29e32f722946a4808f58e3b9a300402835a93bb3 GIT binary patch literal 757 zcmXw1KabNe6i=F@Xk6N0TvDg-pa%VoWyVwFG;cF-~0L5@4s)4j>ZA4$F~Fdbr=Lc+Qa`fIFP$zB3}X#EP^r+ z;kR%Rih+pKpo$i87&P%T**_H8)s0^peR6sA;lvxGHquY$_<*}c7fW7jKnomqr|T`) z29G*!!5Os!i^ji4M%U9>XkbB?dK zvcSalIxD#6IPOHDxN|t_xT6?Z$J#teNu`6O8i^gMomilv9dtnxOU&cYft6fg)Y*GO z9g};qnt02#<3(3f?42x0MYw^+N?l^)y~5OrzA3|PO=$t9t^c+Q*@1w-_>;7N(ap+Y z+_gAsv9}wwbR`x1Y{xzg?Nbt0(*)Bz7pBPbmT;wrv_qI3ymrrUlIMqWo_`PSS`YVv ztxW~YaaVE8-dXbz3h&r^vou~7j=eRyG?#BqUpuNrS2RCHPW6r{xn%ucdN!M%F(*Bo z*7SjBN{&%zmgiDSpXal>xgWi(jMyr8WiKdbNZUOk_!Wg=`1?r^9{-8YCWFuE-*p&- I%)X$ze`xy??*IS* literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e575b78f266a5d309081c7b9a95aa294e468354c GIT binary patch literal 4862 zcmb_f-ESMm5#K%Dkw=jf6-#m~OEoAZMa!fX^U*dfng*`xq)3AhZCw;7i}rH9TgjA< zcg*flHd#Lyj!?L0ismWGpHRW*Uy_%!KoR4Rx4fipw$FeYKXqpJ?s&YgZtQdg4fkfh zemgV!+u08rjRgW_^FyugR0;VMKhk0(mG~M|4v9v7LR@k{TzXSeEj^{vKv&9iRQA(B zXE1uRl=Rf3whY=zPFs>%6SUQAye@SMpsQs%V_a`H95t1a2?7#-3%(BtBR%Spp5|6u z?SS@5Zs~yZbQe(c4A%fzc46&aWlY;|cSPsSz~2qdojv^{A^N-i0Osp#?=yeD*2{0kk?583IV?Od{V zndrE_=vcNG`_2VWG2h)Djw92u`hGvOtjS7lSKF!9L{#YEYvk6(H*e_wG}mJIA`V5F z6s*f#pM(nRn(U=5x!?1IV?6LOh$G77#5l0UA#6xgwJc>8YqB~EBvIF(mskV=NSezZ zH(P&iwmvdj9QBS4B3~pLR34JCTK9+?>2lG?WLF$391mGk#oE5@ji4HG+hJ%Ifx}u* z6fUq2yE4x{N{fI5?h8o1c!SV~nCEY$%XDfY-hcrjXP^bkvQrBk zpS4h`e*%YKBuwJ*Uw@0?8S~$iXM+@^+6i=d9Lm`2o z_OifM>elt1)|6mS2bXc zERd2IZfkrK`f`k=c1cF_R3UJ#G;F2mCQXGQJIr5$9s+F#!Et=!hW>8t*2;++`k$*0 ziQ9)nMlnJS#|s3T)RR@^m&A*-z|k!;tdRxZacEITBZQhN*J&1pz?jS3Abfo=^cYed zb6@2=;9oc(wOtkt8RHl{lVb^pobG9uImYeCp!{TU^CNRJiOWirJ{C0o4VZ2&5CPsV z0!ejp&@(vlSrA{iKi&al{6B0W7lv>^7L6)lf53uKnA{$Uw^D03jE6;QpiZto1(Ags zOqe+ak6y}I=Zm~h@V-2qlP40hPr2$e%pvforkr%6mXJ}&m?a~OU%B@)P)V2F%L4;F zmYeDaI=kfeFzS8nu=EGR0CF9Zm&m(D7CCLozYcxpP?)5V$x;rjA@5 zW$%xBfAT&yPyWL^`9NUsTwggw-=`b;Vfhs8wrhL?7r=wXPl1R^eLqyZC0A}^1Db&2 z)c>8qTG)yLrxP!NI3Tw*%~<x$WM76<;bl3Fl6 z0jfN+0apRHfz-#PwjM2>vqO8A+XH2l`4`2Pip%7p)h}PP!>Hym5%zsM><2#Y0EEH? zTNaRsgu!HWcKL0k{tgV_(=f#tNlO-{Uk1~WqD#oN`V*#L;9MzRMkZ&@{tgcYlP3!} z(uVIr_bHNY+bFo6e;+&i5Jlm7l6ye$ZTOcAbU^OH^|Xav+frV`=Ew3{{d~w-=CjAP zPkNoag!rYGX3SGTZ`htUS;;RaeFR#$>HmY1_zz(9nR_hK(fmLwxlL}P>I~w{mYm~_ zv)JJrih?)fNE9z)<0~j+CV{H?9#FY__Cla|ZpK=>6g4mn`z5!1F${RcZ~M+#1e zB}wG^#Xy9Sj+03rL=7)+Y|pY?7aj{j=9v(&X=++?0^f&+ffYK#$=VE-?OfBVfGKY( zy%@M}V26U0UPB!UG@-z}N~KguxMItUv2edwV(gzM8AzQpAQ?!Vs$}}-R^e$yXP;L2 zL<$ILA*!5J&y=XlSvcbU%oUA`VTj7a60&^S*1Az;d1!`t=kQJ*t+@^p&gVZC|?3r`0=N>-$mzk1eV!c7Y%v%Q(L z#}FQ4!$~8BB|m=)OHWV0OcQ-M4%3^|I|;a?!BV>UyLwfCHuz|ZYP(p-W^ zJiI<`Ev=wDu@NuVAfY))jn!#tle-^rwr;zuo_a0#;PO^IgEh%ja?M#B56qX|{b8iXj-<9?IOB&s}LqObL NSf^We2?*)H{{V7~!yfnMStdJgp?QnQOWBq} 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 X`AFW;mUXn{9lcpc@9zj?QBM62jisx@ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..d04a40a477ce6c738d84601e985835966415c87c GIT binary patch literal 7856 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 A#Q*>R literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_features.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_features.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf7ec2b17391ea5b5ca495a8f28f9138b1456afa GIT binary patch literal 3326 zcmai0O>7&-6`uX!QY4omMM;)qN%qPTYE!UD|J3~fB&K$3w>A=`h+P;KT2QOqA-NK{ z%ghWVQ6K@PFi;vWP~}pqKn_Mv4qPDDplA=i70pGe5SZBrsDYk(V=Wy5pZaFSr4-dg z2fOoT-n@A;^WOK~%$Lz<7(to%QndaOLg=5o^npbfE_uz0&90C0} zYWB3dKaO(WQt3C_OZeOK4VGoTZhz?9-^A`Ts+`( zHOnDX6PLhW#|w-2oSN)Q`&2-WZ_0?px)P#4au-b^ruzITM0y4E015v?b``Bj574TR zl;_?EDHnamMtrCJ@b7&ODsCg@t3^wlQ*;^7SV4DDQkYAMu0k2H?7XX(R^DPXDY(KK z<(Aam;B>($;WX80Nw?EKC(aT!7)^iZEIQ0GX!?D}&O0}*U${o8mA6aSX6e)lwpY?c zYcXA}vVvn@NnN^}rWV5&%DS~RCMT(kgOOO9o` zAy}9&$0<@b#PnsnTCf0Xie4^b+jJ$%W^U-_nt{uVLoeVho(vF<2RF>u&z5z%LR}0Y)&=1b*Kw>Y`6=%R zzjheVQcY?IRj%bQ%p^aE>hck8H?-=3np_Jk!>R;kRwW3u9$z^Gn#2RGDjtT^I=)Rc zR>0bklWVz3F=rKv*nB4(=B~YWY`j#VOk2d7lk=wL98x+E-#hdwe4!yaNl^fVU@cGS7Sl4V<^O6dTlA(r(sCp0@l zlmnP=!3SfnFO)S3*jOfKVSEUFT7{~C+6go~`Euyo_RzU!13N>hZF!*a)8_rQs00Rg z6QeH^Q`?EDmx=S+iSy6y?RWRx23a z`0?L^W3AXgD-!#5KZ?{rz+qtU>)1eB0{Vvb!Ro)8xOM`)I1##bMtU(JK_pfN0jR?NsuG)B#RE z^e$VfEiBw46}+&(m*PR-Yk@6v_jTUW&owP9+=86k5x1BIQntf1*ab}YaSU8}W+H4akCXvw}Qg}$ELd5uf6P_+U}ovdViZ>Lk#RWfHJrrDNihi2w@J- zHPapa(;XMyD}edI=mp2*jwlmn#p0mQ!FAfILzZ>oRKRH*B)VPI44vWw&#V?OfTWv( zQb#f0f8d#GJ#LU{nK^l)mkZU*?DwxiV8jZHl?{jB%JAAu_-#{&aH{RxN{B2=_1H*@f_@fGfef z%FE!cya+E458MaPk7cvE4FRL`R|m~zJ>YtV@>cx}d6w~>5%4xDIcBAZGvp_rlt0Vp zpP+&SDhT@u5>9@D;@_a?KhV@RnrbT%VQkZABOp)l9w+UwK_TAigNzIGRUot%0NTEY zM#mdUQ*Pefm})+F6x%^#ZDm;K-I#16Ae&QroV0(S2&Xo#v=NY}10Hz>BhYJ~K=CtM zy=UCWOiLZxij23SBh9&kN*!)y{v8XQ6&uke+jw_#VAJ^h$j01uY;qrk1kpR&e*g}7 BGD`pe literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6e99ac14a8169970a2cfd9189db1863f0c56fa8e GIT binary patch literal 3252 zcmb_eO>7&-6`t8${+UaP`myOB+M;V&79*Lge_F$aYdMK(Lv|w+b&w!T(anmxk|>j0 zb!I6`0u2bQfKq^ySU>_f=pix?q%vF-x%QTOE@CA^>c$5L@g+B<6dK2;ytm6;Q$m6y zNCw!qvomjIzc=r_@69KX$Swly@Fzn4Um-$1#}BtjjZXcK&{-rJ(P)8Woj1)=f#_u1 z)p6IrTN5<#pVEE78On+RxlA^4_UjFe6d(I)!~A! z7CYpURh#xq{01J(3ff{)pD%cYO@RyL6G5-sE_* zUSsrr<6R+T%EG44Ro3Wz>h%9YO}{%jFILE#t%Ou)MQ~4wl4){J7@FxdhR5}snbZuO zmyVf9UY?jRn3+^fljVoYrk(__`O#8QFPUIixassPaa1?0(96>k`fnLCnB{}TIs+Yr z>n3iXC@IO3xLz2sq>@_HE#IVCDC<^8QS3tiMKnB_OwK+X&x{#GJ;POAR7;uPGXr7q z%pZ(l!_4P+<|U&vY7D$^{CN*fCOxT_CNqWnaAsoK95YI%(kD)4c;3{HPpG-;>Zr~$ zT8U?xT%R+i(i77x3O}9M^UO!-Cd7MSjvoi{WR^S(?|Hj_zJE2GSQ@B>dmgqOSV;`7 zwq03~uRNAp=P%df&LwF{e<1hOVFAgxVS^v&HQS>j(D~UtD!3kX{f+))kY^Bm3=@xq zQOGDFAkZnAUT8Q)WGy%mL(v9;BT#SxYsV2hfgPvgElr?yFNhy{IuGQdtON8@K`W>z z7zBy}>7f)2tqi%SShAx0xvUl%GXa2UY@C)hENEiHq4}ib` zYd^HuztF#QdYRrmv)bNU4Q>ijsOK{|QZ-lPL$@dYBOiJ!$Jgb9HTmG3u?O<8C%3LP zeLdxv5PuJzCI5~;9}`oe708$P1n`O#LQYf7GSe}ryblN}fguET6mg)vZzLVyEjvK7 z6igmLtzUouv|EDT*RvHPdpp-Z@Zaz40fA`KyoVp+rMouyIQ+?&bw(ZFQn$l zO^OkXwb#xIVJ@hN&B*T~r9-<(0U;95jt05m;RX%l;vEe%0`3rKAWiLPpb>P3?EI33 zkVqgFITRkCm2KJ907UA4f;I2bo%LLIsyP_qmes5et|0zb$wyG*z2k~D^q|&zdI7uU z2d}|VK<$Tj%J($x-87owI#=KfZQ2$99t&W^*(~7Ei(Rq-cZEWR3r>bZ!KOnHpEjQZ znL2lvdy+%uhhYzfq3Dnz+KhG8;YGa0%p|`97*s!Js|%pTcY}avejMAq5U9nHOI<6m z6U*JJu@eqkBKQ9^BUlEuXqdrpJ9YxZcABuqXS%){@H0;j;9?ha$G(NjwT_IvxpF;ha%2^(k{wWt?>bHfz_5&d4h&bFMH*6V551 z5qADK$A#CFr@`~i9pVB*PhjToUxU~bDW&x&q3xfN=%Z*?_4<0Ws}}8AIePwMeJ%RC a+3;8XcG~?V0r9`ST{K$XpP(^&-){hb-m?S% literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_no_ssl.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_no_ssl.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30fda382c5517738801d07fc15cde77b356769d1 GIT binary patch literal 3173 zcmbtW&2QX96rZuZUhikV`#}TRqO^1a&C*s>APQ1j(o(4`D5q9 zfp++wQvW?g$e-BhH|WdO`w?DS#32rCkQ%;~25l-eg@RsF6>^a{>LzhCMy?srn#Aj_ z8g^-10?kM?2WUQlW+hqxw0KvEY~>_20@&yP%R6H% zx2!nhn_8{Vq2-B3IJsJ;fqK^M*x|}h(x}rL1!apcQlk*6;?SDfQOatNy>fZ+OU6ar zb06hlO0}#7hH1K1lbL3aHO;2yv>T}BP4la^)rdyYux|Z?d2*Zd(84pjvg$QiMOdO~ zxs@-tca7P;s4RF(o?o{`0etzMKS^c~^ALL+8eYt~tW7{;)1Zyk{biTwf_mGsZ znQxlii8#EuJ~$0iLM)t>=`-~3=5O1 zcK`$&%HY5pdNe2@iuDhVbppG9#T z#h$`)5HS>o;1NiMP0~};)NC(HLFgbt+T$D>o!IbOR543kgrF@+KhxFmw575CT{9b3O}M6Fz zDv{?_nBQWI+l>3&sn~-Ad=jk32S0S(NPlTv2W1N?;v(Vi<8+z6CD4+Wn=wuJtTP^! zsOX3X;Cnb7SplJrOx(?MZOC#t9!CwnNDO1>pZdsT$gDZQAn%818)!?O*Wjnn4fLO*d;9|wAENjO#W@sv z2rcP@;ut*Q42Vs#s{oB}PxeTk(!0Rx!uRSf0EIgm;su0eZ!gL@e}O)U+@A_Pj427* z4H|m%pXEEIB%~_3#F1`8sk+bR%<`G(uw|>=@J&1Xfbw~mi2jSHEN3TnCTH$vkL+ki zq+8`;pfR`I40N~_pmumfZ@`KmeaZFbVJQ3}9t9};gOa}&_){Uj|FZ6d^#*P2Imu_n zneY$vV}1!nF(X9_#I8yy{gvc?Bm4fy7j9&p=#-A#n%tWHZu$uU^@%}g=5_}4OsF4> l^;?}-kLElzvUK(_0rB*;F*@~_fY?1o>Crn~oO@Kp`Zqs%YG42W literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/_tls_util.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/_tls_util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0799cea458f31eeca7737e01e9fa1a0e6794171 GIT binary patch literal 910 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..dd4024402d45d696dcc92119986b44b24f1abb0b GIT binary patch literal 3435 zcmai0&2JmW6`xsti=;@Ck}StoJoDpbP<`4kmB*g#sqMGr;c|DlL)1#+Rv0#ep05Fl>R0zD9skp}9eZ)SH%SxV6X zc0S&|c^@p8(>M*4m2)72CiLLjFzTUVQbDsw4?jeDga%eG zc$$t4t8X4#c++PAcT!C2Q5x4{AUCM@JPa1kz&u`7^f=5jlCJ0pm}igiC1KsyP;$Nh z6hd;?3C4(M=(^zBPvOlourv5%-7MsrnoT#drT9vA*)1#_Y zORrbNJfW&Tt7{ea&mYjC5P!5%Sh1?Kz%^dg%)*bEwMt7iFWk14EZZpY!jCL-*_wIp z>NMxZvRS33UC7^~=DkA2SSr*S_KIb`mH*zg0yk`WwFZ7@%aj*%lNU5$q32n?)?gwo zWG8Npi&%Jw_JdKD^65@cnC65Twd>3*`R>DS6ogOQ)!{eLJw`V4Zd>(k&|iyj13mNh zZsHBhlnuN9G#%fa00S=S3dEPPsca~#;p4m9`D*Mm?U_f~#O5`S6Fep-B;|JzuhEiGHmFYIA+IpYwCbGr zhuzadpj{THd3lm(WO0$(Y;kc??iEyGmF3*uK*$g3jKSSj8j?O@Gs7B@QZ!R1BT@@c zd<~pvj1ujhW>mDL3eCF;Z)*mpoIo_rRcjS0VuiXfNsKblOj0-Rn%0`>OCiz}gLmD@ zOO;MgsAmc|KJP?4-Rl?XrqLDaI^L0#i?S%NJAq|D*%^qwyeee)z;cqWEtv=>r%$qZ z;yava^&}XVsP@9@^_7m?om)WCM{ek8tq@4?SsM9G(#{=LlTZf0~Zb7ePkrIoq*D6$_*?wtE` z?2Q-EQTcm(H#5GM$?sCO{~RK0Jc%F^U}n77N?r0%UuDbu5zeCU9(^)re2;jy^;d(5T=LcX6VP zA(p_%?!ZeAbOU}3-4A?>*6=9m>ID8NNcuu=XcL28yqY{A5|+MRj6OzYjKgdxmSe0VH1e-Q;?CVj-LXiCw!7vj4&5g)Bh{sOU0l;==O~&X{Kq} zWQlg2(Oq!4uv#DYY$P6M*JtUdFNpQSd9I%`Ee~(Le8FAAdQ`iWwNn^N(N*`k( zsez3H>HF~UUqZKq+Uay?4*xYiaF7{lCWc-_2in0v?5B7?`^H{&YBxL8%3fV1cXT$!Pp$$Ph|HJW4np5za+-L z$X@vNAa!2so^yB4wlcSxiCbd#96o9%#nvN!8LWDcp2p}`sZsR1^X~-y7=XodoSeQH zc%Dj64=c}y`=@V&p5F)pfXB`<9OWzcml<33&<8Amd*CxSRH8iVGk7quix-1 zh)&B|y#f*9zC0$0VH&ofRg87G0vW7jV$u@j9AZ2VPl~kaZSixOljU$}Cy4SYm;j+$ z)@3HlirYQOC=TT|e0&hPE%dj@*@M)@X7u9A_?y43f4bI+Uup&~xf$#DLqIqxGu9YP z;6OV6xoPCKC0T|O_RjmS5u;aT(2qho^ya2yd3J)F~ zYATngTU`7O*CuiR{51o%bk5YxB;Hxeom#ENtQs>kn}RmOB=GubfFdZMc|zvOuVoQX zEQ~WuZi6Kh^jpkDy-94!ZE+D1`*mn>= z-wd9YcbDsT!giwu1~PGLPEyoddcQ<#!ZapIqF1hE+VSj@^_huCX5xv)M8T2Pctsvw zGfqlXWsY-wR#irT$%%DjJBgY3h4(v}JM1lB5nl68 zpaVo>{7o3)!LLx?zr!J{93tr28Pqq}MCmWd$X0Co`c7cyqn*TVe5i#++F_LH+ls#m zV;p-L_w|a>Z}USW#`cX6o_O++P%_a@ S$MJjCMBw literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..a0dcdc187acbbf92cd788e90bf998bab4f6cdf94 GIT binary patch literal 38614 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 Vd2fsE+Z?yTS-Y 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 s+KH{gT&V0)SL3G|%2iRU#Z(Bp20RR91 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/btree.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/btree.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2dac3f3bf180c5e3758938181b20cb546e394c9 GIT binary patch literal 40912 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%0A00-1vJsh*l(tyHa)P>0{OIlt@i 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 GBmWl?U)eGM literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/btreezone.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/btreezone.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91b62e68fe1474e56ef729e94b314c2503d9ec47 GIT binary patch literal 21045 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/dnssec.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/dnssec.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..746eeff9549467649bc24f814a1085f15105c2bf GIT binary patch literal 51265 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 GW%>_En1B8N literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/edns.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/edns.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3585d78555978fdf3274ecef2224524adcfc5406 GIT binary patch literal 26141 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..7c75e627e29162122ef33a8ad2f7a836fd268c37 GIT binary patch literal 5991 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 Nm3e;?^LZl$@V}dijRyb# literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/enum.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/enum.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bb0c77ffcaedc5b53ec896632e4a37a8f8a7093 GIT binary patch literal 4888 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 JzXWL}?|;H|9xMO= literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..420b3811b92f92ff9392d244995953ac776552ba GIT binary patch literal 7224 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..931126d337b18257f9d8100f8e5528e4078e64a8 GIT binary patch literal 3805 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 Rw!F*~3rSjgj36N&|6j0uDjNU* literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/ipv4.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/ipv4.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5901dbdc3854fa1f5c247f03dcc8fa6387d4bbe GIT binary patch literal 2585 zcmb_eOKcNI7@paO-_DDKM=IK-kcUHJLra^sN-O16siGhtw1}uE-gr03I`*!anS?kv zEdmaSNE=5W#q=RrJ%Q3n>8ZVQ=pgI?~SkGxN{P z|Id8?_wQZ5-vembcbC(Cb_4K;b?hRPgu-bmtN;ZJ0|hEv3=Tu9<%W5MR|Q2-#W6t< zuL{Es#i5F-FzW<>jtX?tNk^SkqfFODM_oxS;GW(2(TV9J(u5jIs7M-{(j!DObR67d zM;|wxvZ5drW7C0DGKKUAESba_7yWqp6|T~11(5Pw3apl71x|JYfSL(MRjd-Vow5gL z8+BB4O5$ah0t&Z>_PzlMPi3J(7As^&QU1<31Om?4rG7)7RuPe8U9vnYIUs4eG^*(` zN=jn}ic7?hh&n^8PSE3#Xz3`ac63Uz6b_FjiHgJFPKivYFWRcK`jmA)*jkDS3WtN% zrkl)N#Jt~v!A*86o2;|R?kymgd`#6%UeU4XR%aq=f?dAp9ZKp%o*6{QKmmxDDrPIC zt#D|BB@Xhx!omu&oa}D11 zWK(O0*UtQM@~7dok8|GMoX}gK8v;{c4^Rat1_S18;X`^y)qvUt&}s=vxIP7|-7Bo+%hvLf0}LdKJt@8zC}f9yt|Ho6lkJwy@s~MO0B*qi zTH+(zIEZlP{LDYnWS^2M+p^3p)(T)wptHN|+30Rt+HMucI7|uO!yg@Afg!phq~9EW z0qh5)+15G$&?^wUjJ+4Y41X3}fRqxxegP$vIe-yD1wYu7z~)PQU6}<>JdYsU7Uz% z1bNvq22Dg?1Rw@T)2U%a8`p@*8%QyEO(&*1hK^cjzmbv z{$AHOFRuIQmyRwR%?J;CdoK-cI6d=kU71avy!^#;BvRM(1c+kiys%!oV=1+eTKs&Y zp=qUSx$9xW!Fvq{bH0Nc-kN!G?cMbGYwu@HXZPJa^8HE|K%1AoWgkG+jg z02JGwwJ*=p!7cnM_4tlk=T1Gy}7!yYgq>xQmH zC@{;I|DQoFYgolJI%qlxmXleqBA123k~U_gQ_BSEhHj9eBgJqCa8}Z>utgIe17C1z->B5Mie75GJ@E%nf@y4 zih;w($>eE(Y@crJej8YOSch-9dg6vM6;nSzom8F0EoOgZ6cP~rbpXJHbX1^3gn?Vs@~=n*2K+*qX+fPnIt-{njjg${Op`e*x-d BUylF) literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..7d118d516399417c719e0370c6cf7bb851ec6d4a GIT binary patch literal 6711 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 A)c^nh literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/name.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/name.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..efee28338a1484d79ba54454e6792e82e5685484 GIT binary patch literal 49198 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 a$tzgSp*tJU#Il|4a?vS|9k^qN>i-4e?}E+% literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..b46ea01bcce9dbe90943c409291d61f3269f374f GIT binary patch literal 4379 zcmb_f-EZ606~C7vW!jP~`6I3Ee3*`%x^fZ;NsFaevaD&94gngih_@jSI?%L8+l(nu zC21!%Ul>ui;A`MEv`sXxBK#fqf8sx zWu;Woi?i9hC+ShH`LdmZ!5`t{8p%;GfKzEMG)q+OOPb>=WCl#QE#?ILd@igCKs(eh zP*Lpw+Np{_yVOpg5w#0wRE+?QRmcsR>z*XZs4Hf{ybM^k{Lc#kE2Zmmrmacy+LA3P z7T903Q@D{7E|<*dYg*p1Q(avWJeP`+qN+OXPjXCYT7&IKhCZ)JiezcC3x;AzA1yg^ zFuijOCnRT1x0(D7Q_3r#%dkypR&!thd{@&-n(ash#mUb}Go}SPXZ1y`h^D2OcrxU6 z7L*&Zs+F8MH|`H*-O&oR+ab%gV_I6C1`fCb#^7$UT?O(NS|^S_c_&mRe+Jh-YH}!X zT7ls6%0zBDnm`!98{ZOVZ~po+xfW~9?ELTOD^s16xxdrT=%c1}lq``Xm$fhek{zyK zFO)RP6=hj3>W(bCJRX*tkXv4BZq5qBdJA{uh8Iez;%G_UwcYUib@tyW z%XvewZIDBo!F#v9SJQK5K}*|;T~LbYk1UfpH+{vNHXS{0r!SkuSu^wA$qTlv&lU?> z(MhMk1B+=xpH7#UW8X@hI-R!Rz)zNx{Jb)&*=dNFG)4>>oGL9@5%}D4Ps{F9Yey%6 zZJz`3-&SJeijbj;xxp0$k2*Au71c_7{hJ|vlK-n$?8K!uPl2( zAp6O%d@ykGdnbPeLiS}K;DShZ#s2i2s#CvNyZJbBtT8(NeDuu5=$U_wY>b|*WS&K( z%~+z^_vzJD{z3Qs?#Hn=HV;c{eSf`L$=uC0M#tEa7Xs-XxB5ZjE~lW4%qbEK>Tr

0!4a&+#-#5-@Wpk^6L8= zacNzYJhS}tg(c`|X4aQ5*c~g5?}>Kta1FC=&-17$VMmk6OyE-Ysm4$&1KTpj(J z{ouy^8xL0QuRM*P-0VHHe)#84de5#$&o&N?RQP+*JJIUohByeB{#+d15Qo=7&&09) z!Q^|5hvfL7V7eCcGuVMo@HB%^+=5{6K^wm^To}%mLWLOE zxj-TpR%8UhR7-9{w>5c^Z`(k@)k-@J3{K z?bPGQSmW>!h8_n7>mS!X-Xb)1yh!^6}b6QRU`ivcW51fY;{t^^J2t-f79S3CD40E0ff!8#nX zV>|)_NGPal+6Cq!U^6>7>oBa_kIfx(iY)^w1&nEOehce;Ozy3!b)#mi$B(XyNB3tp zMk_PsJ(k_=ZcWGy%#Pp|KnS1t3>X$|KexgKa-NI8o!&xh$hcFGaUsXg{&ug?>mu z<37-fYMZheiuMV-ASNUoDtN5A{cfdM;se}Ezu8xg>o!|$Kz)ccI;z;O0XhjCO7~{1 zkBStDRkf;FP~dy!O!MO@Ny?gz1}YUtng}dGv+~A*s@Yx}0PRH=>cOXFw}Z8Az*vue z0=_jhwX1iTn!?UQ^NfX!&%lucnf`2jt#_C_W*vJ1;pk1^dvT5t;xd8077| z*`4^-wE30-CqBV+_)_dmf+F0^>rkny(skXL^Avj>4Q`B~csALw1KzG+DR9u%2t?rG$^g4YUB-K9mBF4`E-sIzrv_duBh?Go#(phSeuYI(>ghnI*aB{wO{&8fa(LYen*0Pn0n?0|s3f0BcPoDIQY>`ms zSmnZIqOUI2#Coh2TUFOit({(-dy*KhTxi4-)y$uk8Uur?Z?Dn&XR77LiP0AwAoD^b zv7Rsb>I1cb`jOg^ji^+iEiK)(?ll^~jAR^pBMdy~jk;vHV5$oSj-#^t$%0}uX8<5o zGY@QNe{92ap#5R}2)ha`#5-#o$#EoUB!C3OdJD-}Aa1PLHsv+LkmV$`dV$MsiFa8U zWMRw4$QVwre&#lL$?<}?6(v3AZe4oCcL>L~dIjOwRyP$!w!%D&qKrp{qg!Fdqr&Ka z!Uu#=hKEcY`1O})Ks;|62K3y}W7{cZI&X!LE4D)03ko#iA@PK<5%^v_=N-s->mt13 lrDjV&UW6#6FW(__=({kX$G;)*SK&*Pj#h4dNARC%_#bW6x#a)= literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..5381a8010657e106e93998ea76de2c48e1230998 GIT binary patch literal 14218 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..22ced5c928105b0afa2b988a387c8b5741e91582 GIT binary patch literal 69436 zcmeFa3v^t^c_w)K{q6=D?|1W!7s2;ilnjCdNs$C45_-XwnoZmW2@vR}ZZ}8)YDkc* z#DGMHVC;n8SQ)`%Z%mDpwVXIHCp()+mJ@4}IlJ8g6SR78Hp+?4?D%A|3yIv34wKp0 z@2|Rb?`^R8lI_Hk_>!nweQ#CWzg~a+^}p)ReLlB@YssIR`~T~LB>fHj5Z5v_a?M*J zNf)I@Bw6Z_WRq+jH1(L+-P~hlcT10j-K{-VcDMD|*xlY^XLm=BgWa7yPIhb*w2*Q)nA^cUfZboIc1NUh(sl$&F2mexaQ-!QBBH%FU$w#eS7bKta|N5rDr zMjetQ`|vg?p6PelAKfMg@UBRICSrX~x#%Tp&yMJhp8KMd=9Z-wE<3vIgyj|`o2&4V z=uWx#KHOQ2f~Zjm`Y$EEuHOfq)8ithwa6D(?o#9~yG8C7MIQaG<=(||mm_z@Epp%P z+Z`--C307Nj@-ME=Vcux5gRxsV*0(OQ?ADN-YeH2+$Yx}+%MN5d{C}OctCDIco1{q zS$WAzrk+EHrS32umKN?ad0Esio7srD^{80>lC7u9CE+V$pYtm#qDu$+at z^G8{4wxTyzM=b;Yvz}MPv{Cc8Y?j;bXOh?8pIKfjhY;cqZTh<+wSJF2KJb10mx%TG z<#nj-ABf-jec*~77cqH#^oYD6`lQ-78!>B_;R&z(Cj8nQeM;VfzpYyTZNvNRc(OxF zcVF}=-fD$gpS7pnGCNV~{n5vP9qt;+zNPX5Xyvf{1>|{Kz^>m1eyGPqOm2@J$2X4m z+2vj6h2DG0+kv|6ru?!Q*lG{*%QP6C_sRP)wok|pB0R}3zyXYfgLmJ1(c^3c973)xZKMUF!RWT=lYIp<`|@A%^D=#pa`RECm+=rEK_&txQ+l?pDLIP%LNnQ(r{$6Hawfs0> z9x4d)3qLoQpU~j#(PA9(;N7=;^eJ_eRm)#Q9b)pAFp}f)lL$}$6ysSRhxudKDL;j; zJgv8s{308{disBwF-uV1_1xEHre6-DHi~=VGgJKlC0rTxfkPIj-H7gVXHHr1A)Tp6)=7jWt|1pAXY-XoLl9w%Y9fSp2=?^ z6^>u|Iki(kibS2?x1xl2%?sD;u17~-wR{3CG*Ym(zxs2j?Y*ybdY_%t`cbdBgP3XmRjecWec&@aE@JXf;k^vTW&wJWt)%y|>cl{8@jlnr z2^|v!qJGeeag5wCK>QB^CjCC}|LJiNi5H!9IpRM#mx~&cPad ztH2tJ{8nR59`Exp8uc{t%-sC8;0n(v_@@ik2w$<^T#ichE4Qhe^Om{vmL>Z6AA=f6 z+;eO2F^~in9M)*Cd`h22${D;rd(UOlC<@8XV2toqm0Hcl}7>nua20PNzrA?CB?0BoR$x*=viu#o{*+o=Uiu{XDm-h zXH89C87=FM44w)l**_G`S>#wE=ZZel8)cQq zIlA#QKAdba=iJd}PWLMjs$0%MiSal=-wuUtMAn>)4@K7`B8j0$Y|TSTd?4DJOsqK) zKORr^_a@dHipNgGckf!=o=Ehchz&(!$u(_fqOmh;2K$e%IX#j*8INsfTep5qqCXj3 zeLB*6Dsm#4SOZwrJUtv$M%qq~c-b@->2z4+X`zxe#nMcVF3`D48OD{PFe6vTP0Ff{L;QNA}M7k96N4&KVA4TqMKcoF_aO z?~M$GPsf#H&V><^h!0}M=7Qnl@pv+kR3fLt5n0CbM9vZJiwyM-j-VWCB0AWYb85-m zhEj@e&S~cv@quvfU?hUv1D}v9)rFn1hMA#Teo6j_qnd@$}RMBS+-`$#O|*iWIftzT+?W!Vy#U>^P+S@8nf%e zHDwVT>Mx;<#~fqMJ0J>hc^6+8#wtouf+_oeYw>$F(A5+sDfVkAsn9y`LhUW`7ECR^ zi1PZ9KJmqrQ#>ET$Qba8=lXrjm9i=gcc>#xv_)6Ss+H_Q$%I-KT>~`Q7F`&vu9W3X zli=x%xl`@|nxl&@G1|xMDf@2e$v;I;cs>V=9t}os$~qnZoRy0{grBnc6!4+44k^sC zWz2eq@6_B}0?o5RCHI+AUV+fYd@-{ujrm+sk_I`uQr={}h!2EBOuwgmT08lZ%fxfp zie6Rc|5zZoLOf6TKL_NVl-USnDj>dSl9H?Rl0`fAnoZJCVEkmWcsD@vanbdnX_=I4 z6c0f!ScpbA_mM^+*nrxVl}M0t4)*s&X;oCHMx&0kZS3C(HS-smDsujCZ+z(Va55UE zbP2^v`Krr%YBHsP9(q-j6^=!)c)Qeh zD4V(?S1}kr8y>`Fcu*7`PBMzbGZZ-$4JYD2;K`g_y?c%x?l=%W+I?hC`ysa60I&AO zV==7cK>8hVC3 zN*I|fd^sOSuA;hr{`qj65gZ9+EoCx=+4{(v))9#%&PEkrY>~$t=_9a*m0nO+ls^|# z*Ga<~nxJKt;bcR=ms*cnuW+*W^k~guENyt%jUp1$z;NTzf?cYrnb<#H+kAP;Z0&~0 zLlf;2qw|5_`DZ4dnF}n<1eVThyc%fC7FEp^g)&8DwtZB+e5Pl%dhL`WTUGa3?WNi&`v)bV`MS_+k6(Ixu5M$dZsS`|&enBI zb!MyUUu(P6HdnnVQ@tr$S~*kx)~W@IRI~Y-WUdKLIRIYW^10g8ncCH}wQHta*_!2_ zNTt52DaU+S<;BM@JU&;}nkj3&{Mc;SrYUo_B$O^`eakwx_6wP{UwFIy{k40t70c5V zt=YzP(?0F{J2Q1VvsEiETjv_LW*WDqE4N))f6Zd9FTc>a;FRi@PIWGr-NjY&H7jS7 zH#g6u=OyP{<dP!Bv^ys0oQJIfmt2%;DN0KKw(Y6x>B&cW_QhY3xh~Y9E>wiX_Q=`jn zHKq)S#;7dN@)%p!?NZ6gw08wt;+rg8U0tu6ySkbjMA^{H24&+8hlk?w@F2y#;qcSL zkwN~3ks8V#l%o*cn)9hdNpEy;FdTl}q!9K?P!oKClP1_SVAp`)AiW?o^4Uw$b+gU2 zbfHLk$kc7RW?AKGTzvA9+;s926(B-SoLk6k0O)JRHV=MO#rcqavR(z4}M zDN7;Jw&ARwSR+v zD9)5+Jd`qz*8>V^5Ohzr5D%Sc2L4pgbCn5DkS6C};L_)u^1)|BB^J06`CP+?JyPWfgO0X)mD1-Mb^;FIV ziZ(u&D+*(=Alf^8oD<(oE;d99wNlO+QBEXs_ETr+&K4U!6o`tW;Y*j1TCK?M0gYJw zGSVlGA^@?v5R{54(}9Nc@=fXGJO8F=*@rdtuiba)zPXyUnVPk;HS4FGf}Wk0)85rH zeeZi$&o^(&7FA9+r&n&dQV#Ud;-A=k{=vxy|H8Y94|s<{Ls22BSm~l*1A?5Zy)*ns z*U^Xebnu2aOm7}SU>wXx=ph9!(hFKBF_?jyDyy>fOS5HF*^;Wcl4Y5aWuN+rUH(rb z1bjGCp2EX}7Yk6RPl*qOBC*i0mM5eV zW1+YbB8=Te*gAh6V~ZV@wzee+NPS@U0+^^v9nb z?(h8^uqyygq2R|9IZXkZ{`5%0sfe?QYQg>5ZUlFltlAO}X4AM8JXERAe3T|IevcwQ z{o>3elaX1Mv&7?*|K6?2MfI}S=G5s!}X6$iLeX>Pmj{`D(4U~(RelNuB z0DIgvP>4I?5Zr@Ab@FqR3U(^{r`K*oS|+UnJ#!Y^h$kcy{*4IPgO$^X-HIE)Hu;G| z&>u@ABQb1wEs4Zn&UN(Y!J~W!?;w5w_~JPi_?STGaqN`?yZPF3RDF_j?|F1jS9ch@ z``tMYk3V)~fA=0@d!Fb|B%=x&@tjcf;iC$59=6%60MM)EKAF-+WG3ryo>lsj6jO-& zXS;R46&=jk!EcBq6O2qq(3l}iMHgT5T;a13uxi2IR(=I9v-nGpPR_XWagpRK`uc;@ z%C|RsXXiI}UM*es*6|+L`28wqO2cWk>>b!@i0yV=(9fFn=hK$DeC`=y8& zaaW3|C(FP~ZxAtpi-rbvhTWam(NV*_ZqAt!HwzJhTJwVW&AUo@8Og8XFF~^gXv*rF zh)k7E4NuE65B`O94aY_rhC!M;fn~l4tj=!bE6Au!&}XP9(cK1kv;lJt??0o2MtI7m zt$FZJvxrNh$8O-sMQEfX#T)5v_jW|3$tKbX5wma1^0`WZyfd#9EB1iaQ;iY?B#H<4 z3i|D`R1Mh%ztfwenB|6V5w2f!71}8aZsUkq<2I@|q4Ot1+JP{qy{6KFAm<`3V)%42 z9#d$<lab)*jsAc!!Rh2$e{8YDCr;Ymv|Nd zb`egit!&;`bpG+l$LD+v8DGPUd1lwl)8E`P>ua7c-}(($Q(pg6aB}PWp2lhO^t$Qw z)ApIAGY`)knOT{sY)o4liL-;L2@-eCvpnMg=gMR&`yfyX9*?c;1{)_Yw%gmQrFW|9 z+P7QY*=9nR$Kufz(BB5ESU}AQ(}>Bqgc^zT#Om8GsD&0$bf}qiyh>Ip-a=iC#nqIu zAhhaBtZYk}z9Gq29KRv`mL_a?(bVMV>Sh~7<<|gn=V<8((5Dfd?q($Ss5eAtA5%FL z?+c;NsZ$Ic?*-#{2wRALk})%qy2;AB+e`UHpXNL=5xOxhy)A|e7W~?ZFR(4dNR}Wrpx$4+F@ARB^PCDnDbs1;f^r_2B zF2$}oSHIPI<>@O*di}1qPo<9>O&{rgJO2LqM?MUceBi3i21@7M)meA_bZR$G#w$ZFYv8y7P!7D?ZP#=Kl>~E@DXqnF8YD^PvT?q>n*7$q1MYhN)^q ziufp9g&0TJ{;8t^>?WI1ltRcvF+Rg6&_rnM7n*Ra?B}9UO6ZeWt;mf4C!g84#><-#GGP!kpw8;7bp#g z(U`^y=9)&D%37}W2Zpz$H14-aIDgXHdG*BlXC7yBZI?HhFo**NNj}5 zW%`tLcnLzucJ?H|l-^_bbgqoO#iUIn!^fjyf^t3q@oYdvMfwU5P8G(v7)I^D zH6Q_q@-1Ylq-hCd7DCf*$?YHCl?|4UA6PKkY(Wh%FVFav&pa~gYnd=-%WEb)AG;-| zpL8QmPM)0eugLgU%=FFrS5H_z@c2Fq1gDO_JThM%%9b9$Y^(@kHWmjLyi%}iV%G0iq`Q|I0x4Y9@9(-@tZ292{ z&j$|Qd~xZ;$_tfq#p^P~>)zUYWk-7b-uHsD#Rn(sSu7Tw=||I^=~bn;i1boiuq zmNaxUNbmZ*9krHsYfKb2lyz*hK;>lw>I2k8h1oxREy-U+WL@M7O^K9cz(}Z|B)asG zi!pD0p%|%DDvrPBp^i0>>gM?pq^E^M?qZ{30?$*Q7mDNQ(IhAUPum~b--?3qkwqY}V$b#u4vLJ&sUsiR{g4JS_0%HRQ1GG* z-5nx>Xi|=DnniE%pIWC;p~MEH!E+pqjC*USzYl15q>Tn)6v&t{JCR?lc~?9ZMa9pa z?C(9v0Z=ot(Ey0#-iQKF`M_Za9pn){6dxKK3H3s*9F1|-BP(!!ALH;HJI2e!*OQ^d z$@uUf7-`f5jgW?~X43E`txP5k4?*Cr#7`^z5i~xnAT)N{SecAI>jc?#0&^C4JeoWX z={5MrCvsK-mahl4-a){k5?;!;k(417WfHM`%1T*JFDckf7`q!0@ON7!SH+Yvy>X^^ z*12rFeZlOo`Lj;fdEcaODtgsfoAngaM%C*(-#OViwHca0fuafLXY-y?LX0e_W72WX zm38`n{A~UkP~exO^D8zWWAk6) zO411(;0ie&K8f`!agx-p;xVCKrTkl9PwX|=;&29Ww(H1QnNpXvi&VQD$#|HdZ_ioM zVdVq|q|NP=nyommQ;Z7YYflGax>l;nd<|$rix;4L52>QG5|OAHE7ASf(zBJQeAd%A zzI)zLJhcYw*sb5(%2=|Fb=m5sx$1S9>UFc#8zurDddlZKOEaFOS*JG}T9K_>wcwDv z%dSaQFLv)%b@!gP!f3ojVjGjT#tVUb0-N}0AdOXm`KV-AX-H6G!1-KIV`i0LZHIMQ zZx15t6|GSgbGy9YaA+!U%I_iV6g5LDYla0s_AhBi{d}N)TAA5+x#q3-+h=A2U1?|6 zM}cavuaYy5&IVf3&Q>myT83PsWkz+5HlfJTi&C}AjF<&~*E$iP?vkdZ0!HuMax%5G z5&0pkdIUv5%={>BTtntDdSK>p+PvnmV@pu&5$rlofLn+u%9Nv(-$$z7qS`M<1RF@~ z-ip$WiobEzd{|O`@xBZ9&6PA}N*ZTNnkSrDPs#XU&M`(}?Gm-TMh%!RYnILM7Gi;l zgauwgtnd(Gg?A7eJcHPJ+#`1AIC)3_`Y81KfOSTH^)W^bLM}=_i8iOV209MtQ+|N& zY)O_}hBiFO2yuq7h{)DN zsXE=q_;Np?PWuVSQNq~x;s$~?7f`u@P>$~(B+Ax-JtDQfvY#?Qa}(Q`6%@6j zP5b*sxK^f@q|#xvm3!$c;v%^b8|M3*i-<@NZJ693-o*6&64H$ojTMgtQ>L+!RM87P zpMx{2an_U_sUm}zk|bw~E@H!G<1EoA}497jm3h`Uk1duP9K1Vwh zEMI(IsVV&ZF;C&=sZ!ZVNtI?H|!q%b{>C+ST`b!FrFIAC$m}^$pO-UUc1}#(uGF87tRS$_D`N;w#xA zo}|hLR*SfP7bsl7DDG%$r?MuwMx;y`O*Ynwr}}+>2!=(MfH7elH!kr7jGIj&14Lq; zR0-4<*_;H|){*k2+$raq?l;uYkHt46QYY-m%_5F=b1EzH;M^+S4Qv-N{az3z&7=XP zNdu%SzlOG0Dj&@8P%!axJ@>X z)yV#_+L${Pl>Hdl6}bo z;!*ODh@~pTxDj_b_^QU02Y=Eza9E_(?-)~+sS1p%(l<*4o*!$79ZWtf(hM9CG5xN8 zYv2+6sffv?0)nxMRK>t!;<*H@NYpeB$3Jy*+JVT; z&kC1{`E|TNTB(qWF~S3H)@ZG>1SR&}oOhuA=4YY@QcJ=H95XOe%qmaWpBh$fE#621f%-XejW24FFClF|*Y+V&7+WO(EC? zvs&gcMg;q}m1n2S=nnx7fvCd5L;}1S1V5SDUjGt6Xj{Zk4sG1HK@_Dj7gX5;^$z2{ zaLZxd@@F)i#7qbE08A*iTy%o0Mb&3FGAEy21$_;?Tunc%OO!CzH4h%cNd~r zm~v}d2lz=O3qmUcc(CKo^vhA1w^lEEhY`qjC4!4(XfxI}G*cl07WJXgu2urmM{Sg5 zK69B=#W)4ocVeMtmPwm=wAIAdSn1jQ)9`(R)-=YZT20zE-L4gB=BOf(*3d(T zk9MP-U@xfun)>EP5JGngg z`0Q63i(x6lAOf_pQ1`*3p*(IOZ3l^Qh|MBvCEok!DRPrQtt?}p8nBiS0d=%Pw>bvy zDLaUTc{k?@Vf6CJco9kIU7>FzxFt_;C8N#o;X1> z8oHuG42i1i-RTJAs8L$6<1sM7PvX14lTqk)^d@Oo&;()3ANYt+B3Q@)Hb)lf80P94 z)FF$|tB@G-1iUF#5v^uZ6l$dM)yg-uhTy2;WT>6hl|-5CxX{utYsH!;W+Y5SfMS5s z#Go*;Knw$e5e;nw`=gwXMQ9k(LN=JOWOJm?>V|$~j4nPp)N(nIgXX~0C`g-d;bdpm zD6_q#75|25`>0DS|6d$6Ke>H07>jG;N*z`?Zz9&ONe)K03Y4mMg<$bxSb-=a-TgMQ z`h6IY4sP6wx!wkbU(f{XZxmD042XRNSht$vo-> zv`H*At4TmM>KDHTqp=golW>FLI0;=lbV|;lzG4hCa(;qb)K!UV#97$MSoHW-hCV~w zubiE=VnUh4kDt6pK?_pes8u!ku$=K33nlL4Z~`+(j)q2y+(!f%dps_WY!AH-5Kcu$ z6k^1AR8tTzZF`k>=|ew4(6m+g7R70k&a^)jV!CphIftqVk#h=_h@5>`8N>{Ny;B72 zzMK<3)tEp!%5PDAX4^%)MCEM?{t!XVNfRC>JSQ2CSRuv)45pX_K4*sjzMt%`iLc76 zwiOax1KNP0k*s4&1;jQg|;ule4R%f#e%!ftQtkPappyDu~7vi985+PYmR< z`kPtUp<1UQs3KkXq>{6s`}knAACDxWn>Xf)QBCMkMZ?E8Z;ZyMtpPZa_Mgt#WlU@Q zwtxu^(Kzc3NMg|G@6%@;>iEmKm;xmODn~&5NA@;47&(oFIp<5plW;VmlEqAQKIefl zCa@;kl`w;$rlracDeEFe>@IpXLxI+*%Ae3r+L^O@@G;Hi*fM%X16!dn#RVM1`^dS0 zz*HTuoCVco1CNbDz^6PwNvsTd8lpM#Nie2Qu4glenTzJky+cZxUXkAu26N7qqzpNe zl30otw4=EF#Z~&*yiAaSpG0ivL>br*kR^4QrJD6nNtj4ZJv#lx%PXKoR%6dtE5?sZ z6i++|8%<9U8OpfEpL=!LYmJv0=c-p{s#jl*Uaj7k_4vMCezE>S{rjGVY-#QEBh!0l z*3ER!bY9+gd1X4dZn7im3w(X^#Ro1t@V;+pwzO_KI{g@Qk&`ohm%A@_rh^+MIzB8e z8GkVAs7yOT9~DSG+P)ymBTnTigOIGf(mP2PPj#d!Wb<-LZAE!S!kHdU(@0 zzjbo!oTonHsh>Xc+Vhv5zv@|&4Xyl6;G2Q8w=UbX=DQ8AH%vS@^<2iglFHaIxdUau z%=F@p3p*xuf9NRr`m-0GyYSrmjulyF>8qZ(%9c!J%lpoj-{1G4rxr?W^A*jPx6W2< zoY+U1P=jFEe$Sre)sU}hp#+3TfTc@?>s*4`=;+r`K>K;8+KRlXwkg5-0#|KEDaTRQJAAzeNH*GFeoy}9YT z_q~4K)rzh2{>pUK?pgnyv~$mg9`E^WliOa|F}^z+s7P0>Py08F@BPqK_G%0HDtNPH zu6bvsd1t!n{n{1|SHq0y!jlv3zjpZN z*KVI%+nHJ0`S#h_wTCCoAC)f6R%-+c=T|np zx$Lt1M$@-qAKNU&!Rr>A&kxZ|Wy8hPg%s?&vXu=0n!=hD3m&O*`8CO2>7U$py+|sp zo-1j|l(bylJzKJFu4HSbWb2jE*^>JwcEfwa#RC@(eC^N#oG#Q~eD=b#q9hbnH$6P{ zoLU+LsVHq?-={4Abm4x8<>nkU8Ar`@@B5A=Gmp=$*p^wb?Mlb{D;_|vK#_XlM7nI< zTa9z;J2UG$)9dzT{S7nDxzO57Xl=S-9hB*P8?IR_0d%p&<-YEeT;B7ZNe@@n&Z2N< zWyV>VEh@g~y5P!|mR&q_;ZU}!iKMz7$yE!WaHRuT3v+B_CMBViiOCI2=e#M4=Gp}X@Jn^`&}7Ov4@5_V zZwIz=)2DNGzUJp_P=A0yDA1Qgc#mT@Z@3SnTbL&y6A$TSGmspy-pLIY?EL2a=5wY#(0}H#O#cZ zxmkvWy6yx)*HlGGyHQdpOxc`J%>qMi@g=x`qXetX;J}F_Zg9~AiJL9o4uXu}lA5ee zn7y2<*K}LmUCi`-J720t>p<74@euD7qHe_+#>mFeph{$6%Mgp6zy=j`_Fn=bWe!13 z8Z~bZDP(#Er3>m&Wr`kKz#UN*DAjcewopK%5!5W$Sb=uB#Cna~24d9kZ{%#~OIX}d z@6o6c+p_`(Uhs)HtXM>c5a8p z_Egy`J&^o5d{d?uN7=i!S^t`}a}7PRyqKCgGh_L6V4&cH4Qtj!@|A5rYRJKmpF!Tad)4QiPrnDANg#eJ&XRt6Y zt%K2xuk3>&GP?1pMmO&im9%e`-r4NhWw!j#WJ0L?H)@stfS^|u5+Q1^82d8}OblK2 zkb;6*4K%?PT@Y6}#3KxLG!|VL(yH(SC22#1zoW3AZlETh81tsQ1wDoNQa)86;w>bo zh48ZA^D(%^gC9C=(1WdBro$p)Vgii$QyvI`D#Y_bVp5sO{VBy*Ah!GttyUvS((mv= z=94XN>Ux=hRA8(q71#tZ2EJ}ddB%!krOy`))aiLdjPyClSmO<~H^z#S4ca>meJ~Uo zglN8aV2OCE-*q$tF;i)(bYPkOPQ-wm{VBK590gpZg+#8pKvfrsi%~G3ioZ_Di4>7w8sX<=-?VIR$^Ml=9+oGSbt z+R=$0e)@qeugY41ugfIzaYJh?JAV1H!DM=qt~?@Lbxl1PRVRH~HLz6?+3vg}aF=RJRDQ7jy>aH$5=7 z=|E=Ff%kULZhCmq@e!=38!t4@1)DR$=4@sC<$YJM07)e+aBEiLo*=ho74;LI4_!qc zOI{oHJ7w9rRiMr58t3ZPX6n{vi^`^w(~r)45xdKJ|3nuSyN13OvFbwA*J>v0pH=~;>(z@0Y44p#=<6V%i>Jz` zS6rx{DVg=HxE=Z2fNwIWZ+u#ek1bFu+(?kp%Dd)md#fz(?r3-I_1XT!T8ke)_SyE9 zyM7#WP`t{vcc~+fz$Eez>$7z27y`1Rh5?f_fN`++(*1riGd>mfND_#%08I3BG85)a z0yB{ZgP2tiZRgsWOrx(H0WXc%n3{=-ri`buW8E}V+e6*ln{Y_;JFK%@BmE?WQQ5(9 zXst16gTO-MP;n4EWSJFoe1tKLbvL>p5=e4pDex2c9KB}38Ydx%nZjFx9BvzGvL!iDrA|qsAJWoU+Y+n1cPpng3myC0M}bYbcDp8? z&^sBtY=hw?cX9({Cd{Dz5&PUq?ki$4+&sa-1@A0u#dPrU#)h_A`~wt_w_CiEZXo4cbcw!( zL5}HmySg!x?)RJZscmCCsiO_H&)8{_r=2EKcg=yfC+O$fPML0Jlyo*F(({VhIX7{}~ehToyW zwqpc#zWj-ecOe;NmYI3l223PP6{hd9T66J{D&*3UnOfOf>S{!eGa)Zzd_a06k7r^g z_VoyR2E7aehSg2up!Z9nW+r(*rdiKnmrVsxCX#>gTJ-h~3!M#3NJYZ8Mr@s1Lku(+ ztEf$bIv=Ozr<#FaRBOk681S-#i48bONc30EafqwnaB`2)WiTh^T0O%;{1X~7&GuYrNG6uWu7sw z&Iba%SLYW`!9(Z~Lg!k{yoBjov%bwPWUQynUvlqE5V5P!OfR_g_Au*zE8Eam-ca!j zHo`8rrgFa!gp2HAQKo*!mQAfC;2VXrDJ!;5KIWGT_mo%lC2)Iazztk{fmmr93*g%; z@$Kce_;z5x*avD6q7^i_Y)?R2d@S%>;M||RsK(Bkl7zY0rFu7TDQ5UK_>TswY+IxP zODa)8i|`wxgN#yXMRSB56R0HF+RCbS9h}}V>_RhHw8Jkf+G|!PvhF)Y#mhqXDqs0Z z)s+(5Nm$~8{$#b)3jkq6jvO@Ar8_8h<$vH-+Fk4$c^Y1-R9OWV$_P-m;)|d9#-O4S z>OQ)EujmVd6k62PxT+fU1rN1%s48F>2*7c$xM?JdYAUWtl&7|!&>s_Js=90W)l=I~ zq-C|bS>H?@ff!NhxD}r&fQoPXIhLOEN%Wmae;@lOt?S(#Fl~o!3aw~jq#mKg6Dl=a zU&SzOh9ZNg`4B0w;ic-gH^lLOC{7D$D>ao~JhUlMvbI#KRoD*`OXJKJJ0E?ahE@t3 zm~w5A#sVaczAp2MkOz3Zlk#=+v{hQ=l7Jv#J1Ca35GVz}U}-@T+!_>&IsyWe!kr=F zBQy^jQuyJNk|4Aga^Ynea94yOD_O->?6sJ^yc-G(g)N-+sWUPJCj1x1M z6*JV2b8eV}B+-*%oI)3uL3b(|JuDQ=ogd|V2+c{>w`J5-6dxF? zjyR||fS`ktYP6!l2(@?Vnf|#S(NFr4vW0@J6cATVnMRQFiGjx`OvY!+nWDElSwPLe zXwLglSd|4dCou}KX-t{GY=f-a?VoRGyj*&D_^pB2hW3oJcA|75H6K_8x%~M9lLw}2 zXP!-O+yhZHr15siS2kZ+`l>a4--5ZuRX$%-a?x|a!#Q)=qEeDS*RIMgS(gp1 z$u8fr;FF41UXvWf{)zUFH%nk-J(8|#duw=Z%f8H(eeV@t-Etu7E&ae#4jJ#%Sh|E< zF@H$WnZb0)hO~FXXV=$BMJ4B-oqTpKupDO!eFBw$@{j#eaecP5DqRXY-@3WVrc7ni z~cy!bk=o* zEI(Yatix}4*Jq;8@5hsO>&*y%LZ$n#3n=h;1}Z$bd$70tWShI0HRP7sM+KLYSoQj8ncOv31y0NC0JUq*0tIhUg@i z)ac?=5H!0#RrH2R&4b;h*$v_y-Y`oosgSK8+C~!&i0?xn;DngSlak(0DP3025)#*> z0wi2Wm4r2DQ=UWxN+~32(kvArR?zJBAXX^pW%e^h3!0k+4KWKBD7YlDx`VTh|Ar3W zJ4m+W6}TmI7q%Q=1!&0ec8HO$91KB;?X|k6X3K?|6`XY7P*N?slZ62G({Sr0upezK z8R&~B4t3A0mVV34`rQ~-uurM*V-<$dJ{eYU`p8IhsmsUhEh`qO$THK4#dSb4q5MTF z(x#uCWd(0LEO?yY6?~GLze&W_&(E@gfw8ETnq`Gboa)rM+LOQVTib zIh4)|=b)+H!&Hoc%F%wWnGuTW4{j+GiqIy2Ip@?^kZQmF4@0qPd_iV#hh z0l=s~M)&HE~+gRee1SJ##Sy=R~GZGoppmuEpLwG~f1J?|`< zDxTUs-7+(J)wzDYu92yPLI<>*DTYE5)OUXWI`&fP3P>8=@!?|6o04gn{}6;zPe=He6WtRWCKN?HsPC}(uZ*hm96;3 z0(_kL$!((@b>&{MTiP2sN-gh}+B)iR&=JYiXtLy-I4Ok13R8h^qPdw1XtX51_yHUS zD|RQGgMs1^R_vq(B;#t@dLXb`H z{mYDoz&8+EAZ@SuPe>*SitFc0BJuqvZ<&7}<9ZvOVWWSL%DL}#yvM?sm$QMrA+${;i zs1+j2Z%@APLOBV;*j|){aFtpW_P$x*_`7Z*RS^mt-dhuUgc$i(ZnsG?HpQJ=i-D`P z7&-SVOz;`tW2#vyn4B}FnjN=lO~*C|9!j1o7WCaSEP9Jmq*X5bp`z|Wk8%J7;;Bj26TgIg>~Jani4t5CIwK z;$W}AL0ZC<{Yd^-LN&YL}*r6hbAKfEsSJaOUuCw8f4)YmzZT1ZJj@XNaI_ z8iSQ&G+@vS6hbY{q5sxo9sMgN_a*eF>qzNrAFdg}1%k;Qdef$^>6yi?xxr&yJ>pzgW`?3`!^Q{7WEwd9qig>vlO z#`k@8-6a)=zFji6WK(9zrgUKQA{{@R3@n)E1$6(IvhL)@b{h@{TCvM#d8f4=;Saqg zgn5%r)AITU)P_$wgHDKMD1pgE$IzfNlm_xj4<4>@>LM>)qzfXOt2LtobwY`prgQZp z<`FZjanOAHN@}`dh4|pwgi<-n_`>1|vBpq>#$^_zYkG%-df0VSetkk&MlY}t!YO1; zF^m-WXtTlQ8K<<^%JYvi1!(kl!9JZ?4Xwt=<0^(U+?B)h^Bl#xC`Mx)>re+hXrl*I zX{f9ZD|D1a&NR4qOuOl@*D#%$6jdHUhJ!SypF<`LYB%!^iPKglcE94H(Oo>%{&EYB zd%)=SmP{qN`r*upnUgd1vlVMI-nA2Z7i^LzIMx2ume0Ud;6rzFX36GsU`wH)yDSq} zw&*!mTV)K7uUBl4es4wl4$C{$^%QP5A^jg8neXe z4WbxriWE{!R-DrX)Yu+jV6GWk!h4n$74%+IbDm>aE&jQS& zBs;i+!XuF}oYWP&c>0w`FaJCg9)Dr=ZZvZ7r&BlVh7qX$v=XHg^TOQegK`D^p0gb9 zKY`Pf&me9f6W61+IG2)UICxX>Fk84e+mU9an9@uwkxgkksLG+l31tJ`E}qj|9}x+Z zFXHW&Xhxqx!O%xs4&#WM^%0B9$Z-8~_f+j{@%l+S?DT?VQ{7*ynk!zGDPA_yJ~KR9 zy!tI`rg-Co-Keo>g-IZ+?t89U8|S@&Df=r2rmNDPWofH=_N0c^@o^G$(qeIqY;W>r zDwGhRFs?2nV|HEHun_YM>WP|_(w9)VF^6HHlAsc(m*g~vSB;S1`*dk@%w?d)AgEk? z6`Ijt$dNls97FV`MYE`K!>y%@DI8lP*MPEbt&n$5v+fsmt~CErgG|?L5&KuDb`dL; z5eIj#LH6@j@6ym(=(ZYDc}mqp>Il6Qq4374CDfv{m%5rYLvlCFOZG84JeWNbnM@|k zG5bupz10JENu*B5fhdX5BJal=C9mM|lK!ntYne zsBrWdWU8_^ki_ z35#0TyuWO!_to{U?YOi9E^2C8X8m-)z=8!{KlmusL{I>T;%v>ndYe&dSVz@092+!^ zPG)LAZsu~jTvvxGBmy(ID)z?K8$@BZQLy@{l%gQ8gHlu=_MO+RgWHG z7x-F5pG!sRf5PbF%LCio-iwM9$QZF}xK|&yKb4BqFEyl?Vh6bXSkCWWvrP9r%a(D@ z%Ak^dAvL6aPQ;}u*ASZi>Q48zYF8rdJ<@a#x-8mB-%dDZknAw_(l3&lkpVU)bz%*{ zxdw|RPpXII+Y9d&=^h!0)7z`^6!A@RGccY(i|1SPb3Fy5%A=6$3%y@W;~{;W4+140c}u4rp7zZ2T=lLM>O0L>oh!KV&ctZCrseX( z-+kisC$5XpSN! zQH=ftv)$E3`c&{Wag~I5LD+&rEFgHRZR8T*>6zxg^sdem7Z{!s&$aCa2_FK9Qk2VF z1vIpmTYkSD!8>IpggHO6-_ZQJ=4_F{)3HR}wgzxi~HE99aK*|=@z5vOEEv)r7*}@J>N|~X|0vj0Q(3nQ- z6kgkawq@Zpp-?A?;hM+>n{CT3gZFek1FhPcN(O$wN3!u? zJ%g)k?%aZzfN|g0`l_GA7M{*L?Pw1v@pv);vEWS~%y*W3+mEn<;YwWYPekB;S+(&t z!XF8NOd$<3?vofwu)<7<{AkmO229xmezx@^fW;eO7Ib2GFrq+dZIGwcfamqZ=aV>U zEym2S7$wV7!ryXKISb=C6s3fKF)CIYqbeR)u=ADNOU%}C*LU<+|t8DBSrZN&4hIe-bZbc{^D^Prpcd};rr8c;D z3s>6w^h9cIts_-3J#u<5mQZWQO#(OI7gLNT!Y;QmKP*vOB!#&MXD$?( zj>Z?4%JJmVCvhY*EwDKL(B2!NuBSWob7zy6@tSfjhDhO_elWKT6b)uqlFk()S5n}& z^Gxi(bDoc(=v+D1-NB*g>{Maxcw0B%d6YZV&N&c2vo5?o7Z`${q@m#6WehPTM!z=9X^FEZsVO;HtGDyJQXB>&Xvb*$dClFJ1AS)!$q_;koLB z7bb7oQJHm?%sE$PoGWLBuR2#}J>_X@IcFefD=z@iR_~?wk;n9hcTHIuHp!+F8p9q~ z(cG1@pgWsPtUfqj@ZTaIYn!|^u?+63aI7Lyh7CuhDqlt$tfDrk=51h?ytUJvGl#Ei zOfP@n?GhIRQMlaZaU#dVycGxNEAobbOUlsN&wr{f!_+ zU?!$5ary$l7B-lN=?fbP%nKY?2O}{E+ykl)LDzVZfhXcgxv@BzWQ*Q>V=e>NE^r6a zFc)MMR(SMN;R7F}*uVoRhWVGl!=1r2tQgPL4hC;T=YSNGIhlcLqAh{oYxo&&q*An} zeg}VpdTP;?%IA%cSHO~#kMTx=VNF{Gc^ejG#Lral4Yl9E8wrW`;Ee=72XDj{IR(n# z^YTUx9{nfbjrf>8fWpu|l}2u6`mnOt)?})G{o1#Jn;z zUk4`zgu4xuG7yX;MoPj{1}Jp+aug!U2tRE{^Z$6e6SX)g1p81Nw!;pEq2^Z!BL+Xd zs2>X>u%GInQ_z`0wPdk9Y#voB5`zfrE~Mfs%u@MoVknApH$d*98}rU0NrXWcybZfj;oiNuZVR0bKVscpBn4w^dUCuh>BbM=EIh{?f;4MHuZir}PBel*7 z`Im93(Ooe3SkH8KDKBFAhKEEVdi7Jrn~FKV>~HBQWBz@MVnp&P-=o+fz8|qjnC8nJ zn17}B#{AQPV#N9)USK-`Qiu&m{1lbM{R2uF4^7)3hGy4OR(CcR&#wPC1I&d^X^?dotnKPH4e_Kwk zdGNhQ-rGA{-aX;@$OUTOJ@53-IjdowN8;|vRoOt0Pq z!{~i4!aK65h^e*cx|JXCl;;I&$~|s|M{GBaf0n{pUeK!=yHGDo-zi|vQK>sKETj{t zyWk)5rJQ5_*s`QnP(lCan*O+!498HX+@ShZ3LfS7Api6+KiGOethexZslQ-15 zVCSOK{rPfU$T6Sy-=6(NZYWa0fhrv)5rg@pO+W@FS+J0Mh@>VDO%=a^xk8t?kHP#h z|A>%UZojh?nBVPjt z^>gldG-l1ikL>Rd>;Jt%xBg?IzG79-H1`@L#%AoOq6@^5YXy;c~MDiH$s~uWYbvTd;~Ku=FK<&fbr|$ zDlHF6(HOVel^HhQtjhQXj6;S9L~^o77KUdbJp#aRiw=JNacI0IN7!e`LXz*d=$uW~ zqLA%Cdo;rQ)8SdOs93WK%3W*@h-@Bh$Xi_q7;*!rpAXT+4u}BDn3(KJ8G>%ZVGtEu z-}FCWc~mMWfkLrLiv2Cc{tE?vM?n=mgD(bkr%^*OW^z+UF%kfB9fD6OMuK~Tm$!OK zqAM5N$xFz)l>0{%{09oyuHvsL#<<>We-WY*mr}5Vf@Ksi)q~{}TR{OS958E{yxoQP z!PN~|y(zdvyA0xOLqrfGKXHo=W!N<9-<)=C{?JjLb}XHFCcSD)wq)sq^RA9rm~dpP zmQDI^K5GHmg#}MqaN(qdKvnj0AF^O}ZRdw9kT*N)G>%wUR^W(*Pi;sC8HeFKg%W)E zdg;&eJcT-Zb3sCI<5M3#wjd$k?&*GL-n?hK<%e5W?`gBWyP_8HKWVe=+2Z<>4GxNL zw{=<^z33_JLT9n-8o5KeC`F|nlPvX^Wm6BV?0RtOR*y}#_Si>kP4=7vw$z7kE=q(9 zhWEwKhE(_4WEroX^-OX$aE}YhTB1;9ETC49Db%xX>2<702lss z?Rj+15jKMpp`|f8O`V;?ixPK)B7=MTI||ryRsul@Q;HG_XfLHq zpixxN`GXq z7lgKUc@U$0#kF$mvJOZll0umWwAXq>Hqq9p(0*^sp0evEm4!5H3n@^LPWhPQ4!cu2 zgiX^*)nR!%ia)`vPLZ@Ddea$l_1qc;(b_G-_3?&frWYNVShOLbf&~?Kh zL36<}$R_rvCMmWisqHZfNttAeVK3==L!D#-x|EAq>3Q@WB)$N*;|2*A`iWlyAraH> z1^0*&_I(yO@=3aQ_h{@yz31@tHVdeFv^9@x^2kqt^*d)^E z_f$Z%AGjQW-!xw|VYCGXHtQ)w436V{a0&lMg<(-vrvfjuAm>(*UcVBtGVIq4?3V*1{D@%-kZH~iRT20egT-N@|3tVq z2|I})xO(bmCOD&BxbR4j)(-Ql-H>xa){-Fo4IFYT)4|rNVueDcIrKYcrDY{oME(ZY zIm=;QKBuvm(IaEkew(0ZC%D#7@Du`^R!OEo%)*%r9+mU-(6OH{Nu29TXW=l5A|*_* zMHJXB$`F9$20qRcVbx!za)F-WfN42QRa6Lz?m;2FN$`rjMx-4hMBXYUc!{2i#b59w1t~m5WbyRM12As#RZN>+-Td0lOFL(MP4D~m zUygjY@AbZaZ=c(`KZE~m`^P&!aQNtWiY2g#+Bj4F7tU5X>$v7Z&2;y-H_Rlzwas82 z3j5M^Q+>=BWgaKLR!ml$Zq9fvqjZ7+~)F*E7k9}bbMIfIOX{uP&Qw;a^~sFE8eP^t=oCQ zJymwe^ik!KnI$t1U-ryaZk)2sSJYh`yD&D>eR4&GEd~4kgw*T&SsNGy#ePQ)nuqhL4y4*G! z+?Muk`*bIo;!|pmk16;EcHq{F9qaeGOz&>&uPW-W)TfzC^IE|D}mml>bemomPu@3w+Xh>D+r=NndtV(VZDN|LT)5=B5Lu+e?_ND^w ziXgkN;=Q^X*(1sBXVG52X}M_rRVWmi@SFaL)&Nc7-Xfjz-9)Ll4U~#6@_P`msZxXp zTnYhIMUvnh5&wwD6=7w@ls_Yjs25e~^d!jozDJ?!^;|c zPSL=LMtV=W>1$&22&;5(DIu64r4U|>rXMe|64yd-K2-}Py{CAd&*Q25b02ksj5Opv z#|R`!oV5XpP`N6iV)~k|mRs1UjaV=Aapuld+d8PsWwm()@c?CW?(&EYT29MTW3$6) zKxJcOfsw&gZ&1{t+;n_RlC}@x5EJ?uI?3RuEE#8c__(Hzw6U!NXFxF)&oJA7utPv} zGy%0mPeh1gZ+I`8eD4iRj2s8dy{dR1G--{!}x->y?S_w94BZAN^OGQ&c!1V zGFQ9&M4cF~%3%KxhHISr;3a7C$^+QwHh?ZVP4&^uRf(AT&|P7<|FKLtIlqg#MMICg zSB@TTCsPI*Lu%KcXMw_Sco%E7;}OI$_1PdnX2!IX`}+t#U@sc$jfNVTQ$|(5&^J6t zY{6y~AL}9-4IL2{ZsTZEJ19z}jc+?^2q@xS-t;*_OjMp$nvuX(i=?mLZi%5u4PL5B zmFG`u)iVeN6!R>Q;L?=@VR%eo8mrp0xJjU*y&;Tx3-6aW3S$6gS>seN!mPc5G@!dm z^-0&x;fIm&{E|ehXM|urM{NUMVz*c@5AxBk-^0MZM%nuhDN$+MT+zie?mQ}yBBnfc2S&Ix!0aT z0PZT$>}F+&fudFEPAvx_g}iI@kzS8hGQ4q^7C+%8e^xGX`W+Ml?-N6(lOs86f2=PK z^){ljIWyrq!~>whXjx-SX3}sOU41vzYYT%p(4P#4Um%viGUS6fU=_Tk|GUvQkAC;@ z*B}4xQ?Eaju6%T2_gDA7cSenH;|F|IyC}{YhIH|nD-!LM9ytn;8h*hrkBVAi!K5xFSo`*}glW4Eg{rorU3z<$1vJOMi;vycIZ63Mi& zs~4ZsF7bhDwCgR5Mn)`6)*E~7ssXs8!dp-MydEJK8t`|GDs&Ns!F))NSVzpl9>jCz zQ)lxgVa^hZpB~-+GXba#MV+SsO`U-OU0TUUjM*NN7hKdG#B+mejo?)Nn1XykTCQj@ z!dCteseX-$rfL6@^oiM2Ty@QEDqhZ~{o7Pyt3o_~&U5xK3XNm?oX3FrMZf-lK1_433QisI&N=(eeT3p2#L82?PbvNYLC#8Kyz&nH{vieLQn08! zD;Bq>x&SI~BJntZzYiZ{&FXs0^oiZ%s#`#?ays$abC;f*YuK7;*m_OE1A4XcW4qI} z-!vmHNOXTI@q_2T_x#+JBbhBnuCph+OMZ-^$<3jitF7eB`(e8Wf^O8>7HSKPIzwyu zKQ42 zS)e9|LqotkWzvIh(Z`tr`fpKe0|o7rhuAaR1qF$s7@xkWTKWImyVlq^uIoIryTg6X zzTYI5%cn^3C6c0EwFK)$$+As17VW6EC59DgTef5=OV*Jb$)Hjc(xwKqY*UD81!#@9 zHQgUIjiw0OKdsT&(T}#^kOpSG6k?)I(&pdrQ791lqu)7~OAa523$#W1qYLuRIp^NF zbMKk^I_I7__a4FTQ9S8sGk;0}Z!M4pMr_6W8PRAFz7D&IW%Ei3P^5_=$A~?YHoz%WqJ7 z`A1;9!LWS75>cLz->ZGU{oQuG=7?5vWLYB0a@&e4$TT$0~)} z$r*bsHq>u>#ysTPfJ{8XcURi$4y|g(4{Cqd`cbRid06W_eE$ecOahflk|p4}sle6) zmYP!8=)_V-jupH_wXV#gk&3*GTN1|Z&!uGx3aNcrd zUAf)k$0n}cbbaP{>_2Ne{~%hIKQ$X|yZ+4R*KWE#cc$~<{9Cim&e21k+r7D6cTe6s zedn}ZN$xjhLhTQ{@##eStarz>W5?(9E#E8C{na!6>K`=dokz6JBRB}$8O}XBp2+vi zI-6lbq8yVaR!-GfF|H3)Ud{y=l$@pQfzpp) zEB=>uAtXbE(XTQ^FNqSR9B_rl4YV=(vEtQo&H09TE5JNGie%Dj&?#>Gt$kJtBZSp5 zLIrFk6){ZaSDwr&V7MWJkp#^1>2yHT$Fj=0l6bITY}rbv8MLzL#TJTllMq4ud&6%l z69-oJzjW0x5-ug)N(_3P6v$7kZP8zKjnE>Ue_L(|l$F%HPyu^Q#tU$1c2zCK$=K@| zsV?OMLC6bH$O$E|T2cZZkxqmE*F5joY)CtZue1kOeQZDhEdPoUHI(vPyTVy3wG{k* zeTn{S<$=E!@eqVtnZ~?EOQj7z_;7(C;JP5oeoyjuupR%#s!x~A9C;_ZQ62uy z9|(2$M*0<5(3@-nAE9X;hLRJWF@>&{jjvW!PoTkjGu$a$q2oZ_JU5W0vzJyy+*Lmp zEVr9%j7plN|4H?^>2W3%hpBXJo2T!R`Mc>}E;=btSiQ%$!+Igzh=~gy#+^sa=_@+Y zp0Jwl1?UIA@r_kQ3(x6zJn29fCdCPzH7V4=!WN9&QPA1g1FPz3k#xm)7fNO*-S7rr zH!L)bCi!)h#p&&)LYM4TxBoXPlK+=g;{UHWJ^O#t2Ogd5LbI=_lX3HHgDR5DJgH9& z4#9Ch9FIU7_$mxONl6LYXe?R9f!rnirl3h7<;q~^Q ztncL8wt50tX|85ovKS?UQ_7eIP_`a5-A?lGb0!N?46Pe2M@wqd0XPk!kDXT(uR=lE z+b7J-!$MZ!Me{V)F~tNqVSzl_FZ9{{ESxYqGT+BMBBX{tL`oqGki^3Bh%hS65=u62 zCYfSYk|>sm5@&v&Qjw^EGc8%RVA~}&TVjcVWeXBiO>#t$?qOl#AVsVf7KC&_Rv;Th z0~QI2ZnN#tPms|^BpQ$`1H&U2Z8_A@YY&o@_qN^HrYBppWXr^hv&p?#-_6Ka*-r!E z+p)2j9_Z8pos-?OfxS1&e)?;UpfrDrx_&(ke=|4UzW#P@K(ohndzEIddavvK{qOGA zYrD1D?iqXcRO63c`SFF1FU;&ax%p^H`am{0)$zdO;?A%*)Z~LiJw9a32l7WJc1`Y{ zIsun<$>5@74J2|0QI<%;umd(6QXuxwhb%as<+$XEaS^~FFdqK`Jgo&DD!7Mlr1M?EvD>@HcHcfQ zc3@GmsL4X8{O-5zUA=R4Ce)0B&=;&y82)(?@u(1Lei%Yo9#WkaD0uWRUdrjwFxfW# zy~o>Qg&)1hQH$kAk=h=g^%G0S(Ot?XT^7(k>6QuJWd;1HRq65AKlLd+aeI^8Cp2SE zB8w(xZ{}lM#RnBVJ$-$>O|IVFo}OM7{CfZZ&(NpJr6eHrY@*?oCZ)G@SCf7HExk=v zk?}8{v#w>-+uPem8GT{Oytl8nx9J8OYG)QuF8)@dXdA+gQDzY7GUqAyl49PXfTeOW zDdg`oiXMClbN<|Kw_O^5+?@FdQWVQ*f$_#G89Z3Z*eD<=A%n&(Wk|+gJyTJog-8F( zLjyQcb7)woYXprP^06N&Mu#q+9lFF;kq){8Bk@SlP0)%7Vv)(T|05v!D6HtD1VYE) zT?K_2h%@CV65xwIiZ(5+L?)z;DB39sBbsnk<$mGGULAOIsOW}^!8eEcUmY2_bh#L#%h`*Wp|*Y+m9eH8ZX3P=V*)aQ&HREg z@b(UkeCuN7^+O`Q80tTa^c#rI)!U0Ux*x~vB0R}{GBVU$GbGLw)#H7AzsV~`szByh ziuh-aI5;>6-VgT-TsU|BO2&tmiY(Ym$CJ?FWL~C}uTV-~KU{ZV$;TTH!~Olj(9?r& zhK`nAPv#t@zDo_`0znrk$WTC|cv0m7VExGq6yx>pQS5gqc!z>NqTo*`;31vHYZT|? zQ&`2b8j93XnZKZbwN#f0`iO$Rq~Na*kU{InQEItAB+^S1{0#*UDWGFOiVE*rXFjEJ zmQnC?L>2AFd6!_gsH_|eLNP%K{)O)RR|?oXww-9*2#PjtBi1K}u86~ok5CMq3@1!z zpCf3^Ln_ZUdm>y-+YuK!EYR=|ebd|}W z#h_L0gck$ojV@#_=-#yEP3I%C-fg4q&#m4$Tg{x!KcB48lO0;JW7-xgL{oaSQHwU} z(N-)|(LJNjEZcS}4b#ciWeL~i^Kww}jz{!FiQM}_977$4RgHHHzm20kYLjgu}qtCDtotGCaTXCQFOO^b0=E;DMR{RibGYm;10Yl>uQ!Jd-6 z8+ihfi6_9o6V;!q%bqYOxrr}?osgxj-GxNuf_s~clc3kL7bH_wz>pP;b5mm}!%mok zJm;MnJ4N2}@+Y-OzRu|ynBT)(K;X-8%%#RDnRKZ_S2t*2XjfL%P zh0a}tU|bK@Xu+C7x>-+mY3Z(o>Zh!-3U~fDzJ2}Mx;?4clZKT@^KP%95P%AKL%F`} zONO0MhlCg>gKHIF)PtHEDuZ4rR&Mwh^h?pC5nvGJ-} z4(%yXCcDISYDiq~+m@JaY~SJqfa&0V_uhrXzD-O9g?WrCSnN^_J3)>Tvz>&wh(m6I zJf*gx66PgN`v~$AqXPs5iO(T|!kbtYA+q2xL2>F+jxmSsZ`S#j=8Rhd^bSG}Qt%zePH6NDvi zblf)PG3Ykbr_6n`E2rGtrzz1TH<1oVRka2DrE00s?s=?`uBE1%t}i63V9UP{dqtLa z^79Co4}^0gV@X3H1dmzC+|H;&idPs;23^D!H-jF@8^jpYIMztxP(Jj2{N4Eb74KF| z2U|6@Z8EH>-G<63!PVG!c&x(kF)A7^gF%cWMu1K0PND!tUTC{^f-o3-Wz%(1LL9LsTCSTg4~+zrJ;sR*HC3kF^_tSK2kJ0@a$`n;X&GU?h?7LYz0w zTtBnqBj#+n2$@$LoLC_QHt+yP|S`HN2J3oB;&)?SDAD?M|yik!Y_=7YRV)=kM z0?kh>>VhF?a0-phA38p8=#7VF8V@a04by_UfK`H(AsU|=vyI$FYB47p3X@T8Y@qXc zyLQG?n?I|0nhd*$!XOj4b>_QgbhTkdZI}pYYK!3%QEPP(xChP8z~`-aBNX=FV)3pcMo7RI!o#`2?0F+1@Mwv(F@eWv-IEY77KVPK zT)-75j8Q4zloX1t&i1uhu-*_%f{+C6SBpf@)}RA}%i#4Yf8$m6)IU}iLQlwYfLTfa zRvf050aFDzu%Q)*iQmpKqRlh7#ymzL4vMO&jN%)AA#nH@^;5+H3y@IdNdq_WczWqZD^NuX0;KZKEo^i6+ z3X*q~2O?F%?Mt%iN~LcgUiSasB^0TPru%@jl`6lP2C34%wBL8eb}md;`_?1PcfRxc z&h@*UGcW!AHUj0uOEGt+osjRb(JZb8vs)Jl*&+&&i6SUsUMPsNSg^@90o%5`U3T!= zDLX`RohbGVqBy8yDbSo~uU>*R4wn};6Olc9v=c_VV072veuOL?`4DT3_9<@aS3K0S zU{~5U>~cWy(xBpl4>70W2drHQP!F_48l=Gmu@9z$-a(LbD4kr^PCG&FhyEQK4mm_U zOJ_e^Q|(rEyK6dbEapBckD$U0umj!#-PDp^5DN3G9u=?(c!w zB9zDi#Eqy3vP}_XyJCY_vCB@yA-fc(>{eW|M{&z-ibwV;ZL&}C%6`QMzU!BR@PWUA zX9nd?rCkmw9deh_DR(O&`H0db_t0LYdxN}aEGYLu`v^Tsk8!USm7WcIOBOEmM*0j# zDhsDz5X__|AmJQFUk$fib0Bdd!N{+tn~oN z;`I9qF0zY@Y>|=>5fiol1POUgo(qO8o7Z*hF8z>+J(x!fK%$;L34f1-Bxk2@-8NjQ_~f0L_=MrU-xRs--%F)t z;)hlxsnPLY^*Q|PR`Jo3{SkNMRpp& zFcMS@*EU06=Hw_a`qw}<$lE}$a@{1s_R7>-PW*w&=-sTj(a|kQPAVC z>|*R4%qS%>Vq>QOF87Q|n5?*;OF3O?cu_PFv9mMKfqjJJED*z$PAghAoj$BAoQF@A z1cryg1~Kd>%w3_-uovmWB0CA}fsnv(;30uG-~NzTLiA8sCodWY!hk9NZCq2=C@V@h zwUkqfnv_w^V?^89Bq--la_+p8k>=*~BAc5#FBKn9-XHwCabk_(Fc?l3DTBHtUeH*f zF;Oa$)2R-#KthOeMMd|A>fD@^Ti{chN-U_fpsBg+er=jNO7X0q^BwKY#lv@QF3L9@ z5xLtLo&}~A(s+W1z{WroIv!p!y>kLshGYf>m`W?;eHZa{Jo$9<)9uTzz0!_d;_5dX z3;E2VZg?oH$Jwk1-J1o~jNdL^m0M&TUS(xiXC2;ZWys%(4O?Z%>5s&{d}V1Iai4MQ z<$s$e-KUhbOn#M0RK;|vNb@C$Zw7R)NUg0k?Z!oDI2CjbrfFIZ{nQ+`kfV8}*}XO1 zVo2P2Ov|Eoaa(c5G;j~JVk-=;Nf#vWjS=bcg{xP7vh`|kHxb4!vpTR%W7-RuWeNco zM=9PHhJ))vN8N*`ni;;0o17nz;ZtM|J{{kR6;kgdfl&4C=I@^NJ)8f_z&G(%($!bK zp-TKaZ~K#>&7mrN?G5kP8;RfsjIoY0e94$*UIsVN9bj;A861gui;dVqqc=_Yr2uj} zy1*mk9LOooGHm*hxGt=V3nE0KtNgNI?2L4lZ~pwAMI%xXpF7g$4|3TD4eRTBUeUB5 zS4JaJKZpx6YR#}~7@`@EKh1j0C~gKHdevEQtMeH45wJVoqZxkuiNlV8n##u;(Y&e` zrFkli41-6YyIvdSKEW_#*fbKfH{9QCd)on9qY(^Y%@FC@K{LCHTG}-7Z=uTyeEKjD zFme}h`yYSyWvLq9p7`UZPQyBebW}Kp57eX9((0EUKxAi z^FL;fKdB_lShpA9bz>dh_h1DuyuiBF_q{xp_U8iVfn^y|Jrhd8zIeyS&7pE28H?pV zHIs28elQzj?)v{2xS8)8CdJLSA+ zHy<5tbZ)Ay77QD3hMzZ^?-0YzC9Y|;7>6#6YoPVmGklQB_8u*`DZ>WW8ovh&8@Mu0 z8VsGvu+M8+-aJMGSH~>EevSlhS~iFT|0-Dw$ps`IBf&pMb_vO4B&aQh>SLcE!T&*a z6$$`uU zexbjbtrMu8n$oj|ospN>BZJ)QbW+r{R)lRiPJUa1qPp4xf!4LshV zRX<6QGVHtD{ zxqIA0o*vJTx5vxUOoP52Kg(aB{1z&|Mapln@*7ZoOO)SI<+n`vEyu65r$VyzRGNe* z1<8I%kQ@>Jzz+4*^xuJ9dR&dYW#+M->PUs;>^DiSOJdJjCGJLiof7vT9+bR2HIna= z&{He<5!Rud!)i_X@4!=fT#fZ?xKG|Z<=J}_;5`c8GV$_nDb@5ee8gIcP)l*7L48ZD zp2n!q6N-2uUewpE_EG;GcutS2G30z+jqASyJ$hV?^_waXjkuNH7Ade7_fkpEX2}-b zBDF}RfaO;Fmf?4sQ{exk%~JVC=uPXW9d%Ybdaok9L?ud`R(q=d4h-mVHKvrPLWw~& zP5&K;>2WnCnOLvZM0QElAGy@p`;?H?+Vx+hopq6h{sJj@$=b8WDM(v>CGe|3FVE>c zQDXxa)R_J&eRRmt8@Z%fywSd2*&EeG_Dl7V$EEf7`*=O}NgL4PC)m1ez=~@Gj6N#< zX_rDMm2WLx(jltGfTx-wZp{A2rHzqRsTuR_Bdi^1on-3wNSn~-juBI6>!_=<>v&L> z!pU&Ud(=c%NX&YpLucc1vUeyh4G%^_=B&9rHj=e;M*EUkYxnTk!N_|-WbN*w0_r6y zk>vtJt_l$Wn}Sgq7=t(quaxj8cMyQem-Vq((g;_Px1T{RIhy-Kt*e0c>=qQ@! z5OT3xT_H=>(c4S)_x5I8y$YNV_w@FDZa6%sJhAuoO7Xtl-W$R)!REv8=F{<^$mT>i zF%*t%{)8MKi1Z~Bo1cxJj3=XgiOt92u~YHR1Do0tiRh`=P$ZVz+;T1wJGXf-dUEsG zk>u%kthHt9w#|uXGP3DxxbIB(R3x!kiX}Gl-klwhooI&zZHOd*C521EJ(I=ikp1eD zWg4hppR)HME&vG&0^KkFQv_FqF)>jvCJd+piZu07_mJblm}$URgOVW$3W8sfn55R( zH72~J)HNomyHTfb+1zJ3CG?qI1Z6UA9uvmQ_$Q8{M5Qoodc|YGvvIRp^1;37Gl?ws zc_CyPeg1$P37-jugD0c0usjk>Bx#xjqcIF^-|6AlnFI|!k6~;P3(MiYWJFE`6A{d@ za1!Hva)b?aIG6ySBQ5MkrX3k$Z<8W@aVe6u48)_ctW^%jPDQfj!AK0xEHt&VPObK= zQ>im9XT@m3f*RO-rA30Na~{+CBz0G=LIQ1-2|@!Gg}W}nQ?lSMzWn*g&wq71Pc;U2dLL8F5{Svnbc7| zE`-F8M{Zc;GQ5XeP65#=S<{*GE5^j$i_FL?*Wme4{3X^RxG21j z0V%z5dcJf+x^%;AGE>?-7rj-Had)IF9c*#^lA-#gheGvaI1$;=_A3CajklC_p4`#K zhA|?qBS5X3+&mk>ay=d&UkS_p`@=FpE3Nj0K5a7M)`mZtlFK%v8Yk+QkW@%zK~RZi znGl$Uj8ICsR0K9|nCK`^b+bz?pK#|xU*lkD#>FwAQ+RPdkd|r8lys;i&k1tT7?`Fc zEhcthSt$J{q&DhB3GtQcF_UCI^8)5y05QuM^j!9jiFr%$5<93?W2{G#k+3X>N3zb|-Uw?{%9{I5%h=lOpW|(0 zEy>7*WLE4;&_L0)%+Q>Z=ckE4+tLaACBB9Li^?WAil!P~YrSJ%w@_4ZwPm^`Q&c-) zUv!sDoqBELjytqiR5kNtrl@|x{;u15v2(#2xF^`GH51~3+jGS^xqrb^FxB~5>&)lA zxnufR+EX)$EWWAwuY6`^>rC<+yYTo*$Bg*up~x_Ns>9i*}*}3FJ zT}wW}Q*iMpU&O{4NArixIN|_|Gfqd1IvSM3QEX(ANz#xZW=Vj?99x@6$OhxS6K^iQ z;u%96qpECnw0RvDFrrA6#7e|NmeH?vMzA9dMM3A#&_-hta3=8KDQp(WNFs?1DoKyR z$zU`QYXGtx91M;|gOLQN zpVJ_@c&*eTN2u=+P~Q7FF1`brs0d9-7}w?-Zxu2dotq<-4jgKIvQR7lsLI_D*V;G3&mwK;!OLj zcd80Ls|z{9f{ z!vUZ1j>jbn@~U+j9uE{5Zj`8Gm2CYs$$rVwjCfaudE-DN)99RrUbpIlAib&@U=>% z{T4_ytUYT!UinBR%9o>jwN!zbvR0}@xK64<7?jo^tdXh_)=Fy;)=BFS)`R~Sde%!d z2seOp79b;PRK~LXmo~raFh8DMJs0xvuO9=BG*9dAGXw8GyRDMY?~~SFvZ@<=&qjg- zM&g&X$~T~ok0=xS7*L@1aY+q^_i2G-p%iOIz$^&?6Ksw&N==sp?R~f4exu=jt2Pr3 zKt@n1Z30|dB7OxMY^L>x8RwF=7~0r|I<^|>XvO_D!+jg>TMhTyao=XR-+}w>hWnkk z?~ry}6186LLds4O3XTu5FaA`TPBW7-iH7W*>2jE zL*^58z$gTgAcAqu%QS=ORj7(Fp;x0X^3$g}&4|2IGrJ_v5WN=lZbSr=d+kUpnv8}A ziGjtIZ4^%sf7ztGQxj1G!4oII{GK=gN`VmrEHeiSC<>wg7`$4zbVDglkhp(odP?Mo zp)i%Bt1D#9`g+5OUcDIx#;jLMq|ywkGRSBFLtsfn2K#kj=aP>QwM6lO%d|)6_R*~MqlG247oK9K~L+{maRH7K=YZpR_k{-&`&+b(*U&r0 zA^&ILgNp*Fw@t2vmaVTmIn^;!HG6X26S`|g(qAM9!e1(@5HUHN5MRm3cziICwSr}V z2*ukk$A@|;nZ$F{X*N3{P=g3!sg)X@2|B^!B6gQ$LplQ-NzqFgabT=ZMA)(B5}dh%(Cl zL1HP;|1?}CBe#I{t<0z~am+;6g@%iGxf^0v$RtzU@?iuJV$cg`tzgEI5H+z;q$!FO z#<_ey9zXKr=4YQp z$TuIblV{ID$POWE7(&xXU$j5k$2Qr=uuIU+Lt92P!8lU}NY)q=wX$u5H_{ji_JeIq zM&mJv?vL;tMkb-ES^-4!Nv#rU9y>ehM{^LO@D`Sr+G^=~rt2VWd3@uJNK6%gd;Eul zzkhfzc>Z)Grd8EPoVBJ7ppnGhIXD3i*I`;+GlJ3TEKHXLbYP#%ngA>AMa2u+}H zEE(Lo1Co45jpO~=SaDfaSmweJ$k4f@pQa^2go!{{ZNq0lX&7q2kdqV)CAGR}XlogC zB2ukVJc=QVjpQ37jhNC$#F1+D#)Qy2|>8=w@i`^ zBZByn2yy*EO|Yzxku<+ajz=`}P&kI}_V`lFx{9HbGHA+*YR zFiqkyOctVs`;1aw4K=QQbGaKe%S5-|u<|~}C?QS(>+M;@n9hV&or3lqz3AKds2tHq zQRRMtGAn)c8Ty*j8*TrbMQ++Z<&yhx474APS>95RHTE%4ry}yHW7IV`ZoN;9)|EAm zSqDh^yW&#oVQcjZ`DO;a{ex!)6DJb4(GOG|j1MQz4r4B1$e4Cl*%~5JGa>F1C%T}@ zMdmn`p?p4?P*iOMa;RD51p1dXMdMlf0TNOkc_!-sfy2;6$addh%ZmL80yfp64joN0 zHgftTquW10w#1VNpmHmROmWF2cndEdn>;pCKkJy=aI^W2=W$3Ne_Bv<)iv#U)ico! zkurN;GxPlHxw)}B9!MwySNBZsd3A5fvyLxI&y%2|FUSH4p!hqntC1${k#Q91QEbWs9-5!N})^qX010FlX>+ z2c@m%K6pUevT|xMqK1&j78FKv1bvlB8KSd#H#nWkazK=f5y@r#To^WDN&w8!0u4PV zDSv^g5|F85@Gx#bm1C1*vpXinGXACs3zVlR|GJc89l}L_)lBE>PhWd_uKt~kH#Xi( zX8e0oj=g-tGY$`d6`Po_+=k%(!y}1rYlOT$Kw~B>8}sO^>at;A;{*{$rNP8-G|4Ow zNE{(Wp}uDn2edUHK2Iyw3I-Ypum|Z0bc{U;o7?e&9YGD(u|;^FK~a{VzDIRqf6}lT z284~o7~U}#+Fn4LcZ+&CTC4$FPlU2UNHm}%VD8aie#=5o-ko;73#g%W;pJ~J#gED2@6k89KoHoMwmdyZJKdiWPkqymO$xPum)OCiM z2-&Z=^428e(=oh78xt;8yOlyi&H3IjUmt~hnhImhVw6dOMjL!;KcL`!`r21gxy_3y#W{*aRsIVX@&4pVTFf*}N;KnO`NzFlA4ax)$& ztSl=Ej-Ph=+*t5_)q?uLax>Eyw817)w(y}s$%ruoXv>B`MFZJElQ>A=ogb?LyqiG%NYiWhx_mt&K$ zd0&0nR}WI^h3hY5eB17tMQ`i7zQVg!leY+5m%r%p(B#m(uP*JYo9%q_==Gx+-} zvv=!~L+}UgIxXG+kd43MuG{LZc;ABpQ}ths-u2qN<K_*vje^xm0A5AkLo@;!3_w#hH08FMU`FH)Ue_MHIc6ENlKxZnjag)I#4=(Xvm{JoR@s)+ zlyF~yOk~W=7OyyBWARRu5J#*dmb!;6kvw}}y&$)Vf}t0o{M6Ay7GIvFt}Zjc%sg)S zjPT0jdEtWjGs1cC7u3(Z(EUsF(~N#v(a%3r`x&x_MNGvGW>%-mkhu*V=xL(I_|jM9 zyb0L5A%(aXa7SFl%;+mFJ~wPdhp@Zx&5v2+G2^@N$wYi`7&>cambXV^zo-UWaq|PG$7}|h^_)2s?c2wTaRzX|o-4SYKhSr?6@7#ropYXKS*S2m zdHdR}N&=YqU?O-jg1wvo0{cS3fE<8%URiINUis4yJt#Jf5P>i-X+T#E7+HNx2*H7t z!?sc-%u-SVEKDZ>J&fB(@H|W=^|v5?j(Q!BCs7;I0pvjKtQ;pUnX{549>M4b!{@@$ zL81a6Hu>~vqTZPlhe=6bEf9uT2|dlJNFN?B_Nb@pneO1JVOZ3GAH-XtA167-uFx`S z{#?);;X~d(4C@7A<)O7vGa9Twxk^U|c}_=i2N2*z9cWsLCYVGAvLG!xLyU*IWg;VG zlAv)L2fKWc;wKPfEi`4b|qQ8P0rHk%T;c9-A09Z|&7>RTEhYft{IT zDhx~3%GOra93PgmRt!v3&Uz5nif4T+QM+Ho%2;~)hR%+KoOW(w$T0g{pO5d3wo?O#kzJ z->QkRy{NrVxK?yv zz3`pFcHe(h;703*o=)vJw`fH6E}+^P`%}&8nOLR zO)D;D;8SrQMoi30snV8+R&uuAJk*}H{qn8%K{*(!&$e3XJ#D+7hK~!_n6!;7A5EH% znyeS(7Lf>oJncQCHWG@hxZc7vrEA;*;=qK>s9)WT{t|r?f5CmTqHoy9U->PyZy|Hn zp>9yV8_{$5F$zd+!v%d8DRzQ_S19-m3N9m1WfN1B@G1pYD43?;Zz5Q=q;0UN`y6OF zMOe%4o~32|pOBrDR~0ks-zd4`Z&)ZUhjG;9y_0)qw!gmb+P>TF#<|XSj@~$WYx_Uj z`v-e(H$O=^%GOMHpm&(?EtxDUaln$pvU2%@wDpwF?7!^}QDIQDk;$=?qw0hAOI9pS zlcBOVj=%Z*_2=Jw@%oFIqE+#y6&`G6`1SE? zd55COi9MSZWh{@x|E~t|Hcuhq!ExwF)jb? z`vNv+UJv5#{4WTwThspfl%xLtM!>%C2!K7616aDd2*Ap3&_K}Xp!_)XukC8@d=!x6 zEaFqRngua9Q}+cW+h zDaQ^r7qaGL{7lxXYZamR>a*)z6$mhljDC=2Eo7IULOuh*^fuj|qJSv_e+MykN{zT# zT*`R?W)8$Of`0xITEf@qEr^VTPB385vt?~ixF;fVj=t@Y`Uvgciay>{`?ym3SHQYK zI*MN2+GwevdxrGuhF%ur^zzL4uzV_!W9UT_fp)Ite~MCH7QQSRr&c~)`ea^)M4dGG zCfm4aQtT971d_l(JBY=(gzXh4bd08RqP!ugk@u1bwQFnwdZk?X7=~}sDF!n_({EbU z)*-<&OO|tV%3+tGR_jNq)wa4;i!dQh2rz=TUvi9@bo;q+i%G~|_n75t!nk!(93a%T z;*vy^$;Y6Od=}TZZOrz?vSbmzgT)`6cUWWMf>|9@`1_@6BD8dc9=04*Qy}<9XU$JY9R;ei_6fmosGxn_+5|-f09wibQF6| z>e(ubP#ge;4mUE0<3yTm4;L%3CBV%%pqnBwi}DiC@ScR`7fovYfLSVu51UASsN1OW ziW8*Tg5=2{N7PJXVvqwu-h@+0YA2a6k~f_gKFN()A=+vp;bD_Ox9~M9`nsVBurI@tRFD@&D;K`ZkeZSr0Sk&eZxLynd{30 zx6K!{ru|QG8_r+b5E4V~tR3Tld62~MR#@tUNm3_c!-X11pMY6ky9#4w3nD*5T_xlq z{{aOwG;$9GpGAPZqJ!e>ef|l>i1Nr<2w!GRvc$5py#m$(bn(5yuDqM71yOF_qWAh4 zD)ObjgOdsZV?|6?hg$ZOe*aFdYj3Qb4?U3%J&|f`|915c6F-ckj(4X5pE8sxMBDGL5eklqw?^;y zpPI0s9^aaY?#nMtzBJRF^43n6kWx~0^{MHnW#kR%XCqLfX z<{}w?TguV)fBJ>P^j=Pekh+24*FZRjZz34ZoQClFTY^L)yP#-oG*J z-#B+L8_9Gqm@5>fy}Bp|Vk=p$2mvmugOaGSNVO0ur!$jp)B z!cwF|O2|l0Z9wIJL52U2ir7x_HA3KJ=uV}LY1;5UDYUVc@Gi}vUqgPFG8G7(qALTl zC2yPWlr}GvR9<~y`h{0tnmn}VEt|38V68W_SOO8nt1qZY!*{&vk#F+QPm9*fd^V#9 z`%6;pnm799HvHC^n@`=|uzwNHdNM_gDNo~%{bjFp%-X(rbZ+bIHO+XCs@{<)+L`j~ zT=17ncvmk+Z6L8KmSM|s*XNNiqw z$G@J<&`p`5mXxPu!Cf%5^((t)th45CxCvAcU~f!$HvW|Ym3#+pz~B@8IRG^3(c!ao zhx~8o;i}o>Qh~B{hiWXRr#K%SEY@#$2`n#VifDTM6$Q&6?Q1`O`R6a2bAZ#j?P0)a z-L?`qt=kF-I6EF`&bV%uH~lq$4lQtk&d*=A=fJ0J*Tdk`wreGP+ICeuI(+=M%QxqT z4|PesH2iZ3DIXjEn7&v*=cot=fdtSokC6m0vplP@ljd3oWCCF)OBY*mqkgvWg0+qZ z;iqYE(3pVZ9Kc$%_+ZVd?cMPG6F(0bUG&uHm6q+a{D8z%?nJq*A!t60TJDdbd=rn) z49_OM@p*tURSmBgn^T_6P~@cCRkPMP%ir=j?gC|g5))GB?u1!9RsQkxt?}U4gWH0R#`zzG^UFi=SnigG{-oFNaSFCT7wgi z^XuBu>)P&kx39?b=HT_gOmS<<(+WB=RlNYzVQY&z~xrlb6yDLFqWtIow8_nC`Y`VPX@|Ai_d(QJOL6UpNqwkyac!rKO{ zui*=vYr>lJa*F9BP8vtTF<2opn$kk|_jv_Ijni`@HBR^Kgs|73PwMjNzT>Wisl>$7 z#4hz_iZ-P@n^wOLZAgs!{;!YnIvb(F*0wMdP%eg43M4h8cG=VQ`{b=;rS$rsl!98(gIi|X45bmhV@z5jk0&v-B_0?-ktL7 z<~S~K$6H6}Yv=UN`QrL?asBM4GR4FP7+7FFrZk4?rH$!JNcpnqD*W+41%aukzI{I? zCG45h`~qyK%3y@7)iGh}$B06W0hNp@RZ+|K!7A#wpjjTRa0y^RolMqDLWQr3nyeXv zIu54C*a=03?8}iM%d~M~9WddscrNPzFu9b0Iu{HYCC9Z~v&G;|4nOnAj(Z5|v?D6A z4m`~)rL-J)RW9k@$ep;P!xb#vqt8Q zmHZ?%x|t)s>6+zva%lIodHz4}ZFrYoNHAK;ijSs!kPDSx0MgFZ#>l(%Jy+irW> z$ZT-?hY4B(zvl1=uT(dK25io?C8B=NA2a00#X#yH8Bg@*6O`&cJYYJ9BSBn#4<{Fc z(L@q{M4kvp^gshvm@9qkj55X$Q)YTPw zoQvz-$SD)pWZIeJCn-2V!LL*BJqp4U{E&i93ZA9l(-hD$;ipE}%I~M!S13qRutWhZ zDZcb+hV$bj9x_ZPDvC){=@=j*nfSq_FVO8p3MMG{3I$)M;3@^*q+phU-=g5RDVU>x zR$JE1bVX2+osLUcC$!v=7*37KS!cHzl^FsWr_8obFs-uudsO^86tq)tfP#Nb!EFlY z@TZ)mV3C4%1s-edjQMr%H7{3XEDK0jTH~;8o$Xi_@B=3*jm zzUr8EQ0=ppbV2=+6^W#Ys92)MXa-gUtuyASU6ZjTC#ARqmv`Asv7oSR$3nwK7!}`j z+fkLTaLI~alm@zOoNk;iYDgC~%)u(TWywwt9P}PJtvM;#C6rYyQ4Kh0hL`eEtW;PN zT-dr@t-jP6MDMmH@-Rw+|911NQ=cz`j<* zDA=}GOYccyv31S8CaZPBJ)hlLf3L`HU9SbW6|Fnbbz32N!G>U=W!qi96QIi-bEF_! z?wC`WL%`Xl7bjnw_tvMq^-E4lbFG4@o02^=f?kUGgz~DZ7p5;<9iJYbO{U8>E>Vj( zbM7jmv{JQed^~eIMWeE^Q^ctlvgj*tcS~=yY6CZ>AU`-B`bcZ zrzJ~viaCUm@|pVSr(t0 zYeI~pB|=7h_|q<@5?k&9{`xWqbfaHI$#m+M=~dAbaWRj94w!+6Faz=QxXIIo4q2>#*0KO32@V-|RDMqb@@V2OXEel9Rk{91A zD3OXkMr+(>n-6uD!s{BmFdAwun-DIDJ>?KT1=zvp(ce_gVk_n%OnJ)va&?mv6>jK& z2t1fb%&D2o4jqwI1wRqP9bBA`z|P?uxiN=927If_E>a@FtvmP507oa>zY5Gr)HLoJ zjE;TQ!bf&2em(|wC5hT#qVF_3I&hN;?ml@h;K}XZA4jaKk9KYKqBx_U+7S6V*)pk5 zgh|Vo@Eh3cNn)q6AE1!n`nwlI3%^D*YRQ@yh8mSq6J>Y^E9nmW=$0D1FV7%Nv(r)t z*iNL4mKYDUx2P_|;%F&Rh36kYui_D$zDax9Q9e^PyWx%_1oudA55{%p1|zi~vgi2I zAcl!ZqUDna=;Wk9vXMqdxZ$=4rdb_yvO5aTV>!E9*S7WJ? z^|PN$`5WGDxLN!QO$Tnt2-5xoDaQfM2N^8@G(PAGZ~-*S=6t3uDt|L>AG7DP zkyM|<1+qOKDGgJ#op{A{58;1lFjibfZWMjXBT-S?9(Ulme?@WJN}7Oppm6n5t+X>) ztUetGs4@L_%&89kxGPEXm|bHoHFeDKvER_3UNgb`ot72qrNxJftJ_R3)Q#3F&JvX4 ziBcShf+Qobr-F}mcJlCry6(}2+-&5conaO-mpU&)^NsV7WM~lbFzX$BBLb3b2_|Q7 z(}Q*T&(WrQ0zuXp(U?8=@e#NhzQ8oz%=sM2!m~Afapo5oW*uWRxNo>`4&QFvyI53mb<^~w z`J#mDBrnspcCG0LbG&6Ug4m*^g=lhLj#^;Swk%0Xj#r6>YhWUseM9T>`c7s zlfnoqYKy^Y8zNUB(NWgZ1F@RE);Nst;S`wY=~G(%`>1Rsn241etM^|$2fsT9K37+z+^K8-M9@^d<>TTkExzc3Legx%jABa zv&f7qXS_JQ6#RQ~w~c)f_>1g?+XA*X({Mh)7uqPbKtY6F@V_DQ!6o6Y1z#rtty)w* zjWa!3saa7 zT-MKl%99VNFV8Nldm|aQ7)om@KE3&Td$=xjh2tx0zI~d$Gp7=p<5s7j@79tzpILX3 zs4I5S$=OpH4WMGgaa*!N%VC2Z8pee#g0>E8J`%J^VGN34byt{N!?HavCzBh~x@Q;U z#&HoG&ZGBO`b+fK0DHoPE3SN62bu}|4fDNs%nup3@m+wSJ|n~|s%#r4Y`><)$uLOV zlofkF6kG7Asjkn2{PH@&GJk+>FrtDB8tajkE$Nj;U@#T!>m7`TC4TZA`q3fE8m8bo z6wsQH0|>Cx_*bkTLXGrCFUW@}(G5R!`1lheOc+Lj{gy^jWZht{RHsvn+{k(qcgl>0 z&bql3JHIib`qf8R{A?p20*bG3nd~oT3GET!f^XEl*>b&QzP2r0+jg@*RkJVS+dpB(p8un==EbVDukXLMf4*vKx@zmq zhD_C-iKiDUt5^8GnK-&oxqiNKQ@V1~T;I)Bc*B`Ex@7WtD;7)Z76N5g4^JO{EjBlJ z>u9R_a3(_y8-R$Qxm90}93mu29biL8>X4mzu`T8B{`W?4ysk+D0fhQJrJ$9vQ z_Q|>DQgv;&1KU4XEUSeJ)xwG&2g>0swXouUEqT$>hY7FpiK3HKqjd9==H5krZ5?~n)08e1P@PPD>~5fgpE zE&T5RmZ!1km`jfQatG{V{6l5zm<0~L;4^9}W1&eJxb#8!mKyn(9E9Iky(F#cgzl)F zHbq@Z!OG&ON-QXWbBHNtOyDIZflsTsh^}YnQkT9(@|J)s`7H4>XZ}3eX9OL1bP3!Pb1pcvO z(Zmorc0q?#xeNyhK36t;G68peV8Q@E`cTg>%qr30W>ztsv0*JH2=XJW6*~WO;)Id< zL@$|8i3@lRbwN*i0@KH!&>_^t5OCIXq$_LcIGh#R<@HE}8}UFsfH2vRkvh-%R9xN5 zNE~?^O8;ZRhJ9!Xl9*Cb3^2wilrAFI)Yd%{wnazTYi54(B9tx<&F#3k{YQ@7SozjH zFp0GWu5?`On(mqptWO8l;~XQ-FuwWn^_Op1?^yQ#*j@$lux3NrQN8389BVk~rD1qE zAJgcHN-xza->aaT)rUuY^XS>BZhsMq#^vFBh93DY7Tms!y5R<_Gu= zVvU-j|LW2qJg?K!)tD|7f&kPxuwG5qf7Ke~D|o&^Pgi4-g`Hsd8+v~RLh3X9SLI@f zmVHa1>mY~PsODhLT?5VPz5Xj%lO!f&*I3>hDs14mU9H(M<`~$jmePOKcNuq&x%1bh z$iN;vq9hMmaY;6{7ck;kLs-nRPr7i*F`)YO69YE1tf zb4d35CG|~x%+Uw8ar94})${?dI$Q8%M8_(4`0Fn*o|^>glD zb)VVtEb$E;bZicrGjo!xcYqH9driGA0HR8Vxj1>#L~qPV8LC8k z%a(4{FoIX20Yq(|`;9XcrSmK;bm#>I8VydKLkxrrHj1w~h4vwDxT^Cs<+Hs?pL7QV zIaHQ1>p)~Rv;3NeH$A^fwKeHU@L}-Cy;Qy7@gDLCM8up{8xfTBq6yv?CQ*;TzaCA3 zCIuuxZc47YpCg@}3N)SASlOi`on7r6M1>GBsV+Xg6zLJ`it^RX&HO{OGRkwD6yYmBsWzgl6oLdJr?3JN@xCa;{fehUdZHC(RiSoDBTC995_b= zindfe39bMcBFI*3;G=bDD>yVvA5A1A!j~v5EEp!LbGu9SBP%yr+^u?+M}L_P1nh;% z#3YzTgZW;jqv%{u%1|;bCH|d}>J@h1vYo1SQ1CCYQy8qKzE8Jd3VuidqqGZ=FI%8l z%zau0n*uhZh>DZ&kgH#;!}#cMQYKE7>t0BUk#%aKWG<&?s=^U^Qa}QUNPLi4)-mmf z9dF6Lan(bWeii{#I}aq088o>-#L8Y-E~UQOPO%$&2${L>CW4Z-5%toQEKF*Ahsgea zg>oSKVbOulf7V?|+_6`YcV6arcYWHoezx!CK+3m1<78fpa z+^vhg@>IqCjPLQ3<8jTcZ$sMGFz;(l``{9hS?n%SJTA~NG>KGPgyD_9$$2NE>}-h&pR8^&IY(CgNw5H#`bh$d&YU-uCU%( zvQQ9!>c{Q7(hRK({0(1ya{9?vkHCkS$A9_Ax?=M}plas1+2e37-*jsTVwcLIDI3Ht6>!N&51ylMJlw4L{o}Vv|5?=^RAsgw z{PsEIswkSW!h8eYl7Ru&H~sL0SX@4H==G!5j@~YA(4;@D>B`n6lc{3Uve{Bqx+GXi z$g;&#II)jJKF6kyEfkc^l+G5<_RkI6LMBs*Z{i@6uFn_L!^$gDuz}vN;PSr7eJOXn ze!6^k_RP)g-`e-P`|fz!@rpk?DgquqBcR-);KKyTcE7i$v*GYY^A96YK6@}rVt z8$cQuGyayjSxe~-E(5d6ZZ%fFJuABbYOPcqOV^vW&V{FMk1+MRixX5||?iPQ06 za3m+W)D5P(pnT!n9b~ZLZkTPITm`REJ&#T*>Qt3RsBoHth%iQX<)is{+aM-*LrSwB zoqV9Zs|&UUC$Q`uVY`;M(>~~gOX(r5QXqXO43_lWlyD)pQXun%d^%xfW4skBF>w_+ zusQC5BVX>X{+F|{J)UlSJmY-gE`)6dML0Z}_t(MkAc@;=K6mT6jKA||khC2Xf5x7Z zNbaD>ZRfvR891=Z{M}v9rP^g?CT;H8hk{C~vwAh!FLh8VT)Y;n}XD9rnC zz4@{l`C96u%01FYgj94!>6^fM;u}{%@NtRoDOfo&B-5nKXDYR#_IQ6keF6>~0Toxh zQJsw7BV1&~gaj2$aZX*Wku8io^>O^D)?@4vdu63Rf4%UgmE%o94YHFkqg&k_z=1P7 zXH>T`6e2&4eDV_%oT6fcFIQ<*<(>4Lg69ZNb|DY&WT!~R@6S&@f4O(Eccy!GXU4nn z;-Lknf8JS@cEU48##u{9u3cP&b5-avZH)nr%8MwAkv ziGtWjg3g9LZP7Q1(WBkiXjw)kUc)R)o>rtDC^4=@5xO_&3a%bNavOYBT1P38S%TeGyFZ|d+5%fU~u$ByYlV? zM&ezyR|nZ_Al_jqqT8&H3ie z-JPJio5{ucf$}jyw>;6F!!&x_7F%PmIKgf>kVms2L7uFZQh&*bp>B0TJ>&Ko4|&&h zYLE3_W?5?ZJnM)hWDSP^s7`}0Sq2JBK>HXB2f4uo(PGeEf*>Fys?B{BA?D5%hM!gO z4PirrP?XCtwo-y^#8r>RT8rL6j}VAgZM{hQouJu!y>>)uz^9TpmglnkE?@8c98kw~g`- zp)%ybgwooQ3N}Vt6zhDL>*K4X>R!U~l1D~%5P*GeEEad>@9L4vh)l@1jtslQtdNc0h>~?JN_dqij2rzlGT(*I=E?wFtUG z1$>JLQ!*2MeV<}X{zWpbtQ%l~6rX+Hlwl$!Qp;MXJ6TsWp-vU)bKvaL?}rwx*bC1n$+@IdK|5oDL9hr@XGsQkIrCf!hnFqu7cKTLAN%65dCR)AW!anFE7bjap)M`d{h1K>3H?U~{=bCXA_e*V z?|S_gU64txt4j%0@7t`RW2W!*p=(3)Yj>sB?s{K90-gq&ZgwvVPSL-#R;b3|l4YcK zirNEq6Tru=FFNnp3dDkY8ysQ@&ZI$hi?49bdur33+KWzzC{5NcSAVg3UMx?GGy;Iwl1^g)45L-Ge3Re4)SrGkGmZ?w8v~pv*wsck7 z&Ax99{qE5Gj$`Q^$5K_t(j{G~PaIE`98VYE6-=TD-6&dj(YH{(CMA?C+bTr=@~#qb z!_qFHzHy8RS}<>KdH+8LZ}O*=O%+1V2Z0psxGVfNY4&a@}EJp-@h%+0 zUEMdmZ?-L6+_2G z*OZQ4D}8%r@|h(&OUlz-mhPe!-7NZ?NG*En#rC`{QhL4?DIG1k3zqCGO2=N8oGc0< zw|~jaq8_zN^c7bIF4hp8&2%sODa-p^qFuC2^<5pB9-1%Rk}loyzJP?KHA3aOg-|nh z+qx$m+H=<(#HU9fI$ttV1btevQiSFKPLZ;xgAR^5S=2=vf;Q+&tT0U5ZLasd^qj5B F{|$FJgzx|W literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/rdataclass.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/rdataclass.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..704aa0227083d05b7157aedeec3246ee42e78e76 GIT binary patch literal 3541 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/renderer.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/renderer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6d317230e94c3bd0d9789a074a75b923df84141 GIT binary patch literal 16335 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/rrset.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/rrset.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dacc7c1f73c6b44342ca9b2ab35f06ffbbeaa45f GIT binary patch literal 12487 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..80803fdf986faef954c3a19c38cb0fd0d865a9d6 GIT binary patch literal 17040 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 G0{$<1tcmjg literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/tsigkeyring.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/tsigkeyring.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c0d968677dc806397bd3ab6a47ed724e2e41945 GIT binary patch literal 2875 zcmbtW-%k`r9G|`YaR+zjQu$%j0t;&85bmr}jFpfoSZtbNQ*2@{8nWyyu;<C>X3nP?{cjv?xf=NXIQgQP}tegc%e=Q50iho&*zRpk))DBpYRuT$E#+ zaXu>0c`vk_%Ben;9ro;nwJ{!gLVULiTyEnvW!aV7Rl{6NxWPsJG4I=~rhAqw04yJ1 z1=5~S#kKvXrBp&zRLL?V(=xCsT^RVJPr9O}&F~+@ivEztW>4#Bo9opS%l297RSRqS zh%I2%8pFE6xVAz|yu`w{@iC0fAPr@i42o~oC@L=%+g^0}8-y--5XyS8tOc!8;+!j$ z+qkzZyHdupR7B5mRz+!)wP++sf2oLx!<*0pu4cK7i)GoBGF*mr*T<{e(Q=z%GF%sQ zZz~y=o`KH_Z?%h%;g^Wtpp-Db$(jz) zkXlGw#^i+5+dm-ba#C%RhGbLi?2t@Vfk=faB`7Ib!=|L^5Nuf*(kKXon#5E3dQ?+J zrBT_GT4K5xCK%y<2Pkc+X2@CGCK;GQ{Rcx@wUO|rYPx7# zm|FG`w$dr}L+Yqdh0SZ0nlx<|^lgru%B~p3Mskoi%&yc-O*buBS5%udEo^fH(q;kE z7K)zpVjkjRdy9_3akyIn5Y$jzr@;Nr* zs$!XuFN`6>(iAiDnW2vuy=U4_o3OK;)zSswet4z#fF(focn55Z6-1 zh?W>5#@qll_*dTeuE-l~7P2&6%pS6dTU?IG@)p^JmJDaPOkQS&fCptf8O~)A$UvTE z^|8(C;yxFn-0px??O^Ss?c!$zz1AYZq9wz+>=(d?SM9GPQZDF@Jw{5Ep z4$ZyFBwKDR@qRUY8!@P6kX3wx5Q>*xFdAH1Wsn>BcG#fYi&=(Z43NeA9z;4du|VjK!0CP}y!YP({nv%^_&ZJ%XO;tO{r3`I_u-Qm() zC#af-pqfDIV(psPloOj4-ph-jHSuUpJen7et%)ad;>o;tYSniN*MVlpi%C}4-Xba{ zH`V5;UDOI3gaJ(cYe1FcKBZDzPb5mCEZiQOw(fF??4+Ky~#qm!~h^8GL~%`LoQ;3g9^9Nmb2EU^;MTmqTsJy$^ueer-6s z$llc_&rN-K>-=+Z@9cs3*16Un)yHBRo%*@*er3Ml__Y6dL({^6pIU!ty=zSQpEfqn zXXmnOjUBnhj(lV1ihs4C*TE>%k{t_mC{QzT-f>OP_6`ol3}tW-lixmDfJX_QSxl}D z=E#W8y~&vTxHw%x>5kK=dpPm{JH$?oejib6>X4VduA~tgOQ_vA1XCp3W(ukT%P`EJ z2NBc!3Oan7uf+tAL=p*s;t$^gZxB3_b|1n5#$6T>E16>b|yIZF>R1#PY#qbERkb>VxoKq)RRO8|UJhTL1t6 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..1d8afcb9be5614fb5800f1900974d2905916a5c1 GIT binary patch literal 2366 zcmaJDO-~y~bk^%{8)L9ZfrwOtrwJJgn~3hb=_u!Uqajbu)?B)Dmd%un+cG=^mB29j;qGViPj9q#j{ z=EF0Cw)x6=+AiC%Lv~>2j8%4SSf^dG3p=qR?LerCbpzJDY6*MNfzgRcVQT7%5ZBZt zOmsz4Bd?jBi3C&k_V(M`Jc?HoSKeIpu;&M03t=?Pf%g`fo900yZ`dzNGAK-kYMPg% zL<$RvE~pw6mT@A{H=~gxmLn5ktKk$yRZ3z}G~A+?)Z|ox!Cq0km68%wibE7-EiQ_` zqCIANUyROaNgUNBJt?Wt&xkgU<5Z7c({5^1iR;m;nmVhEUhEsub!ApfVwFZCOITfs zCX}1e#Z@||se_UJfvB!f+_xyj7o=INM`cxyQksY?t`aL)F<&PD(AlTjK!&x4u%w%K z!%FcAHS7e_6j9@~lmHpo;bEEE+Xw6x%AomrD&v5)UD*)v(&|S;z`0%JT;tT=Xstl% z@G`g11rFFLvth0~oXoAU$0A$S_zW6FH-1KF&6=_PuhnB2D6(`ja9T2!IuEQh57G^c zCR^9|d*KX!)FSA1)$AYcwTPH)*=G9b%5Y%Eo`y(FeVjT9G)EoHSw|ZJnyZfHuA_AU z%~MD7*3sI5)?m`?%wE+}BZXqGEZLW_K1OomFMKUJGizqX?&Ylrd^Gc_d+KEThrAic z?}7#D0OFwsZMQHAU!q>3sa|3dg`3m=TryjV3Um$&tX>3G_kuPfNM^@TO^{SUQ7N9q zB;s}pu%>|Z;`qd?tn64`0HA&I24qQ2EVzG!B2Z4m4sn;xB9~<1Vvi^|^8Jw)Ff6l=^r}%vc_T=mHz+iBC z+LuxnRBc%`qZT^%xz4no5=luY>TE4V>3-&lKBuJ;vTzf_k1k`Z3TMH(EDUsa4-N=@ zLch?ZsPRNfSC()%Y&RT=E-SMNHF$8(;1gJ_bg(UfP0Q-xmGvHag&QEC@*SBk-wt+CCqhvPY}7zpP1 z2j_~xw)|I5mkJ$yg<#+2FM9mzSIpsR*v!k~$UtSo*c_+?fGtE1#%Tso zR%Pbeu0q6;L6b*$nEX+my0$$Wx*&Bh)@1ImnZuTe^zpc)vdgP-M^d43qe1_gg=4rC zT~T!^sc}qN!IZ&6$C(9TxW8nV-(^BH66Qzw$t|*oG*|bIIRU8L z%~CAYN3k?(Nz%iXKFhGR&r0LFHEHX!lh~ogPBnI^v0IIc)VNrUJ!SQWd|FH0v?{5-a#Gs?wVhD=1?_u2PVC61wUl1FlB?{i;>r_Y z?MpuHtA_kt`FAbV7l8bB?JXbo1tGs9|E{I_YR0S~*LY2L&nc#REEOG&$C%S2Y*gf! zQ==mzXri@_YZaoxa5UBW7N1T)7eedFbbnfm$As47 z>C|Al`(R6_AjAh#!(2*i4PW3=7h03?{??H(aVVYI9d2uH72+b-G7^oQiw<%^E1MEp zM+w8iBV)2FG90}WA08bZuUa##u$I3QND*)~Pg3_weXEe5dO}4|F9N*hA`!VL5*bdj zqe)DAB9Zf>(WLst8Hupz7-ZY}PV^j-9dDgHbn?*Kvg7ny-JQLMq+X7Dxz4)Me^-w&49@&t!MbTQYfA3ChYRzjNR2 zdtjmMWvf=oQAScqJJ`gUg@FCwv)T?b@ngeM;Ht?276O$I5u8GrO(Ng zDs=}uQ`7*>x-NVBd|Vmp=E|Y14N8(A3}e8;7G1XKn+{}3qS<70#fFVp`HcN|4E

f^6Aks;`Dc4sLM%)Y+(jxp$M5=D%#~ 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.vz{5x+}n%vvTt+F7o4`CTL+*{f3~q* zYHYvNeaF{%d(X13^S=4V*Kc<$`(FR5GBA5)=1jJ-S*mQ#R<=o%ZMU}GI{)eRyOpm@ z9nM2@pGIX7^}@!`tMbHuDL7LJbqp0Ef5X!kiedY}#1N%xPxsj0ydwQmYAE&<|H|t9;IU z0(Z*C)4ZW^gJt?hClyz|rVS9yH?7BC$K;C3rVN_&`t7_$@@>)g zGP;84FQ`ke-PjMSsH)&PvGKy)%Ds;l8Q8}LM8=w14P2;1THwM_e}>abes&U4U9apH zqRCOt=tLw}Mg}=CLR-TwkJv3h=5ht_l-3cPt5Ohq!@f90N9Rg|zk@CfGdGU^~G;`ig$+vUqjb&d)t|Iv1sf9E1XR`HSsXqL9MHuSN?)~}R zuR!b!&p_1{sjB6UujRgyeXk%pS6cPq?uGsH`?IwzQfF&bG9Zd)r6;=_x*K%)Jm?EWSMpe{`~Eq`%m8*{O9^?=V=N4 zUOWwMuVq!>eN`5it6B^!?YUFh2IdPQCcrNge4~3p+j!hdd>ICa2FN_h-iYc05#ds! z!-g5P9EBgGpr^K6#&QGE(WDsBy{pw5{2nmWDBcBa*dB@>{v8ZZzmr`fd>Uk&IL4!F z;!k3po%nJntHJ@MDR33%2|z!BMTj}LA5(b4Nldmp+Hd$22IQC`C)5bO3K)z*07Fo` zp<`S!8Hs@N!(PZG-lVT9_ocYPc?@>phl?N}|8$wU?{AzuzT%>qcja1kee#1FKTxi` za}AqwJDPLNt)CpZaU|P(SZY2Dx8x5>L-ua^eo5KYp({gIhp!CJ#iWwP#cru&$BG?4 z0qeoxxnZUuTh=6%HLW;FrVBEs#hIEFH+e0>GJ%=EZ2e6AqF*Z8wo**;JydlKItbtz zT8x3$^@^9|mr!2c^wuli`6KCl<-0WHC|R*s?94Pfw|iml{N9BF^9Q7=op6V5XIA0& zipp0Ql%wd!@4ojg{(7+<^UGE+1vIK90K@B^2U5dslltXZdqA=W^cI4JE!gHxp(S=9 zG9QrwEha5^ethP=GkX0BQh(Lqw%2}L^@6>VUU`f5+IP=c7hLl$Gy*SKrNGV=3i57s zk?3~!D!#2WykZB-@9u?{=U-lUZT_{T-BN9Mg~H5Rf)d{rSIPUzCE90ipX*vUGJj;@ z`26vuE~&0{g@U}>RuX+?TgA7Pt-I|d>xw}h7J~?jL9{YLQ!l^zz{%K43MYQW4w<;y z{ECbEcFDJW2~b4KEt}-)SiveV2}LEVZb*IIO*{SvKRDIjYh~S60UR8ecyPy9Bd_^E3{2YQ6D^1f6wo-KCe^Pb7r%J!1{9jV#Ut*%{OUm~p zRrw`VK{6p-`p{8CmrZxg9+^3k^>3B@TOU%8@v!EIoof`AIj zbaWv?ktlITnKET0H{c3gh2)CPkvmtJvw?yrS!v(A+4pvKXU=8WhhQzAv&N-}(50J< zmoy^R4v?=1BMo6jV=$|+nA14SYdl1O@R&bYLCBsVOcP;&u(!xH(RV=;%}vm`TV0vf zu}B1PN#J@2F6kdG&%_m&t^l{ZO@6Es2Ky=;LWIRLZmd8Dgv0EF^VmP(f~#s$Szc8t zA#Booxo~WmOE$?8osy*@v8e&MVScpioL&GP9k~;AsG6`+R>4S9b=NX8)|>6M2&G2G z%n`GohKs~3s#znY-nF_BezLAoqd?a3diq4q5URo$)X_uXeEG)nB4^>xS@P0egf|)3 zv=4B_F%0wj5n|S^MCA2Vg@MOAhQM6aKY%lFzy<10FSz-^?ESh_KWzjXQscB0Y)M^Z zr55W8v-P73VX;50wrG2+z0y@S{^zmVq4q*oS+B+Zs^0c@-gO^8`yqBpUFB6Z20v8h s>Zvau8k?={Z_j&js2%JqpFQZw8=dS=y|)$X$+2Ido_tXA+4IocA32ujfdBvi literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..c58658f259906c8ab7b7ec22386e1857f9ae32dc GIT binary patch literal 14828 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/wire.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/wire.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbe4ebac83cb89a29fc8d44a731049c4d6c9aedd GIT binary patch literal 5798 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

W6s7eO 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/zonefile.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/zonefile.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f726bfbcba62601f35b879fa87e26b214baab036 GIT binary patch literal 34543 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/__pycache__/zonetypes.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/__pycache__/zonetypes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0047adbc0f1f5b53dc1b8c9092e1b02ee087ce23 GIT binary patch literal 1367 zcmbVLO;6N77@q0piYyB(2tmXpa9~N;2GE3qF+o8gk(J1;m!`B6w(GXVP9g4|FyRO2 z2|cMNL%8`jIB^1dBH_dhvYfc+`?h?nvf2suLM za>y4({RE6HViTR%)D|jKr|~T4qCj2}TihhJ#Kdyyl6XCr#_g&Zbay8#$V*#s}A+vK~b(QhL zG17`wBN^0-M|~KkEy4(>M0CNXx@Zf!#AMWI=VFnrRHeKewaz#zjE75BiMgzn)?XKA z7iMyadrV=ih0r)qySfChMXGdLC?5X?dz)<2NVF=P6yHnwV7Z%sI* zW_w&iDG~)%t5M2u&G(MG=GM9!?@_o6V;$<)L>uHV(^&_D6!R+349|3#VMHy4;rjMk z1?^VDc(-O&E;6cN*uDi}Wodq4@zrc}*Q%JDy9~}|2Wi+TxPy>EXhlFxV!$Nt2iPRP zq@Kfc+s2DSx&2h2a<(oKr6aZyB66D7a0D_pYiNtQ%oJN{sH0ky^GcLW?muVp(sX6T z51g~wH`Q?@J50Kl2fC3fssQ_O} z(1Cze390hSA(qrJ{$U@GBZZetIAd`C(t(3U8N-g>je%RTfdA7A=cy-2 zTRjbifrf8_Gk_D3QhKToI#_QZG;>5UXNpR*A1Ak_K27ashxW2VX9NOgT`8LVJoI(s s%gBD;=w9FG8G*n_FOhrqrGY(Z;G6oR?R(pS)YIsm$YNIbkck!l1IMZ)00000 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/_asyncbackend.py b/.venv/lib/python3.12/site-packages/dns/_asyncbackend.py new file mode 100644 index 0000000..23455db --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_asyncbackend.py @@ -0,0 +1,100 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# This is a nullcontext for both sync and async. 3.7 has a nullcontext, +# but it is only for sync use. + + +class NullContext: + def __init__(self, enter_result=None): + self.enter_result = enter_result + + def __enter__(self): + return self.enter_result + + def __exit__(self, exc_type, exc_value, traceback): + pass + + async def __aenter__(self): + return self.enter_result + + async def __aexit__(self, exc_type, exc_value, traceback): + pass + + +# These are declared here so backends can import them without creating +# circular dependencies with dns.asyncbackend. + + +class Socket: # pragma: no cover + def __init__(self, family: int, type: int): + self.family = family + self.type = type + + async def close(self): + pass + + async def getpeername(self): + raise NotImplementedError + + async def getsockname(self): + raise NotImplementedError + + async def getpeercert(self, timeout): + raise NotImplementedError + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + await self.close() + + +class DatagramSocket(Socket): # pragma: no cover + async def sendto(self, what, destination, timeout): + raise NotImplementedError + + async def recvfrom(self, size, timeout): + raise NotImplementedError + + +class StreamSocket(Socket): # pragma: no cover + async def sendall(self, what, timeout): + raise NotImplementedError + + async def recv(self, size, timeout): + raise NotImplementedError + + +class NullTransport: + async def connect_tcp(self, host, port, timeout, local_address): + raise NotImplementedError + + +class Backend: # pragma: no cover + def name(self) -> str: + return "unknown" + + async def make_socket( + self, + af, + socktype, + proto=0, + source=None, + destination=None, + timeout=None, + ssl_context=None, + server_hostname=None, + ): + raise NotImplementedError + + def datagram_connection_required(self): + return False + + async def sleep(self, interval): + raise NotImplementedError + + def get_transport_class(self): + raise NotImplementedError + + async def wait_for(self, awaitable, timeout): + raise NotImplementedError diff --git a/.venv/lib/python3.12/site-packages/dns/_asyncio_backend.py b/.venv/lib/python3.12/site-packages/dns/_asyncio_backend.py new file mode 100644 index 0000000..303908c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_asyncio_backend.py @@ -0,0 +1,276 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""asyncio library query support""" + +import asyncio +import socket +import sys + +import dns._asyncbackend +import dns._features +import dns.exception +import dns.inet + +_is_win32 = sys.platform == "win32" + + +def _get_running_loop(): + try: + return asyncio.get_running_loop() + except AttributeError: # pragma: no cover + return asyncio.get_event_loop() + + +class _DatagramProtocol: + def __init__(self): + self.transport = None + self.recvfrom = None + + def connection_made(self, transport): + self.transport = transport + + def datagram_received(self, data, addr): + if self.recvfrom and not self.recvfrom.done(): + self.recvfrom.set_result((data, addr)) + + def error_received(self, exc): # pragma: no cover + if self.recvfrom and not self.recvfrom.done(): + self.recvfrom.set_exception(exc) + + def connection_lost(self, exc): + if self.recvfrom and not self.recvfrom.done(): + if exc is None: + # EOF we triggered. Is there a better way to do this? + try: + raise EOFError("EOF") + except EOFError as e: + self.recvfrom.set_exception(e) + else: + self.recvfrom.set_exception(exc) + + def close(self): + if self.transport is not None: + self.transport.close() + + +async def _maybe_wait_for(awaitable, timeout): + if timeout is not None: + try: + return await asyncio.wait_for(awaitable, timeout) + except asyncio.TimeoutError: + raise dns.exception.Timeout(timeout=timeout) + else: + return await awaitable + + +class DatagramSocket(dns._asyncbackend.DatagramSocket): + def __init__(self, family, transport, protocol): + super().__init__(family, socket.SOCK_DGRAM) + self.transport = transport + self.protocol = protocol + + async def sendto(self, what, destination, timeout): # pragma: no cover + # no timeout for asyncio sendto + self.transport.sendto(what, destination) + return len(what) + + async def recvfrom(self, size, timeout): + # ignore size as there's no way I know to tell protocol about it + done = _get_running_loop().create_future() + try: + assert self.protocol.recvfrom is None + self.protocol.recvfrom = done + await _maybe_wait_for(done, timeout) + return done.result() + finally: + self.protocol.recvfrom = None + + async def close(self): + self.protocol.close() + + async def getpeername(self): + return self.transport.get_extra_info("peername") + + async def getsockname(self): + return self.transport.get_extra_info("sockname") + + async def getpeercert(self, timeout): + raise NotImplementedError + + +class StreamSocket(dns._asyncbackend.StreamSocket): + def __init__(self, af, reader, writer): + super().__init__(af, socket.SOCK_STREAM) + self.reader = reader + self.writer = writer + + async def sendall(self, what, timeout): + self.writer.write(what) + return await _maybe_wait_for(self.writer.drain(), timeout) + + async def recv(self, size, timeout): + return await _maybe_wait_for(self.reader.read(size), timeout) + + async def close(self): + self.writer.close() + + async def getpeername(self): + return self.writer.get_extra_info("peername") + + async def getsockname(self): + return self.writer.get_extra_info("sockname") + + async def getpeercert(self, timeout): + return self.writer.get_extra_info("peercert") + + +if dns._features.have("doh"): + import anyio + import httpcore + import httpcore._backends.anyio + import httpx + + _CoreAsyncNetworkBackend = httpcore.AsyncNetworkBackend + _CoreAnyIOStream = httpcore._backends.anyio.AnyIOStream # pyright: ignore + + 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 + if local_port != 0: + raise NotImplementedError( + "the asyncio transport for HTTPX cannot set the local port" + ) + + async def connect_tcp( + self, host, port, timeout=None, local_address=None, 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: + attempt_expiration = _expiration_for_this_attempt(2.0, expiration) + timeout = _remaining(attempt_expiration) + with anyio.fail_after(timeout): + stream = await anyio.connect_tcp( + remote_host=address, + remote_port=port, + local_host=local_address, + ) + return _CoreAnyIOStream(stream) + except Exception: + pass + raise httpcore.ConnectError + + async def connect_unix_socket( + self, path, timeout=None, socket_options=None + ): # pylint: disable=signature-differs + raise NotImplementedError + + async def sleep(self, seconds): # pylint: disable=signature-differs + await anyio.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 and bootstrap_address 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 "asyncio" + + async def make_socket( + self, + af, + socktype, + proto=0, + source=None, + destination=None, + timeout=None, + ssl_context=None, + server_hostname=None, + ): + loop = _get_running_loop() + if socktype == socket.SOCK_DGRAM: + if _is_win32 and source is None: + # Win32 wants explicit binding before recvfrom(). This is the + # proper fix for [#637]. + source = (dns.inet.any_for_af(af), 0) + transport, protocol = await loop.create_datagram_endpoint( + _DatagramProtocol, # pyright: ignore + source, + family=af, + proto=proto, + remote_addr=destination, + ) + return DatagramSocket(af, transport, protocol) + elif socktype == socket.SOCK_STREAM: + if destination is None: + # This shouldn't happen, but we check to make code analysis software + # happier. + raise ValueError("destination required for stream sockets") + (r, w) = await _maybe_wait_for( + asyncio.open_connection( + destination[0], + destination[1], + ssl=ssl_context, + family=af, + proto=proto, + local_addr=source, + server_hostname=server_hostname, + ), + timeout, + ) + return StreamSocket(af, r, w) + raise NotImplementedError( + "unsupported socket " + f"type {socktype}" + ) # pragma: no cover + + async def sleep(self, interval): + await asyncio.sleep(interval) + + def datagram_connection_required(self): + return False + + def get_transport_class(self): + return _HTTPTransport + + async def wait_for(self, awaitable, timeout): + return await _maybe_wait_for(awaitable, timeout) diff --git a/.venv/lib/python3.12/site-packages/dns/_ddr.py b/.venv/lib/python3.12/site-packages/dns/_ddr.py new file mode 100644 index 0000000..bf5c11e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_ddr.py @@ -0,0 +1,154 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license +# +# Support for Discovery of Designated Resolvers + +import socket +import time +from urllib.parse import urlparse + +import dns.asyncbackend +import dns.inet +import dns.name +import dns.nameserver +import dns.query +import dns.rdtypes.svcbbase + +# The special name of the local resolver when using DDR +_local_resolver_name = dns.name.from_text("_dns.resolver.arpa") + + +# +# Processing is split up into I/O independent and I/O dependent parts to +# make supporting sync and async versions easy. +# + + +class _SVCBInfo: + def __init__(self, bootstrap_address, port, hostname, nameservers): + self.bootstrap_address = bootstrap_address + self.port = port + self.hostname = hostname + self.nameservers = nameservers + + def ddr_check_certificate(self, cert): + """Verify that the _SVCBInfo's address is in the cert's subjectAltName (SAN)""" + for name, value in cert["subjectAltName"]: + if name == "IP Address" and value == self.bootstrap_address: + return True + return False + + def make_tls_context(self): + ssl = dns.query.ssl + ctx = ssl.create_default_context() + ctx.minimum_version = ssl.TLSVersion.TLSv1_2 + return ctx + + def ddr_tls_check_sync(self, lifetime): + ctx = self.make_tls_context() + expiration = time.time() + lifetime + with socket.create_connection( + (self.bootstrap_address, self.port), lifetime + ) as s: + with ctx.wrap_socket(s, server_hostname=self.hostname) as ts: + ts.settimeout(dns.query._remaining(expiration)) + ts.do_handshake() + cert = ts.getpeercert() + return self.ddr_check_certificate(cert) + + async def ddr_tls_check_async(self, lifetime, backend=None): + if backend is None: + backend = dns.asyncbackend.get_default_backend() + ctx = self.make_tls_context() + expiration = time.time() + lifetime + async with await backend.make_socket( + dns.inet.af_for_address(self.bootstrap_address), + socket.SOCK_STREAM, + 0, + None, + (self.bootstrap_address, self.port), + lifetime, + ctx, + self.hostname, + ) as ts: + cert = await ts.getpeercert(dns.query._remaining(expiration)) + return self.ddr_check_certificate(cert) + + +def _extract_nameservers_from_svcb(answer): + bootstrap_address = answer.nameserver + if not dns.inet.is_address(bootstrap_address): + return [] + infos = [] + for rr in answer.rrset.processing_order(): + nameservers = [] + param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.ALPN) + if param is None: + continue + alpns = set(param.ids) + host = rr.target.to_text(omit_final_dot=True) + port = None + param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.PORT) + if param is not None: + port = param.port + # For now we ignore address hints and address resolution and always use the + # bootstrap address + if b"h2" in alpns: + param = rr.params.get(dns.rdtypes.svcbbase.ParamKey.DOHPATH) + if param is None or not param.value.endswith(b"{?dns}"): + continue + path = param.value[:-6].decode() + if not path.startswith("/"): + path = "/" + path + if port is None: + port = 443 + url = f"https://{host}:{port}{path}" + # check the URL + try: + urlparse(url) + nameservers.append(dns.nameserver.DoHNameserver(url, bootstrap_address)) + except Exception: + # continue processing other ALPN types + pass + if b"dot" in alpns: + if port is None: + port = 853 + nameservers.append( + dns.nameserver.DoTNameserver(bootstrap_address, port, host) + ) + if b"doq" in alpns: + if port is None: + port = 853 + nameservers.append( + dns.nameserver.DoQNameserver(bootstrap_address, port, True, host) + ) + if len(nameservers) > 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 new file mode 100644 index 0000000..65a9a2a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_features.py @@ -0,0 +1,95 @@ +# 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) + # This shouldn't happen, but it apparently can. + if version is None: + return False + 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>=45"], + "doh": ["httpcore>=1.0.0", "httpx>=0.28.0", "h2>=4.2.0"], + "doq": ["aioquic>=1.2.0"], + "idna": ["idna>=3.10"], + "trio": ["trio>=0.30"], + "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 new file mode 100644 index 0000000..b3d72de --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_immutable_ctx.py @@ -0,0 +1,76 @@ +# 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) # pyright: ignore + 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/_no_ssl.py b/.venv/lib/python3.12/site-packages/dns/_no_ssl.py new file mode 100644 index 0000000..edb452d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_no_ssl.py @@ -0,0 +1,61 @@ +import enum +from typing import Any + +CERT_NONE = 0 + + +class TLSVersion(enum.IntEnum): + TLSv1_2 = 12 + + +class WantReadException(Exception): + pass + + +class WantWriteException(Exception): + pass + + +class SSLWantReadError(Exception): + pass + + +class SSLWantWriteError(Exception): + pass + + +class SSLContext: + def __init__(self) -> None: + self.minimum_version: Any = TLSVersion.TLSv1_2 + self.check_hostname: bool = False + self.verify_mode: int = CERT_NONE + + def wrap_socket(self, *args, **kwargs) -> "SSLSocket": # type: ignore + raise Exception("no ssl support") # pylint: disable=broad-exception-raised + + def set_alpn_protocols(self, *args, **kwargs): # type: ignore + raise Exception("no ssl support") # pylint: disable=broad-exception-raised + + +class SSLSocket: + def pending(self) -> bool: + raise Exception("no ssl support") # pylint: disable=broad-exception-raised + + def do_handshake(self) -> None: + raise Exception("no ssl support") # pylint: disable=broad-exception-raised + + def settimeout(self, value: Any) -> None: + pass + + def getpeercert(self) -> Any: + raise Exception("no ssl support") # pylint: disable=broad-exception-raised + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + return False + + +def create_default_context(*args, **kwargs) -> SSLContext: # type: ignore + raise Exception("no ssl support") # pylint: disable=broad-exception-raised diff --git a/.venv/lib/python3.12/site-packages/dns/_tls_util.py b/.venv/lib/python3.12/site-packages/dns/_tls_util.py new file mode 100644 index 0000000..10ddf72 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_tls_util.py @@ -0,0 +1,19 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import os +from typing import Tuple + + +def convert_verify_to_cafile_and_capath( + verify: bool | str, +) -> Tuple[str | None, str | None]: + cafile: str | None = None + capath: str | None = 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") + return cafile, capath diff --git a/.venv/lib/python3.12/site-packages/dns/_trio_backend.py b/.venv/lib/python3.12/site-packages/dns/_trio_backend.py new file mode 100644 index 0000000..bde7e8b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/_trio_backend.py @@ -0,0 +1,255 @@ +# 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, sock): + super().__init__(sock.family, socket.SOCK_DGRAM) + self.socket = sock + + async def sendto(self, what, destination, timeout): + with _maybe_timeout(timeout): + if destination is None: + return await self.socket.send(what) + else: + 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): + super().__init__(family, socket.SOCK_STREAM) + 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=None, local_address=None, 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 + ) + assert isinstance(sock, StreamSocket) + return _CoreTrioStream(sock.stream) + except Exception: + continue + raise httpcore.ConnectError + + async def connect_unix_socket( + self, path, timeout=None, 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 and bootstrap_address 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 or destination is not None: + connected = False + with _maybe_timeout(timeout): + assert destination is not None + 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 new file mode 100644 index 0000000..0ec58b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/asyncbackend.py @@ -0,0 +1,101 @@ +# 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 new file mode 100644 index 0000000..bb77045 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/asyncquery.py @@ -0,0 +1,953 @@ +# 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 random +import socket +import struct +import time +import urllib.parse +from typing import Any, Dict, Optional, Tuple, cast + +import dns.asyncbackend +import dns.exception +import dns.inet +import dns.message +import dns.name +import dns.quic +import dns.rdatatype +import dns.transaction +import dns.tsig +import dns.xfr +from dns._asyncbackend import NullContext +from dns.query import ( + BadResponse, + HTTPVersion, + NoDOH, + NoDOQ, + UDPMode, + _check_status, + _compute_times, + _matches_destination, + _remaining, + have_doh, + make_ssl_context, +) + +try: + import ssl +except ImportError: + import dns._no_ssl as ssl # type: ignore + +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: dns.message.Message | bytes, + destination: Any, + expiration: float | None = 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: Any | None = None, + expiration: float | None = None, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + keyring: Dict[dns.name.Name, dns.tsig.Key] | None = None, + request_mac: bytes | None = b"", + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + ignore_errors: bool = False, + query: dns.message.Message | None = 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: float | None = None, + port: int = 53, + source: str | None = 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: dns.asyncbackend.DatagramSocket | None = None, + backend: dns.asyncbackend.Backend | None = 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) # pyright: ignore + (r, received_time, _) = await receive_udp( + s, # pyright: ignore + 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: float | None = None, + port: int = 53, + source: str | None = None, + source_port: int = 0, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + udp_sock: dns.asyncbackend.DatagramSocket | None = None, + tcp_sock: dns.asyncbackend.StreamSocket | None = None, + backend: dns.asyncbackend.Backend | None = 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: dns.message.Message | bytes, + expiration: float | None = 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("EOF") + count = count - len(n) + s = s + n + return s + + +async def receive_tcp( + sock: dns.asyncbackend.StreamSocket, + expiration: float | None = None, + one_rr_per_rrset: bool = False, + keyring: Dict[dns.name.Name, dns.tsig.Key] | None = None, + request_mac: bytes | None = 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: float | None = None, + port: int = 53, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: dns.asyncbackend.StreamSocket | None = None, + backend: dns.asyncbackend.Backend | None = 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) # pyright: ignore + (r, received_time) = await receive_tcp( + s, # pyright: ignore + 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: float | None = None, + port: int = 853, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: dns.asyncbackend.StreamSocket | None = None, + backend: dns.asyncbackend.Backend | None = None, + ssl_context: ssl.SSLContext | None = None, + server_hostname: str | None = None, + verify: 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_ssl_context(verify, server_hostname is not None, ["dot"]) + 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 + + +def _maybe_get_resolver( + resolver: Optional["dns.asyncresolver.Resolver"], # pyright: ignore +) -> "dns.asyncresolver.Resolver": # pyright: ignore + # We need a separate method for this to avoid overriding the global + # variable "dns" with the as-yet undefined local variable "dns" + # in https(). + if resolver is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.asyncresolver + + resolver = dns.asyncresolver.Resolver() + return resolver + + +async def https( + q: dns.message.Message, + where: str, + timeout: float | None = None, + port: int = 443, + source: str | None = None, + source_port: int = 0, # pylint: disable=W0613 + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + client: Optional["httpx.AsyncClient|dns.quic.AsyncQuicConnection"] = None, + path: str = "/dns-query", + post: bool = True, + verify: bool | str | ssl.SSLContext = True, + bootstrap_address: str | None = None, + resolver: Optional["dns.asyncresolver.Resolver"] = None, # pyright: ignore + family: int = socket.AF_UNSPEC, + http_version: HTTPVersion = HTTPVersion.DEFAULT, +) -> 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. + """ + + try: + af = dns.inet.af_for_address(where) + except ValueError: + af = None + # we bind url and then override as pyright can't figure out all paths bind. + url = where + if af is not None and dns.inet.is_address(where): + if af == socket.AF_INET: + url = f"https://{where}:{port}{path}" + elif af == socket.AF_INET6: + url = f"https://[{where}]:{port}{path}" + + extensions = {} + if bootstrap_address is None: + # pylint: disable=possibly-used-before-assignment + parsed = urllib.parse.urlparse(url) + if parsed.hostname is None: + raise ValueError("no hostname in URL") + if dns.inet.is_address(parsed.hostname): + bootstrap_address = parsed.hostname + extensions["sni_hostname"] = parsed.hostname + if parsed.port is not None: + port = parsed.port + + if http_version == HTTPVersion.H3 or ( + http_version == HTTPVersion.DEFAULT and not have_doh + ): + if bootstrap_address is None: + resolver = _maybe_get_resolver(resolver) + assert parsed.hostname is not None # pyright: ignore + answers = await resolver.resolve_name( # pyright: ignore + parsed.hostname, family # pyright: ignore + ) + bootstrap_address = random.choice(list(answers.addresses())) + if client and not isinstance( + client, dns.quic.AsyncQuicConnection + ): # pyright: ignore + raise ValueError("client parameter must be a dns.quic.AsyncQuicConnection.") + assert client is None or isinstance(client, dns.quic.AsyncQuicConnection) + return await _http3( + q, + bootstrap_address, + url, + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + verify=verify, + post=post, + connection=client, + ) + + if not have_doh: + raise NoDOH # pragma: no cover + # pylint: disable=possibly-used-before-assignment + if client and not isinstance(client, httpx.AsyncClient): # pyright: ignore + raise ValueError("client parameter must be an httpx.AsyncClient") + # pylint: enable=possibly-used-before-assignment + + wire = q.to_wire() + headers = {"accept": "application/dns-message"} + + h1 = http_version in (HTTPVersion.H1, HTTPVersion.DEFAULT) + h2 = http_version in (HTTPVersion.H2, HTTPVersion.DEFAULT) + + backend = dns.asyncbackend.get_default_backend() + + if source is None: + local_address = None + local_port = 0 + else: + local_address = source + local_port = source_port + + if client: + cm: contextlib.AbstractAsyncContextManager = NullContext(client) + else: + transport = backend.get_transport_class()( + local_address=local_address, + http1=h1, + http2=h2, + verify=verify, + local_port=local_port, + bootstrap_address=bootstrap_address, + resolver=resolver, + family=family, + ) + + cm = httpx.AsyncClient( # pyright: ignore + http1=h1, http2=h2, verify=verify, transport=transport # type: ignore + ) + + 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( # pyright: ignore + url, + headers=headers, + content=wire, + extensions=extensions, + ), + 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( # pyright: ignore + url, + headers=headers, + params={"dns": twire}, + extensions=extensions, + ), + 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( + f"{where} responded with status code {response.status_code}" + f"\nResponse body: {response.content!r}" + ) + 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 _http3( + q: dns.message.Message, + where: str, + url: str, + timeout: float | None = None, + port: int = 443, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + verify: bool | str | ssl.SSLContext = True, + backend: dns.asyncbackend.Backend | None = None, + post: bool = True, + connection: dns.quic.AsyncQuicConnection | None = None, +) -> dns.message.Message: + if not dns.quic.have_quic: + raise NoDOH("DNS-over-HTTP3 is not available.") # pragma: no cover + + url_parts = urllib.parse.urlparse(url) + hostname = url_parts.hostname + assert hostname is not None + if url_parts.port is not None: + port = url_parts.port + + q.id = 0 + wire = q.to_wire() + the_connection: dns.quic.AsyncQuicConnection + if connection: + cfactory = dns.quic.null_factory + mfactory = dns.quic.null_factory + else: + (cfactory, mfactory) = dns.quic.factories_for_backend(backend) + + async with cfactory() as context: + async with mfactory( + context, verify_mode=verify, server_name=hostname, h3=True + ) as the_manager: + if connection: + the_connection = connection + else: + the_connection = the_manager.connect( # pyright: ignore + where, port, source, source_port + ) + (start, expiration) = _compute_times(timeout) + stream = await the_connection.make_stream(timeout) # pyright: ignore + async with stream: + # note that send_h3() does not need await + stream.send_h3(url, wire, post) + wire = await stream.receive(_remaining(expiration)) + _check_status(stream.headers(), where, wire) + 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 + + +async def quic( + q: dns.message.Message, + where: str, + timeout: float | None = None, + port: int = 853, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + connection: dns.quic.AsyncQuicConnection | None = None, + verify: bool | str = True, + backend: dns.asyncbackend.Backend | None = None, + hostname: str | None = None, + server_hostname: str | None = 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 + + if server_hostname is not None and hostname is None: + hostname = server_hostname + + 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( # pyright: ignore + where, port, source, source_port + ) + (start, expiration) = _compute_times(timeout) + stream = await the_connection.make_stream(timeout) # pyright: ignore + 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 + + +async def _inbound_xfr( + txn_manager: dns.transaction.TransactionManager, + s: dns.asyncbackend.Socket, + query: dns.message.Message, + serial: int | None, + timeout: float | None, + expiration: float, +) -> Any: + """Given a socket, does the zone transfer.""" + rdtype = query.question[0].rdtype + is_ixfr = rdtype == dns.rdatatype.IXFR + origin = txn_manager.from_wire_origin() + wire = query.to_wire() + is_udp = s.type == socket.SOCK_DGRAM + if is_udp: + udp_sock = cast(dns.asyncbackend.DatagramSocket, s) + await udp_sock.sendto(wire, None, _timeout(expiration)) + else: + tcp_sock = cast(dns.asyncbackend.StreamSocket, s) + tcpmsg = struct.pack("!H", len(wire)) + wire + await tcp_sock.sendall(tcpmsg, expiration) + with dns.xfr.Inbound(txn_manager, rdtype, serial, is_udp) as inbound: + done = False + tsig_ctx = None + r: dns.message.Message | None = 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: + timeout = _timeout(mexpiration) + (rwire, _) = await udp_sock.recvfrom(65535, timeout) # pyright: ignore + else: + ldata = await _read_exactly(tcp_sock, 2, mexpiration) # pyright: ignore + (l,) = struct.unpack("!H", ldata) + rwire = await _read_exactly(tcp_sock, l, mexpiration) # pyright: ignore + 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, + ) + done = inbound.process_message(r) + yield r + tsig_ctx = r.tsig_ctx + if query.keyring and r is not None and not r.had_tsig: + raise dns.exception.FormError("missing TSIG") + + +async def inbound_xfr( + where: str, + txn_manager: dns.transaction.TransactionManager, + query: dns.message.Message | None = None, + port: int = 53, + timeout: float | None = None, + lifetime: float | None = None, + source: str | None = None, + source_port: int = 0, + udp_mode: UDPMode = UDPMode.NEVER, + backend: dns.asyncbackend.Backend | None = 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) + 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() + (_, expiration) = _compute_times(lifetime) + if query.question[0].rdtype == dns.rdatatype.IXFR and udp_mode != UDPMode.NEVER: + s = await backend.make_socket( + af, socket.SOCK_DGRAM, 0, stuple, dtuple, _timeout(expiration) + ) + async with s: + try: + async for _ in _inbound_xfr( # pyright: ignore + txn_manager, + s, + query, + serial, + timeout, + expiration, # pyright: ignore + ): + pass + return + except dns.xfr.UseTCP: + if udp_mode == UDPMode.ONLY: + raise + + s = await backend.make_socket( + af, socket.SOCK_STREAM, 0, stuple, dtuple, _timeout(expiration) + ) + async with s: + async for _ in _inbound_xfr( # pyright: ignore + txn_manager, s, query, serial, timeout, expiration # pyright: ignore + ): + pass diff --git a/.venv/lib/python3.12/site-packages/dns/asyncresolver.py b/.venv/lib/python3.12/site-packages/dns/asyncresolver.py new file mode 100644 index 0000000..6f8c69f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/asyncresolver.py @@ -0,0 +1,478 @@ +# 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 + +import dns._ddr +import dns.asyncbackend +import dns.asyncquery +import dns.exception +import dns.inet +import dns.name +import dns.nameserver +import dns.query +import dns.rdataclass +import dns.rdatatype +import dns.resolver # lgtm[py/import-and-import-from] +import dns.reversename + +# 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = None, + search: bool | None = None, + backend: dns.asyncbackend.Backend | None = 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: 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: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = None, + search: bool | None = None, + backend: dns.asyncbackend.Backend | None = 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: 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: 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: dns.name.Name | str, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + tcp: bool = False, + resolver: Resolver | None = None, + backend: dns.asyncbackend.Backend | None = 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: dns.name.Name | str, + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Resolver | None = 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[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 = Resolver(configure=False) + res.nameservers = nameservers + return res + + +async def resolve_at( + where: dns.name.Name | str, + qname: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = None, + search: bool | None = None, + backend: dns.asyncbackend.Backend | None = None, + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Resolver | None = 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/btree.py b/.venv/lib/python3.12/site-packages/dns/btree.py new file mode 100644 index 0000000..12da9f5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/btree.py @@ -0,0 +1,850 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +""" +A BTree in the style of Cormen, Leiserson, and Rivest's "Algorithms" book, with +copy-on-write node updates, cursors, and optional space optimization for mostly-in-order +insertion. +""" + +from collections.abc import MutableMapping, MutableSet +from typing import Any, Callable, Generic, Optional, Tuple, TypeVar, cast + +DEFAULT_T = 127 + +KT = TypeVar("KT") # the type of a key in Element + + +class Element(Generic[KT]): + """All items stored in the BTree are Elements.""" + + def key(self) -> KT: + """The key for this element; the returned type must implement comparison.""" + raise NotImplementedError # pragma: no cover + + +ET = TypeVar("ET", bound=Element) # the type of a value in a _KV + + +def _MIN(t: int) -> int: + """The minimum number of keys in a non-root node for a BTree with the specified + ``t`` + """ + return t - 1 + + +def _MAX(t: int) -> int: + """The maximum number of keys in node for a BTree with the specified ``t``""" + return 2 * t - 1 + + +class _Creator: + """A _Creator class instance is used as a unique id for the BTree which created + a node. + + We use a dedicated creator rather than just a BTree reference to avoid circularity + that would complicate GC. + """ + + def __str__(self): # pragma: no cover + return f"{id(self):x}" + + +class _Node(Generic[KT, ET]): + """A Node in the BTree. + + A Node (leaf or internal) of the BTree. + """ + + __slots__ = ["t", "creator", "is_leaf", "elts", "children"] + + def __init__(self, t: int, creator: _Creator, is_leaf: bool): + assert t >= 3 + self.t = t + self.creator = creator + self.is_leaf = is_leaf + self.elts: list[ET] = [] + self.children: list[_Node[KT, ET]] = [] + + def is_maximal(self) -> bool: + """Does this node have the maximal number of keys?""" + assert len(self.elts) <= _MAX(self.t) + return len(self.elts) == _MAX(self.t) + + def is_minimal(self) -> bool: + """Does this node have the minimal number of keys?""" + assert len(self.elts) >= _MIN(self.t) + return len(self.elts) == _MIN(self.t) + + def search_in_node(self, key: KT) -> tuple[int, bool]: + """Get the index of the ``Element`` matching ``key`` or the index of its + least successor. + + Returns a tuple of the index and an ``equal`` boolean that is ``True`` iff. + the key was found. + """ + l = len(self.elts) + if l > 0 and key > self.elts[l - 1].key(): + # This is optimizing near in-order insertion. + return l, False + l = 0 + i = len(self.elts) + r = i - 1 + equal = False + while l <= r: + m = (l + r) // 2 + k = self.elts[m].key() + if key == k: + i = m + equal = True + break + elif key < k: + i = m + r = m - 1 + else: + l = m + 1 + return i, equal + + def maybe_cow_child(self, index: int) -> "_Node[KT, ET]": + assert not self.is_leaf + child = self.children[index] + cloned = child.maybe_cow(self.creator) + if cloned: + self.children[index] = cloned + return cloned + else: + return child + + def _get_node(self, key: KT) -> Tuple[Optional["_Node[KT, ET]"], int]: + """Get the node associated with key and its index, doing + copy-on-write if we have to descend. + + Returns a tuple of the node and the index, or the tuple ``(None, 0)`` + if the key was not found. + """ + i, equal = self.search_in_node(key) + if equal: + return (self, i) + elif self.is_leaf: + return (None, 0) + else: + child = self.maybe_cow_child(i) + return child._get_node(key) + + def get(self, key: KT) -> ET | None: + """Get the element associated with *key* or return ``None``""" + i, equal = self.search_in_node(key) + if equal: + return self.elts[i] + elif self.is_leaf: + return None + else: + return self.children[i].get(key) + + def optimize_in_order_insertion(self, index: int) -> None: + """Try to minimize the number of Nodes in a BTree where the insertion + is done in-order or close to it, by stealing as much as we can from our + right sibling. + + If we don't do this, then an in-order insertion will produce a BTree + where most of the nodes are minimal. + """ + if index == 0: + return + left = self.children[index - 1] + if len(left.elts) == _MAX(self.t): + return + left = self.maybe_cow_child(index - 1) + while len(left.elts) < _MAX(self.t): + if not left.try_right_steal(self, index - 1): + break + + def insert_nonfull(self, element: ET, in_order: bool) -> ET | None: + assert not self.is_maximal() + while True: + key = element.key() + i, equal = self.search_in_node(key) + if equal: + # replace + old = self.elts[i] + self.elts[i] = element + return old + elif self.is_leaf: + self.elts.insert(i, element) + return None + else: + child = self.maybe_cow_child(i) + if child.is_maximal(): + self.adopt(*child.split()) + # Splitting might result in our target moving to us, so + # search again. + continue + oelt = child.insert_nonfull(element, in_order) + if in_order: + self.optimize_in_order_insertion(i) + return oelt + + def split(self) -> tuple["_Node[KT, ET]", ET, "_Node[KT, ET]"]: + """Split a maximal node into two minimal ones and a central element.""" + assert self.is_maximal() + right = self.__class__(self.t, self.creator, self.is_leaf) + right.elts = list(self.elts[_MIN(self.t) + 1 :]) + middle = self.elts[_MIN(self.t)] + self.elts = list(self.elts[: _MIN(self.t)]) + if not self.is_leaf: + right.children = list(self.children[_MIN(self.t) + 1 :]) + self.children = list(self.children[: _MIN(self.t) + 1]) + return self, middle, right + + def try_left_steal(self, parent: "_Node[KT, ET]", index: int) -> bool: + """Try to steal from this Node's left sibling for balancing purposes. + + Returns ``True`` if the theft was successful, or ``False`` if not. + """ + if index != 0: + left = parent.children[index - 1] + if not left.is_minimal(): + left = parent.maybe_cow_child(index - 1) + elt = parent.elts[index - 1] + parent.elts[index - 1] = left.elts.pop() + self.elts.insert(0, elt) + if not left.is_leaf: + assert not self.is_leaf + child = left.children.pop() + self.children.insert(0, child) + return True + return False + + def try_right_steal(self, parent: "_Node[KT, ET]", index: int) -> bool: + """Try to steal from this Node's right sibling for balancing purposes. + + Returns ``True`` if the theft was successful, or ``False`` if not. + """ + if index + 1 < len(parent.children): + right = parent.children[index + 1] + if not right.is_minimal(): + right = parent.maybe_cow_child(index + 1) + elt = parent.elts[index] + parent.elts[index] = right.elts.pop(0) + self.elts.append(elt) + if not right.is_leaf: + assert not self.is_leaf + child = right.children.pop(0) + self.children.append(child) + return True + return False + + def adopt(self, left: "_Node[KT, ET]", middle: ET, right: "_Node[KT, ET]") -> None: + """Adopt left, middle, and right into our Node (which must not be maximal, + and which must not be a leaf). In the case were we are not the new root, + then the left child must already be in the Node.""" + assert not self.is_maximal() + assert not self.is_leaf + key = middle.key() + i, equal = self.search_in_node(key) + assert not equal + self.elts.insert(i, middle) + if len(self.children) == 0: + # We are the new root + self.children = [left, right] + else: + assert self.children[i] == left + self.children.insert(i + 1, right) + + def merge(self, parent: "_Node[KT, ET]", index: int) -> None: + """Merge this node's parent and its right sibling into this node.""" + right = parent.children.pop(index + 1) + self.elts.append(parent.elts.pop(index)) + self.elts.extend(right.elts) + if not self.is_leaf: + self.children.extend(right.children) + + def minimum(self) -> ET: + """The least element in this subtree.""" + if self.is_leaf: + return self.elts[0] + else: + return self.children[0].minimum() + + def maximum(self) -> ET: + """The greatest element in this subtree.""" + if self.is_leaf: + return self.elts[-1] + else: + return self.children[-1].maximum() + + def balance(self, parent: "_Node[KT, ET]", index: int) -> None: + """This Node is minimal, and we want to make it non-minimal so we can delete. + We try to steal from our siblings, and if that doesn't work we will merge + with one of them.""" + assert not parent.is_leaf + if self.try_left_steal(parent, index): + return + if self.try_right_steal(parent, index): + return + # Stealing didn't work, so both siblings must be minimal. + if index == 0: + # We are the left-most node so merge with our right sibling. + self.merge(parent, index) + else: + # Have our left sibling merge with us. This lets us only have "merge right" + # code. + left = parent.maybe_cow_child(index - 1) + left.merge(parent, index - 1) + + def delete( + self, key: KT, parent: Optional["_Node[KT, ET]"], exact: ET | None + ) -> ET | None: + """Delete an element matching *key* if it exists. If *exact* is not ``None`` + then it must be an exact match with that element. The Node must not be + minimal unless it is the root.""" + assert parent is None or not self.is_minimal() + i, equal = self.search_in_node(key) + original_key = None + if equal: + # Note we use "is" here as we meant "exactly this object". + if exact is not None and self.elts[i] is not exact: + raise ValueError("exact delete did not match existing elt") + if self.is_leaf: + return self.elts.pop(i) + # Note we need to ensure exact is None going forward as we've + # already checked exactness and are about to change our target key + # to the least successor. + exact = None + original_key = key + least_successor = self.children[i + 1].minimum() + key = least_successor.key() + i = i + 1 + if self.is_leaf: + # No match + if exact is not None: + raise ValueError("exact delete had no match") + return None + # recursively delete in the appropriate child + child = self.maybe_cow_child(i) + if child.is_minimal(): + child.balance(self, i) + # Things may have moved. + i, equal = self.search_in_node(key) + assert not equal + child = self.children[i] + assert not child.is_minimal() + elt = child.delete(key, self, exact) + if original_key is not None: + node, i = self._get_node(original_key) + assert node is not None + assert elt is not None + oelt = node.elts[i] + node.elts[i] = elt + elt = oelt + return elt + + def visit_in_order(self, visit: Callable[[ET], None]) -> None: + """Call *visit* on all of the elements in order.""" + for i, elt in enumerate(self.elts): + if not self.is_leaf: + self.children[i].visit_in_order(visit) + visit(elt) + if not self.is_leaf: + self.children[-1].visit_in_order(visit) + + def _visit_preorder_by_node(self, visit: Callable[["_Node[KT, ET]"], None]) -> None: + """Visit nodes in preorder. This method is only used for testing.""" + visit(self) + if not self.is_leaf: + for child in self.children: + child._visit_preorder_by_node(visit) + + def maybe_cow(self, creator: _Creator) -> Optional["_Node[KT, ET]"]: + """Return a clone of this Node if it was not created by *creator*, or ``None`` + otherwise (i.e. copy for copy-on-write if we haven't already copied it).""" + if self.creator is not creator: + return self.clone(creator) + else: + return None + + def clone(self, creator: _Creator) -> "_Node[KT, ET]": + """Make a shallow-copy duplicate of this node.""" + cloned = self.__class__(self.t, creator, self.is_leaf) + cloned.elts.extend(self.elts) + if not self.is_leaf: + cloned.children.extend(self.children) + return cloned + + def __str__(self): # pragma: no cover + if not self.is_leaf: + children = " " + " ".join([f"{id(c):x}" for c in self.children]) + else: + children = "" + return f"{id(self):x} {self.creator} {self.elts}{children}" + + +class Cursor(Generic[KT, ET]): + """A seekable cursor for a BTree. + + If you are going to use a cursor on a mutable BTree, you should use it + in a ``with`` block so that any mutations of the BTree automatically park + the cursor. + """ + + def __init__(self, btree: "BTree[KT, ET]"): + self.btree = btree + self.current_node: _Node | None = None + # The current index is the element index within the current node, or + # if there is no current node then it is 0 on the left boundary and 1 + # on the right boundary. + self.current_index: int = 0 + self.recurse = False + self.increasing = True + self.parents: list[tuple[_Node, int]] = [] + self.parked = False + self.parking_key: KT | None = None + self.parking_key_read = False + + def _seek_least(self) -> None: + # seek to the least value in the subtree beneath the current index of the + # current node + assert self.current_node is not None + while not self.current_node.is_leaf: + self.parents.append((self.current_node, self.current_index)) + self.current_node = self.current_node.children[self.current_index] + assert self.current_node is not None + self.current_index = 0 + + def _seek_greatest(self) -> None: + # seek to the greatest value in the subtree beneath the current index of the + # current node + assert self.current_node is not None + while not self.current_node.is_leaf: + self.parents.append((self.current_node, self.current_index)) + self.current_node = self.current_node.children[self.current_index] + assert self.current_node is not None + self.current_index = len(self.current_node.elts) + + def park(self): + """Park the cursor. + + A cursor must be "parked" before mutating the BTree to avoid undefined behavior. + Cursors created in a ``with`` block register with their BTree and will park + automatically. Note that a parked cursor may not observe some changes made when + it is parked; for example a cursor being iterated with next() will not see items + inserted before its current position. + """ + if not self.parked: + self.parked = True + + def _maybe_unpark(self): + if self.parked: + if self.parking_key is not None: + # remember our increasing hint, as seeking might change it + increasing = self.increasing + if self.parking_key_read: + # We've already returned the parking key, so we want to be before it + # if decreasing and after it if increasing. + before = not self.increasing + else: + # We haven't returned the parking key, so we've parked right + # after seeking or are on a boundary. Either way, the before + # hint we want is the value of self.increasing. + before = self.increasing + self.seek(self.parking_key, before) + self.increasing = increasing # might have been altered by seek() + self.parked = False + self.parking_key = None + + def prev(self) -> ET | None: + """Get the previous element, or return None if on the left boundary.""" + self._maybe_unpark() + self.parking_key = None + if self.current_node is None: + # on a boundary + if self.current_index == 0: + # left boundary, there is no prev + return None + else: + assert self.current_index == 1 + # right boundary; seek to the actual boundary + # so we can do a prev() + self.current_node = self.btree.root + self.current_index = len(self.btree.root.elts) + self._seek_greatest() + while True: + if self.recurse: + if not self.increasing: + # We only want to recurse if we are continuing in the decreasing + # direction. + self._seek_greatest() + self.recurse = False + self.increasing = False + self.current_index -= 1 + if self.current_index >= 0: + elt = self.current_node.elts[self.current_index] + if not self.current_node.is_leaf: + self.recurse = True + self.parking_key = elt.key() + self.parking_key_read = True + return elt + else: + if len(self.parents) > 0: + self.current_node, self.current_index = self.parents.pop() + else: + self.current_node = None + self.current_index = 0 + return None + + def next(self) -> ET | None: + """Get the next element, or return None if on the right boundary.""" + self._maybe_unpark() + self.parking_key = None + if self.current_node is None: + # on a boundary + if self.current_index == 1: + # right boundary, there is no next + return None + else: + assert self.current_index == 0 + # left boundary; seek to the actual boundary + # so we can do a next() + self.current_node = self.btree.root + self.current_index = 0 + self._seek_least() + while True: + if self.recurse: + if self.increasing: + # We only want to recurse if we are continuing in the increasing + # direction. + self._seek_least() + self.recurse = False + self.increasing = True + if self.current_index < len(self.current_node.elts): + elt = self.current_node.elts[self.current_index] + self.current_index += 1 + if not self.current_node.is_leaf: + self.recurse = True + self.parking_key = elt.key() + self.parking_key_read = True + return elt + else: + if len(self.parents) > 0: + self.current_node, self.current_index = self.parents.pop() + else: + self.current_node = None + self.current_index = 1 + return None + + def _adjust_for_before(self, before: bool, i: int) -> None: + if before: + self.current_index = i + else: + self.current_index = i + 1 + + def seek(self, key: KT, before: bool = True) -> None: + """Seek to the specified key. + + If *before* is ``True`` (the default) then the cursor is positioned just + before *key* if it exists, or before its least successor if it doesn't. A + subsequent next() will retrieve this value. If *before* is ``False``, then + the cursor is positioned just after *key* if it exists, or its greatest + precessessor if it doesn't. A subsequent prev() will return this value. + """ + self.current_node = self.btree.root + assert self.current_node is not None + self.recurse = False + self.parents = [] + self.increasing = before + self.parked = False + self.parking_key = key + self.parking_key_read = False + while not self.current_node.is_leaf: + i, equal = self.current_node.search_in_node(key) + if equal: + self._adjust_for_before(before, i) + if before: + self._seek_greatest() + else: + self._seek_least() + return + self.parents.append((self.current_node, i)) + self.current_node = self.current_node.children[i] + assert self.current_node is not None + i, equal = self.current_node.search_in_node(key) + if equal: + self._adjust_for_before(before, i) + else: + self.current_index = i + + def seek_first(self) -> None: + """Seek to the left boundary (i.e. just before the least element). + + A subsequent next() will return the least element if the BTree isn't empty.""" + self.current_node = None + self.current_index = 0 + self.recurse = False + self.increasing = True + self.parents = [] + self.parked = False + self.parking_key = None + + def seek_last(self) -> None: + """Seek to the right boundary (i.e. just after the greatest element). + + A subsequent prev() will return the greatest element if the BTree isn't empty. + """ + self.current_node = None + self.current_index = 1 + self.recurse = False + self.increasing = False + self.parents = [] + self.parked = False + self.parking_key = None + + def __enter__(self): + self.btree.register_cursor(self) + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.btree.deregister_cursor(self) + return False + + +class Immutable(Exception): + """The BTree is immutable.""" + + +class BTree(Generic[KT, ET]): + """An in-memory BTree with copy-on-write and cursors.""" + + def __init__(self, *, t: int = DEFAULT_T, original: Optional["BTree"] = None): + """Create a BTree. + + If *original* is not ``None``, then the BTree is shallow-cloned from + *original* using copy-on-write. Otherwise a new BTree with the specified + *t* value is created. + + The BTree is not thread-safe. + """ + # We don't use a reference to ourselves as a creator as we don't want + # to prevent GC of old btrees. + self.creator = _Creator() + self._immutable = False + self.t: int + self.root: _Node + self.size: int + self.cursors: set[Cursor] = set() + if original is not None: + if not original._immutable: + raise ValueError("original BTree is not immutable") + self.t = original.t + self.root = original.root + self.size = original.size + else: + if t < 3: + raise ValueError("t must be >= 3") + self.t = t + self.root = _Node(self.t, self.creator, True) + self.size = 0 + + def make_immutable(self): + """Make the BTree immutable. + + Attempts to alter the BTree after making it immutable will raise an + Immutable exception. This operation cannot be undone. + """ + if not self._immutable: + self._immutable = True + + def _check_mutable_and_park(self) -> None: + if self._immutable: + raise Immutable + for cursor in self.cursors: + cursor.park() + + # Note that we don't use insert() and delete() but rather insert_element() and + # delete_key() so that BTreeDict can be a proper MutableMapping and supply the + # rest of the standard mapping API. + + def insert_element(self, elt: ET, in_order: bool = False) -> ET | None: + """Insert the element into the BTree. + + If *in_order* is ``True``, then extra work will be done to make left siblings + full, which optimizes storage space when the the elements are inserted in-order + or close to it. + + Returns the previously existing element at the element's key or ``None``. + """ + self._check_mutable_and_park() + cloned = self.root.maybe_cow(self.creator) + if cloned: + self.root = cloned + if self.root.is_maximal(): + old_root = self.root + self.root = _Node(self.t, self.creator, False) + self.root.adopt(*old_root.split()) + oelt = self.root.insert_nonfull(elt, in_order) + if oelt is None: + # We did not replace, so something was added. + self.size += 1 + return oelt + + def get_element(self, key: KT) -> ET | None: + """Get the element matching *key* from the BTree, or return ``None`` if it + does not exist. + """ + return self.root.get(key) + + def _delete(self, key: KT, exact: ET | None) -> ET | None: + self._check_mutable_and_park() + cloned = self.root.maybe_cow(self.creator) + if cloned: + self.root = cloned + elt = self.root.delete(key, None, exact) + if elt is not None: + # We deleted something + self.size -= 1 + if len(self.root.elts) == 0: + # The root is now empty. If there is a child, then collapse this root + # level and make the child the new root. + if not self.root.is_leaf: + assert len(self.root.children) == 1 + self.root = self.root.children[0] + return elt + + def delete_key(self, key: KT) -> ET | None: + """Delete the element matching *key* from the BTree. + + Returns the matching element or ``None`` if it does not exist. + """ + return self._delete(key, None) + + def delete_exact(self, element: ET) -> ET | None: + """Delete *element* from the BTree. + + Returns the matching element or ``None`` if it was not in the BTree. + """ + delt = self._delete(element.key(), element) + assert delt is element + return delt + + def __len__(self): + return self.size + + def visit_in_order(self, visit: Callable[[ET], None]) -> None: + """Call *visit*(element) on all elements in the tree in sorted order.""" + self.root.visit_in_order(visit) + + def _visit_preorder_by_node(self, visit: Callable[[_Node], None]) -> None: + self.root._visit_preorder_by_node(visit) + + def cursor(self) -> Cursor[KT, ET]: + """Create a cursor.""" + return Cursor(self) + + def register_cursor(self, cursor: Cursor) -> None: + """Register a cursor for the automatic parking service.""" + self.cursors.add(cursor) + + def deregister_cursor(self, cursor: Cursor) -> None: + """Deregister a cursor from the automatic parking service.""" + self.cursors.discard(cursor) + + def __copy__(self): + return self.__class__(original=self) + + def __iter__(self): + with self.cursor() as cursor: + while True: + elt = cursor.next() + if elt is None: + break + yield elt.key() + + +VT = TypeVar("VT") # the type of a value in a BTreeDict + + +class KV(Element, Generic[KT, VT]): + """The BTree element type used in a ``BTreeDict``.""" + + def __init__(self, key: KT, value: VT): + self._key = key + self._value = value + + def key(self) -> KT: + return self._key + + def value(self) -> VT: + return self._value + + def __str__(self): # pragma: no cover + return f"KV({self._key}, {self._value})" + + def __repr__(self): # pragma: no cover + return f"KV({self._key}, {self._value})" + + +class BTreeDict(Generic[KT, VT], BTree[KT, KV[KT, VT]], MutableMapping[KT, VT]): + """A MutableMapping implemented with a BTree. + + Unlike a normal Python dict, the BTreeDict may be mutated while iterating. + """ + + def __init__( + self, + *, + t: int = DEFAULT_T, + original: BTree | None = None, + in_order: bool = False, + ): + super().__init__(t=t, original=original) + self.in_order = in_order + + def __getitem__(self, key: KT) -> VT: + elt = self.get_element(key) + if elt is None: + raise KeyError + else: + return cast(KV, elt).value() + + def __setitem__(self, key: KT, value: VT) -> None: + elt = KV(key, value) + self.insert_element(elt, self.in_order) + + def __delitem__(self, key: KT) -> None: + if self.delete_key(key) is None: + raise KeyError + + +class Member(Element, Generic[KT]): + """The BTree element type used in a ``BTreeSet``.""" + + def __init__(self, key: KT): + self._key = key + + def key(self) -> KT: + return self._key + + +class BTreeSet(BTree, Generic[KT], MutableSet[KT]): + """A MutableSet implemented with a BTree. + + Unlike a normal Python set, the BTreeSet may be mutated while iterating. + """ + + def __init__( + self, + *, + t: int = DEFAULT_T, + original: BTree | None = None, + in_order: bool = False, + ): + super().__init__(t=t, original=original) + self.in_order = in_order + + def __contains__(self, key: Any) -> bool: + return self.get_element(key) is not None + + def add(self, value: KT) -> None: + elt = Member(value) + self.insert_element(elt, self.in_order) + + def discard(self, value: KT) -> None: + self.delete_key(value) diff --git a/.venv/lib/python3.12/site-packages/dns/btreezone.py b/.venv/lib/python3.12/site-packages/dns/btreezone.py new file mode 100644 index 0000000..27b5bb6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/btreezone.py @@ -0,0 +1,367 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +# A derivative of a dnspython VersionedZone and related classes, using a BTreeDict and +# a separate per-version delegation index. These additions let us +# +# 1) Do efficient CoW versioning (useful for future online updates). +# 2) Maintain sort order +# 3) Allow delegations to be found easily +# 4) Handle glue +# 5) Add Node flags ORIGIN, DELEGATION, and GLUE whenever relevant. The ORIGIN +# flag is set at the origin node, the DELEGATION FLAG is set at delegation +# points, and the GLUE flag is set on nodes beneath delegation points. + +import enum +from dataclasses import dataclass +from typing import Callable, MutableMapping, Tuple, cast + +import dns.btree +import dns.immutable +import dns.name +import dns.node +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.versioned +import dns.zone + + +class NodeFlags(enum.IntFlag): + ORIGIN = 0x01 + DELEGATION = 0x02 + GLUE = 0x04 + + +class Node(dns.node.Node): + __slots__ = ["flags", "id"] + + def __init__(self, flags: NodeFlags | None = None): + super().__init__() + if flags is None: + # We allow optional flags rather than a default + # as pyright doesn't like assigning a literal 0 + # to flags. + flags = NodeFlags(0) + self.flags = flags + self.id = 0 + + def is_delegation(self): + return (self.flags & NodeFlags.DELEGATION) != 0 + + def is_glue(self): + return (self.flags & NodeFlags.GLUE) != 0 + + def is_origin(self): + return (self.flags & NodeFlags.ORIGIN) != 0 + + def is_origin_or_glue(self): + return (self.flags & (NodeFlags.ORIGIN | NodeFlags.GLUE)) != 0 + + +@dns.immutable.immutable +class ImmutableNode(Node): + def __init__(self, node: Node): + super().__init__() + self.id = node.id + self.rdatasets = tuple( # type: ignore + [dns.rdataset.ImmutableRdataset(rds) for rds in node.rdatasets] + ) + self.flags = node.flags + + 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, + ) -> dns.rdataset.Rdataset | None: + 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 Delegations(dns.btree.BTreeSet[dns.name.Name]): + def get_delegation(self, name: dns.name.Name) -> Tuple[dns.name.Name | None, bool]: + """Get the delegation applicable to *name*, if it exists. + + If there delegation, then return a tuple consisting of the name of + the delegation point, and a boolean which is `True` if the name is a proper + subdomain of the delegation point, and `False` if it is equal to the delegation + point. + """ + cursor = self.cursor() + cursor.seek(name, before=False) + prev = cursor.prev() + if prev is None: + return None, False + cut = prev.key() + reln, _, _ = name.fullcompare(cut) + is_subdomain = reln == dns.name.NameRelation.SUBDOMAIN + if is_subdomain or reln == dns.name.NameRelation.EQUAL: + return cut, is_subdomain + else: + return None, False + + def is_glue(self, name: dns.name.Name) -> bool: + """Is *name* glue, i.e. is it beneath a delegation?""" + cursor = self.cursor() + cursor.seek(name, before=False) + cut, is_subdomain = self.get_delegation(name) + if cut is None: + return False + return is_subdomain + + +class WritableVersion(dns.zone.WritableVersion): + def __init__(self, zone: dns.zone.Zone, replacement: bool = False): + super().__init__(zone, True) + if not replacement: + assert isinstance(zone, dns.versioned.Zone) + version = zone._versions[-1] + self.nodes: dns.btree.BTreeDict[dns.name.Name, Node] = dns.btree.BTreeDict( + original=version.nodes # type: ignore + ) + self.delegations = Delegations(original=version.delegations) # type: ignore + else: + self.delegations = Delegations() + + def _is_origin(self, name: dns.name.Name) -> bool: + # Assumes name has already been validated (and thus adjusted to the right + # relativity too) + if self.zone.relativize: + return name == dns.name.empty + else: + return name == self.zone.origin + + def _maybe_cow_with_name( + self, name: dns.name.Name + ) -> Tuple[dns.node.Node, dns.name.Name]: + (node, name) = super()._maybe_cow_with_name(name) + node = cast(Node, node) + if self._is_origin(name): + node.flags |= NodeFlags.ORIGIN + elif self.delegations.is_glue(name): + node.flags |= NodeFlags.GLUE + return (node, name) + + def update_glue_flag(self, name: dns.name.Name, is_glue: bool) -> None: + cursor = self.nodes.cursor() # type: ignore + cursor.seek(name, False) + updates = [] + while True: + elt = cursor.next() + if elt is None: + break + ename = elt.key() + if not ename.is_subdomain(name): + break + node = cast(dns.node.Node, elt.value()) + if ename not in self.changed: + new_node = self.zone.node_factory() + new_node.id = self.id # type: ignore + new_node.rdatasets.extend(node.rdatasets) + self.changed.add(ename) + node = new_node + assert isinstance(node, Node) + if is_glue: + node.flags |= NodeFlags.GLUE + else: + node.flags &= ~NodeFlags.GLUE + # We don't update node here as any insertion could disturb the + # btree and invalidate our cursor. We could use the cursor in a + # with block and avoid this, but it would do a lot of parking and + # unparking so the deferred update mode may still be better. + updates.append((ename, node)) + for ename, node in updates: + self.nodes[ename] = node + + def delete_node(self, name: dns.name.Name) -> None: + name = self._validate_name(name) + node = self.nodes.get(name) + if node is not None: + if node.is_delegation(): # type: ignore + self.delegations.discard(name) + self.update_glue_flag(name, False) + del self.nodes[name] + self.changed.add(name) + + def put_rdataset( + self, name: dns.name.Name, rdataset: dns.rdataset.Rdataset + ) -> None: + (node, name) = self._maybe_cow_with_name(name) + if ( + rdataset.rdtype == dns.rdatatype.NS and not node.is_origin_or_glue() # type: ignore + ): + node.flags |= NodeFlags.DELEGATION # type: ignore + if name not in self.delegations: + self.delegations.add(name) + self.update_glue_flag(name, True) + node.replace_rdataset(rdataset) + + def delete_rdataset( + self, + name: dns.name.Name, + rdtype: dns.rdatatype.RdataType, + covers: dns.rdatatype.RdataType, + ) -> None: + (node, name) = self._maybe_cow_with_name(name) + if rdtype == dns.rdatatype.NS and name in self.delegations: # type: ignore + node.flags &= ~NodeFlags.DELEGATION # type: ignore + self.delegations.discard(name) # type: ignore + self.update_glue_flag(name, False) + node.delete_rdataset(self.zone.rdclass, rdtype, covers) + if len(node) == 0: + del self.nodes[name] + + +@dataclass(frozen=True) +class Bounds: + name: dns.name.Name + left: dns.name.Name + right: dns.name.Name | None + closest_encloser: dns.name.Name + is_equal: bool + is_delegation: bool + + def __str__(self): + if self.is_equal: + op = "=" + else: + op = "<" + if self.is_delegation: + zonecut = " zonecut" + else: + zonecut = "" + return ( + f"{self.left} {op} {self.name} < {self.right}{zonecut}; " + f"{self.closest_encloser}" + ) + + +@dns.immutable.immutable +class ImmutableVersion(dns.zone.Version): + def __init__(self, version: dns.zone.Version): + if not isinstance(version, WritableVersion): + raise ValueError( + "a dns.btreezone.ImmutableVersion requires a " + "dns.btreezone.WritableVersion" + ) + super().__init__(version.zone, True) + self.id = version.id + self.origin = version.origin + for name in version.changed: + node = version.nodes.get(name) + if node: + version.nodes[name] = ImmutableNode(node) + # the cast below is for mypy + self.nodes = cast(MutableMapping[dns.name.Name, dns.node.Node], version.nodes) + self.nodes.make_immutable() # type: ignore + self.delegations = version.delegations + self.delegations.make_immutable() + + def bounds(self, name: dns.name.Name | str) -> Bounds: + """Return the 'bounds' of *name* in its zone. + + The bounds information is useful when making an authoritative response, as + it can be used to determine whether the query name is at or beneath a delegation + point. The other data in the ``Bounds`` object is useful for making on-the-fly + DNSSEC signatures. + + The left bound of *name* is *name* itself if it is in the zone, or the greatest + predecessor which is in the zone. + + The right bound of *name* is the least successor of *name*, or ``None`` if + no name in the zone is greater than *name*. + + The closest encloser of *name* is *name* itself, if *name* is in the zone; + otherwise it is the name with the largest number of labels in common with + *name* that is in the zone, either explicitly or by the implied existence + of empty non-terminals. + + The bounds *is_equal* field is ``True`` if and only if *name* is equal to + its left bound. + + The bounds *is_delegation* field is ``True`` if and only if the left bound is a + delegation point. + """ + assert self.origin is not None + # validate the origin because we may need to relativize + origin = self.zone._validate_name(self.origin) + name = self.zone._validate_name(name) + cut, _ = self.delegations.get_delegation(name) + if cut is not None: + target = cut + is_delegation = True + else: + target = name + is_delegation = False + c = cast(dns.btree.BTreeDict, self.nodes).cursor() + c.seek(target, False) + left = c.prev() + assert left is not None + c.next() # skip over left + while True: + right = c.next() + if right is None or not right.value().is_glue(): + break + left_comparison = left.key().fullcompare(name) + if right is not None: + right_key = right.key() + right_comparison = right_key.fullcompare(name) + else: + right_comparison = ( + dns.name.NAMERELN_COMMONANCESTOR, + -1, + len(origin), + ) + right_key = None + closest_encloser = dns.name.Name( + name[-max(left_comparison[2], right_comparison[2]) :] + ) + return Bounds( + name, + left.key(), + right_key, + closest_encloser, + left_comparison[0] == dns.name.NameRelation.EQUAL, + is_delegation, + ) + + +class Zone(dns.versioned.Zone): + node_factory: Callable[[], dns.node.Node] = Node + map_factory: Callable[[], MutableMapping[dns.name.Name, dns.node.Node]] = cast( + Callable[[], MutableMapping[dns.name.Name, dns.node.Node]], + dns.btree.BTreeDict[dns.name.Name, Node], + ) + writable_version_factory: ( + Callable[[dns.zone.Zone, bool], dns.zone.Version] | None + ) = WritableVersion + immutable_version_factory: Callable[[dns.zone.Version], dns.zone.Version] | None = ( + ImmutableVersion + ) diff --git a/.venv/lib/python3.12/site-packages/dns/dnssec.py b/.venv/lib/python3.12/site-packages/dns/dnssec.py new file mode 100644 index 0000000..0b2aa70 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssec.py @@ -0,0 +1,1242 @@ +# 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.""" + +# pylint: disable=unused-import + +import base64 +import contextlib +import functools +import hashlib +import struct +import time +from datetime import datetime +from typing import Callable, Dict, List, Set, Tuple, Union, cast + +import dns._features +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 AlgorithmKeyMismatch as AlgorithmKeyMismatch +from dns.exception import 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: 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: 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: 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() + assert rdata is not None # for mypy + 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, key: DNSKEY) -> bool: # pragma: no cover + return False + + def ok_to_validate(self, key: DNSKEY) -> bool: # pragma: no cover + return False + + def ok_to_create_ds(self, algorithm: DSDigest) -> bool: # pragma: no cover + return False + + def ok_to_validate_ds(self, algorithm: 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: dns.name.Name | str, + key: dns.rdata.Rdata, + algorithm: DSDigest | str, + origin: dns.name.Name | None = None, + policy: Policy | None = 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(f'unsupported algorithm "{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(f'unsupported algorithm "{algorithm}"') + + if isinstance(name, str): + name = dns.name.from_text(name, origin) + wire = name.canonicalize().to_wire() + kwire = key.to_wire(origin=origin) + assert wire is not None and kwire is not None # for mypy + dshash.update(wire) + dshash.update(kwire) + 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: dns.name.Name | str, + key: dns.rdata.Rdata, + algorithm: DSDigest | str, + origin: dns.name.Name | None = 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, dns.rdataset.Rdataset | dns.node.Node], rrsig: RRSIG +) -> List[DNSKEY] | None: + 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: 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: + # pylint: disable=possibly-used-before-assignment + 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: dns.rrset.RRset | Tuple[dns.name.Name, dns.rdataset.Rdataset], + rrsig: RRSIG, + keys: Dict[dns.name.Name, dns.node.Node | dns.rdataset.Rdataset], + origin: dns.name.Name | None = None, + now: float | None = None, + policy: Policy | None = 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) + + # pylint: disable=possibly-used-before-assignment + 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: dns.rrset.RRset | Tuple[dns.name.Name, dns.rdataset.Rdataset], + rrsigset: dns.rrset.RRset | Tuple[dns.name.Name, dns.rdataset.Rdataset], + keys: Dict[dns.name.Name, dns.node.Node | dns.rdataset.Rdataset], + origin: dns.name.Name | None = None, + now: float | None = None, + policy: Policy | None = 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: dns.rrset.RRset | Tuple[dns.name.Name, dns.rdataset.Rdataset], + private_key: PrivateKey, + signer: dns.name.Name, + dnskey: DNSKEY, + inception: datetime | str | int | float | None = None, + expiration: datetime | str | int | float | None = None, + lifetime: int | None = None, + verify: bool = False, + policy: Policy | None = None, + origin: dns.name.Name | None = None, + deterministic: bool = True, +) -> 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. + + *deterministic*, a ``bool``. If ``True``, the default, use deterministic + (reproducible) signatures when supported by the algorithm used for signing. + Currently, this only affects ECDSA. + + 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 = _make_rrsig_signature_data(rrset, rrsig_template, origin) + + # pylint: disable=possibly-used-before-assignment + 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, deterministic) + + return cast(RRSIG, rrsig_template.replace(signature=signature)) + + +def _make_rrsig_signature_data( + rrset: dns.rrset.RRset | Tuple[dns.name.Name, dns.rdataset.Rdataset], + rrsig: RRSIG, + origin: dns.name.Name | None = 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"" + wire = rrsig.to_wire(origin=signer) + assert wire is not None # for mypy + data += wire[: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: 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) + + # pylint: disable=possibly-used-before-assignment + 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: 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: dns.name.Name | str, + salt: str | bytes | None, + iterations: int, + algorithm: 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: dns.rrset.RRset | Tuple[dns.name.Name, dns.rdataset.Rdataset], + algorithms: Set[DSDigest | str], + origin: dns.name.Name | None = 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(f'unsupported algorithm "{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: dns.name.Name | str, + rdataset: dns.rdataset.Rdataset, + algorithm: DSDigest | str, + origin: dns.name.Name | None = 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: datetime | str | int | float | None = None, + expiration: datetime | str | int | float | None = None, + lifetime: int | None = None, + policy: Policy | None = None, + origin: dns.name.Name | None = None, + deterministic: bool = True, +) -> 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 = sign( + rrset=rrset, + private_key=private_key, + dnskey=dnskey, + inception=inception, + expiration=expiration, + lifetime=lifetime, + signer=signer, + policy=policy, + origin=origin, + deterministic=deterministic, + ) + txn.add(rrset.name, rrset.ttl, rrsig) + + +def sign_zone( + zone: dns.zone.Zone, + txn: dns.transaction.Transaction | None = None, + keys: List[Tuple[PrivateKey, DNSKEY]] | None = None, + add_dnskey: bool = True, + dnskey_ttl: int | None = None, + inception: datetime | str | int | float | None = None, + expiration: datetime | str | int | float | None = None, + lifetime: int | None = None, + nsec3: NSEC3PARAM | None = None, + rrset_signer: RRsetSigner | None = None, + policy: Policy | None = None, + deterministic: bool = True, +) -> 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. + + *deterministic*, a ``bool``. If ``True``, the default, use deterministic + (reproducible) signatures when supported by the algorithm used for signing. + Currently, this only affects ECDSA. + + 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() + + if zone.origin is None: + raise ValueError("no zone origin") + + 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, + deterministic=deterministic, + ) + return _sign_zone_nsec(zone, _txn, _rrset_signer) + + +def _sign_zone_nsec( + zone: dns.zone.Zone, + txn: dns.transaction.Transaction, + rrset_signer: RRsetSigner | None = None, +) -> None: + """NSEC zone signer""" + + def _txn_add_nsec( + txn: dns.transaction.Transaction, + name: dns.name.Name, + next_secure: dns.name.Name | None, + rdclass: dns.rdataclass.RdataClass, + ttl: int, + rrset_signer: RRsetSigner | None = 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(txn).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 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 new file mode 100644 index 0000000..0810b19 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__init__.py @@ -0,0 +1,124 @@ +from typing import Dict, Tuple, Type + +import dns._features +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 + +# pyright: reportPossiblyUnboundVariable=false + +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 = bytes | dns.name.Name | None + +algorithms: Dict[Tuple[Algorithm, AlgorithmPrefix], Type[GenericPrivateKey]] = {} +if _have_cryptography: + # pylint: disable=possibly-used-before-assignment + 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: 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( + f'algorithm "{Algorithm.to_text(algorithm)}" not supported by dnspython' + ) + + +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: int | str, + algorithm_cls: Type[GenericPrivateKey], + name: dns.name.Name | str | None = None, + oid: bytes | None = 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 new file mode 100644 index 0000000000000000000000000000000000000000..6f4e4adf3f76fa7bd22d96174329acd962984701 GIT binary patch literal 5506 zcmc&1O>Y~=b(hNzarq&UBB^hAZGBR<$Vj#v+f5qCmMl9?WJgkrnn+5~+>x|0xm0IY zPAE`?8U(0xvJ3=}g188CXL|^gacB7iPhm&Kll3ON6n18OS%1RMGRU#mX>MqZi^nb4 zghbcZY#GK;*sU%T$OaR^Y-6I4N_1yJ*>ED9jU*y;&S#pk&57o0OQMC&J(<>QTcR!7 zo@mc@Bs#L4iO%e?#4(_G6M}3LeF*^_2W`g8{j72EvIU#aii?3iCw-z@ViG-wMK;8v z?m1fvupkcAk+CxC75#4ztbo5pJ&V^(8PBc+9bHf!Nbh|(fJ zyKQ2uk>p zr}o5IH(A?Svo>t56C?kN)<7LYb1E47{HpA|(=WWrp@5v8DIm13CrB%Z* zv%Hi+hJ7Z#geIe`5tu+S!czJMmhPrhbPeSJ=^o9@=dh$MWML9|UsjfvmU37{VlCb| zHaUH5{FdPqWko^hsLkLjLdK>?jYbQeoF2b)diu)f5U@ll= zQEuh$6G#;*M(7kMXG(1E++PFgvTLcy?CEVE`pr1yET+ zX=yGm$@7A`fP}K82-Rg3Ia4V~K?*E-nT3HAX^yV0E)yh0SeEa|xsPO10zH0(DgkB~ zE1M!LP+~{ydUlp7zv8jvomgovZs2aF%J1>BcrM$Y3lcP>dF2b;9Q+E)K5RjWk zDy}1tOU)%!^syS{a1+ci$y)}O&L|4Gd^n|_bMb{-7R8m6l1<6+8#uR!(y9`l%H7VX zQd)_>o0I2rV;2WU6-Antvq)CsvAamV8_!6$<7Ev_$A*UEilm~!rBwP(Y91+Z(2jiI zapX7S$)qGpYBCvH${PVtE?ND%BGs=L-IOFfy9GIXl`Sxfg#A!ct8TGabpVUyPCuM&OeIc5 z+afvqLv7J>2mw>r%X<_n+P<#r_5qBm?5a(k_>WrokIJE14X6Q=@~YiPHa6XCm3tLe zffKnE;A{nr)W#Z_CRj5TSo062nW%l`um8qv%U);tEZ7WOQN>})Byce`CxFpd9&WmQ z4x1kK|M1aCOm69WVop)M>|WFn`5RMLZ;sB4gZc&sKV;bDR2CU-5~Y$KNf;R%{3GO{ zGTZg5V+IESiF!fQj!%HRvY)a^!6@3(G` z7M%|RU-4St3#PUQeBb;`d` zr?m1#fwkPwG%&@Z5`Nl5)->Z!Shbp+UgnHemRW@?vuZCeHO~8ztVCVut%7aMqx!15 z)#N6%-ozGcDm)g-&D0SeQ-daMF=Rs4xJY92p;kDMMOW?e^Z^~s2T08-s$*|~JD{Vz z=0w^|X0^kF3e|s@uby3jx;gCF3fa5%e;t;r0(XGotf8=K_0a;=fPKzJ%6lmBT&Z=q z&M7o6DJsG>&U_@Fo57%JEB{*pw+aE0Yz8g}qr&XbyjzYb=GMex$*!`HTT-Q*oXS7} z0PYndgxDl>5SC~uL92>W!W>M%hn|NmkU9oQ7}=cZhol8;wWh zKuxM9JV2H?=)D*mgkoA=MIGf}A2(Ov*9b&BQX!#;9B(C1M{vd54;4HYfQ>vd6&*S# zc)A&HWHbA?=j>z8>-+vDt$Aq2KdjXa?>Ds6)IoclCrB7?`0VO#s9z8DKXUc&dFt2S z*blaA9hY~46PkBo-xJ;xA6$Cu5%wD+Tj%bd+imR88+(f4PUFe-vAsZe!@1`Ul7eYK zj|@C=5A1pT>%X89#&?33HSgv9K;!|ped&w3&6S#=-+TYar^fD&05REj{k~Q zcjbxOOQ)B2{1aN;#9r^|Pv72jZ=cobPdu`B(NG^{C!@B>$>=%5spglU#>OP1V-n{v zIVHnK+$lMSQt&oG3O#?L&U7)OkwEZOD#+OFl!D6lTW?W7Fs!6Wmr6q)rxA76a`77> zoLHn>vC+v}v2sCy$y>y5SGP9o3#q%v2wHN%C17(Ug-OYVNrh&F4pC8>&`B=9r1CmS z7YUtY1x(Vx5$PDHq%gxIFZps&1g1X!Qf7qN3=T8iDr3R+8Q@2N+w{&XI2b;FLzU=?J|I5M82lZgz z!)`qoTdRM5{k=7QeQYzlnJ>n*rXf8r{LPql{%!rtH7#&W_r9~ky!V6)KezjL?P1*> ze(sCx`Z{!9$F{0@yNlU9U*z708y{|uY2M?-3ozUDb?Uy(qFwX$6n{(SUAnKUIHP$7 zv{P?UM4#^Kdl=HZG3~VpiV$?4Q0&&ceOmPV%Mrc0ulFIy9Mt0Dl;a5E;I8+Q8_8`^ z^L7p)4ZjEo#9qlh1z)|Y2XJ3EV zTVi1NW=6YkO&@vZ1(}ovnEq3H&Fw$9yV)~UIRHOzgb)_h8wW}bn3bH2$A7PXqyOIE m#^83B&UX~;I^SJ#5n3I?*KdY4+Dk-6iD#Vrj~;-iS^Nvt*;R!A literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..7601afdf91ac737d6ffd2fb1e8cecdd2ca551cc4 GIT binary patch literal 4611 zcmb_fJ#ZVx72f;BKLL{9k7!bYEb|AlNXUv4%C;n1q9{e95aEcH6QMzXy9EUbxI^}i zN`PVEWRxORtcr9Mrb;+Up(2G#r%B^RiVATq(wH+&3U{EWG_LaA9)1o06{X0|-2T1y zcHh3;{r0=BB9S10bo?u^_}2g--(aJelt$&?GpKA5o#;Y|l!c5?7BixNZLuVk<&0cb zG74`?B{k!NeyQZ=rCbV>gP9$JL5w8}YKyAA#7<@Cz+#amF5uat7Dn(Kp``x=b{SOElsl8m5} zjChk!iORZg4#ZT^MXKtOE*E57xhFqpm}Gn)QK?_|!G}^6mf4mDKq^sxLDU1D-XIOZ zXyL|aL60Ot`__&&hV~SC2u29gm>z*oqG6b&@1BzBry=eajzqw(XnUlVe*;`6#=S$G z{zG&ITV4*drI~nDP7XS;tJI*Zs4cJOOGOQ0>I8Dla68f+Xse9c>&!@NqQ_&b;yxe9 zkZqDG*%8t^WJh#li!gmX=j@!;IK(LA z$-7t~Db|jB0#Pee6KF^E1f0H=#f1c#rr7{kBqbJyf*rwZ3^EHxNF)+$2uer2%gt*e zJzlzkK~Ort0RD=wQ7AMxxgDw7pQDS0X6iHnJC?{(q|Gow9JS zh9fb`?XqB(tjDR_tz>K@*Jr`K#u3du4CfN|ypzKZ?S!e z3s(w9CDix3^S?d6web6;&zJrlN;v<{s z`{}3g$;a`@XM@9==kK3?Iyn7!aC&!OWOry{cX({mxNq#n;!k6fk7JWhV$&}pzF4qM ze1Tx^v4>$h;1CgFVu8_|O}&vSFtgloA=#Vg6(0E}FoaO2xt#Hy<#?oTgH`($Ize|w zvK3nXuSkuq{qz;CEiW$3x|dx~tgPn|s9W#CHKSl6vRD0W!yGzU z7@L^kXT1f>{SDcTjy{n`_msi9C@7Jwg*t&^`|=Ac4kV(+d4FAP)MmC7)R_6xOQ)zc=2W709*B8P~Ua^J+)OpH%>iDet?sm;*+`K$V6;$jCig8c|@015SSvh*7RV8A%@K+?e}6~7HmM5i`oWuSc)@JX%v0p)B2JAtJl_Qh6!u^qzKWS6fYRda9I z0@N%tM38eyhKy{lHrx}8IqXOMHXgqgxW+nwi$A1A!?JURMqL!`0iR~X@u6$I4DCQQ z@E@c<>hQz~Ra_-RE?_3`I{(6iXBq?JP2GVy*?c5iYwjU}bixa+%hwlHPMmE~-V-n& z`ypo6AhU4DX7D)v9Jawt;9o|w!&xOrdY;#rX8b(v!SnP==R|jZxnG_Kmm^|rw0BEg zK>taHQ%Uj4$G}GMq`Roe>1m7sb0zF7X76E!e94u+k2O3)Z6s$OK+}=)rdeW(D7lIm z{)4_6p+7>6^d zxr^yrb1ubonQMm-6yeU{4=8_p-HrbU9qoK)zw@7S3BUz*6Qsy0EPRgZk{}3QlM`Q) t@o&h`KS=yJnS4&>o|EKrGW}AW6-GX@UJ@u?{!Ew<=C`DO6DW9W{sX)3B3%Fg literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/cryptography.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d21435b7a7b6a25fa795e34e9cd80dfa08aa7774 GIT binary patch literal 3821 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/__pycache__/rsa.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ce8bae026893f565d751a605db86085eaedd722 GIT binary patch literal 7281 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 R-!eCe_?8m*e 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, + deterministic: bool = True, + ) -> bytes: + """Sign DNSSEC data""" + + @abstractmethod + def public_key(self) -> "GenericPublicKey": + """Return public key instance""" + + @classmethod + @abstractmethod + def from_pem( + cls, private_pem: bytes, password: bytes | None = None + ) -> "GenericPrivateKey": + """Create private key from PEM-encoded PKCS#8""" + + @abstractmethod + def to_pem(self, password: bytes | None = 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 new file mode 100644 index 0000000..a5dde6a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/cryptography.py @@ -0,0 +1,68 @@ +from typing import Any, 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] # pyright: ignore + + 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: bytes | None = None + ) -> "GenericPrivateKey": + key = serialization.load_pem_private_key(private_pem, password=password) + return cls(key=key) + + def to_pem(self, password: bytes | None = 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 new file mode 100644 index 0000000..a4eb987 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/dsa.py @@ -0,0 +1,108 @@ +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, + deterministic: bool = True, + ) -> 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 # pyright: ignore + ) + 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 new file mode 100644 index 0000000..e3f3f06 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/ecdsa.py @@ -0,0 +1,100 @@ +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, + deterministic: bool = True, + ) -> bytes: + """Sign using a private key per RFC 6605, section 4.""" + algorithm = ec.ECDSA( + self.public_cls.chosen_hash, # pyright: ignore + deterministic_signing=deterministic, + ) + der_signature = self.key.sign(data, algorithm) + dsa_r, dsa_s = utils.decode_dss_signature(der_signature) + signature = int.to_bytes( + dsa_r, length=self.public_cls.octets, byteorder="big" # pyright: ignore + ) + int.to_bytes( + dsa_s, length=self.public_cls.octets, byteorder="big" # pyright: ignore + ) + 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() # pyright: ignore + ), + ) + + +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 new file mode 100644 index 0000000..1cbb407 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/eddsa.py @@ -0,0 +1,70 @@ +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] # pyright: ignore + + def sign( + self, + data: bytes, + verify: bool = False, + deterministic: bool = True, + ) -> 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 new file mode 100644 index 0000000..de9160b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssecalgs/rsa.py @@ -0,0 +1,126 @@ +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, + deterministic: bool = True, + ) -> bytes: + """Sign using a private key per RFC 3110, section 3.""" + signature = self.key.sign( + data, padding.PKCS1v15(), self.public_cls.chosen_hash # pyright: ignore + ) + 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 new file mode 100644 index 0000000..02131e0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/dnssectypes.py @@ -0,0 +1,71 @@ +# 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 new file mode 100644 index 0000000..942d2c0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/e164.py @@ -0,0 +1,116 @@ +# 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 + +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: dns.name.Name | None = 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: dns.name.Name | None = 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[dns.name.Name | str], + resolver: dns.resolver.Resolver | None = 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 = 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 new file mode 100644 index 0000000..eb98548 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/edns.py @@ -0,0 +1,591 @@ +# 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 + +import dns.enum +import dns.inet +import dns.ipv4 +import dns.ipv6 +import dns.name +import dns.rdata +import dns.wire + + +class OptionType(dns.enum.IntEnum): + """EDNS option type codes""" + + #: 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 + #: REPORTCHANNEL + REPORTCHANNEL = 18 + + @classmethod + def _maximum(cls): + return 65535 + + +class Option: + """Base class for all EDNS option types.""" + + def __init__(self, otype: OptionType | str): + """Initialize an option. + + *otype*, a ``dns.edns.OptionType``, is the option type. + """ + self.otype = OptionType.make(otype) + + def to_wire(self, file: Any | None = None) -> bytes | None: + """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 + + def to_generic(self) -> "GenericOption": + """Creates a dns.edns.GenericOption equivalent of this rdata. + + Returns a ``dns.edns.GenericOption``. + """ + wire = self.to_wire() + assert wire is not None # for mypy + return GenericOption(self.otype, wire) + + @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: OptionType | str, data: bytes | str): + super().__init__(otype) + self.data = dns.rdata.Rdata._as_bytes(data, True) + + def to_wire(self, file: Any | None = None) -> bytes | None: + if file: + file.write(self.data) + return None + else: + return self.data + + def to_text(self) -> str: + return f"Generic {self.otype}" + + def to_generic(self) -> "GenericOption": + return self + + @classmethod + def from_wire_parser( + cls, otype: 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: int | None = 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 f"ECS {self.address}/{self.srclen} scope/{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(f'could not parse ECS from "{text}"') + ecs_text = tokens[1] + else: + raise ValueError(f'could not parse ECS from "{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(f'could not parse ECS from "{text}"') + try: + scope = int(tscope) + except ValueError: + raise ValueError("invalid scope " + f'"{tscope}": scope must be an integer') + try: + srclen = int(tsrclen) + except ValueError: + raise ValueError( + "invalid srclen " + f'"{tsrclen}": srclen must be an integer' + ) + return ECSOption(address, srclen, scope) + + def to_wire(self, file: Any | None = None) -> bytes | None: + 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: 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): + """Extended DNS Error (EDE) codes""" + + 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: EDECode | str, text: str | None = 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: Any | None = None) -> bytes | None: + 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: 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) -> bytes | None: + 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: OptionType | str, parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_remaining()) + + +class CookieOption(Option): + def __init__(self, client: bytes, server: bytes): + super().__init__(OptionType.COOKIE) + self.client = client + self.server = server + if len(client) != 8: + raise ValueError("client cookie must be 8 bytes") + if len(server) != 0 and (len(server) < 8 or len(server) > 32): + raise ValueError("server cookie must be empty or between 8 and 32 bytes") + + def to_wire(self, file: Any = None) -> bytes | None: + if file: + file.write(self.client) + if len(self.server) > 0: + file.write(self.server) + return None + else: + return self.client + self.server + + def to_text(self) -> str: + client = binascii.hexlify(self.client).decode() + if len(self.server) > 0: + server = binascii.hexlify(self.server).decode() + else: + server = "" + return f"COOKIE {client}{server}" + + @classmethod + def from_wire_parser( + cls, otype: OptionType | str, parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_bytes(8), parser.get_remaining()) + + +class ReportChannelOption(Option): + # RFC 9567 + def __init__(self, agent_domain: dns.name.Name): + super().__init__(OptionType.REPORTCHANNEL) + self.agent_domain = agent_domain + + def to_wire(self, file: Any = None) -> bytes | None: + return self.agent_domain.to_wire(file) + + def to_text(self) -> str: + return "REPORTCHANNEL " + self.agent_domain.to_text() + + @classmethod + def from_wire_parser( + cls, otype: OptionType | str, parser: dns.wire.Parser + ) -> Option: + return cls(parser.get_name()) + + +_type_to_class: Dict[OptionType, Any] = { + OptionType.ECS: ECSOption, + OptionType.EDE: EDEOption, + OptionType.NSID: NSIDOption, + OptionType.COOKIE: CookieOption, + OptionType.REPORTCHANNEL: ReportChannelOption, +} + + +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: 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: 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 +REPORTCHANNEL = OptionType.REPORTCHANNEL + +### 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 new file mode 100644 index 0000000..6430926 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/entropy.py @@ -0,0 +1,130 @@ +# 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 + + +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: bytes | None = None): + self.pool_index = 0 + self.digest: bytearray | None = 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 | bytearray) -> 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 | bytearray) -> 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: Any | None +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 new file mode 100644 index 0000000..822c995 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/enum.py @@ -0,0 +1,113 @@ +# 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 Any, Type, TypeVar + +TIntEnum = TypeVar("TIntEnum", bound="IntEnum") + + +class IntEnum(enum.IntEnum): + @classmethod + def _missing_(cls, value): + cls._check_value(value) + val = int.__new__(cls, value) # pyright: ignore + val._name_ = cls._extra_to_text(value, None) or f"{cls._prefix()}{value}" + val._value_ = value # pyright: ignore + 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) + return cls(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: 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) -> str: + return "" + + @classmethod + def _extra_from_text(cls, text: str) -> Any | None: # 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) -> Type[Exception]: + return ValueError diff --git a/.venv/lib/python3.12/site-packages/dns/exception.py b/.venv/lib/python3.12/site-packages/dns/exception.py new file mode 100644 index 0000000..c3d42ff --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/exception.py @@ -0,0 +1,169 @@ +# 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 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: str | None = None # non-parametrized message + supp_kwargs: Set[str] = set() # accepted parameters for _fmt_kwargs (sanity check) + fmt: str | None = 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 + ), f"following set of keyword args is required: {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 new file mode 100644 index 0000000..4c60be1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/flags.py @@ -0,0 +1,123 @@ +# 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 new file mode 100644 index 0000000..8d366dc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/grange.py @@ -0,0 +1,72 @@ +# 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.exception + + +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(f"Could not parse {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 new file mode 100644 index 0000000..36b0362 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/immutable.py @@ -0,0 +1,68 @@ +# 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 new file mode 100644 index 0000000..765203b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/inet.py @@ -0,0 +1,195 @@ +# 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, 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: int | None = 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 new file mode 100644 index 0000000..a7161bc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/ipv4.py @@ -0,0 +1,76 @@ +# 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 + +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 f"{address[0]}.{address[1]}.{address[2]}.{address[3]}" + + +def inet_aton(text: 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: 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 inet_ntoa(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 new file mode 100644 index 0000000..eaa0f6c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/ipv6.py @@ -0,0 +1,217 @@ +# 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 + +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: 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 = ( + f"{m.group(1).decode()}:{b[0]:02x}{b[1]:02x}:{b[2]:02x}{b[3]:02x}" + ).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: 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 inet_ntoa(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 new file mode 100644 index 0000000..bbfccfc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/message.py @@ -0,0 +1,1954 @@ +# 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 enum +import io +import time +from typing import Any, Dict, List, Tuple, cast + +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.SOA +import dns.rdtypes.ANY.TSIG +import dns.renderer +import dns.rrset +import dns.tokenizer +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, + dns.rdatatype.RdataType | None, + dns.rdataclass.RdataClass | None, +] +IndexType = Dict[IndexKeyType, dns.rrset.RRset] +SectionType = int | str | List[dns.rrset.RRset] + + +class Message: + """A DNS message.""" + + _section_enum = MessageSection + + def __init__(self, id: int | None = 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: dns.rrset.RRset | None = None + self.request_payload = 0 + self.pad = 0 + self.keyring: Any = None + self.tsig: dns.rrset.RRset | None = None + self.want_tsig_sign = False + self.request_mac = b"" + self.xfr = False + self.origin: dns.name.Name | None = None + self.tsig_ctx: Any | None = None + self.index: IndexType = {} + self.errors: List[MessageError] = [] + self.time = 0.0 + self.wire: bytes | None = None + + @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: dns.name.Name | None = 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(f"id {self.id}\n") + s.write(f"opcode {dns.opcode.to_text(self.opcode())}\n") + s.write(f"rcode {dns.rcode.to_text(self.rcode())}\n") + s.write(f"flags {dns.flags.to_text(self.flags)}\n") + if self.edns >= 0: + s.write(f"edns {self.edns}\n") + if self.ednsflags != 0: + s.write(f"eflags {dns.flags.edns_to_text(self.ednsflags)}\n") + s.write(f"payload {self.payload}\n") + for opt in self.options: + s.write(f"option {opt.to_text()}\n") + 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") + if self.tsig is not None: + s.write(self.tsig.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: dns.rdataclass.RdataClass | None = None, + create: bool = False, + force_unique: bool = False, + idna_codec: dns.name.IDNACodec | None = 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: dns.rdataclass.RdataClass | None = None, + create: bool = False, + force_unique: bool = False, + idna_codec: dns.name.IDNACodec | None = None, + ) -> dns.rrset.RRset | None: + """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. + opt_rdata = cast(dns.rdtypes.ANY.OPT.OPT, self.opt[0]) + for option in opt_rdata.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: dns.name.Name | None = None, + max_size: int = 0, + multi: bool = False, + tsig_ctx: Any | None = 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: + if self.want_tsig_sign: + (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) + if multi: + self.tsig_ctx = ctx + r.add_rrset(dns.renderer.ADDITIONAL, self.tsig) + r.write_header() + wire = r.get_wire() + self.wire = 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: dns.name.Name | str | None = None, + fudge: int = 300, + original_id: int | None = None, + tsig_error: int = 0, + other_data: bytes = b"", + algorithm: dns.name.Name | str = dns.tsig.default_algorithm, + ) -> None: + """When sending, a TSIG signature using the specified key + should be added. + + *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, + ) + self.want_tsig_sign = True + + @property + def keyname(self) -> dns.name.Name | None: + if self.tsig: + return self.tsig.name + else: + return None + + @property + def keyalgorithm(self) -> dns.name.Name | None: + if self.tsig: + rdata = cast(dns.rdtypes.ANY.TSIG.TSIG, self.tsig[0]) + return rdata.algorithm + else: + return None + + @property + def mac(self) -> bytes | None: + if self.tsig: + rdata = cast(dns.rdtypes.ANY.TSIG.TSIG, self.tsig[0]) + return rdata.mac + else: + return None + + @property + def tsig_error(self) -> int | None: + if self.tsig: + rdata = cast(dns.rdtypes.ANY.TSIG.TSIG, self.tsig[0]) + return rdata.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: int | bool | None = 0, + ednsflags: int = 0, + payload: int = DEFAULT_EDNS_PAYLOAD, + request_payload: int | None = None, + options: List[dns.edns.Option] | None = 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: + rdata = cast(dns.rdtypes.ANY.OPT.OPT, self.opt[0]) + return rdata.payload + else: + return 0 + + @property + def options(self) -> Tuple: + if self.opt: + rdata = cast(dns.rdtypes.ANY.OPT.OPT, self.opt[0]) + return rdata.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_options(self, otype: dns.edns.OptionType) -> List[dns.edns.Option]: + """Return the list of options of the specified type.""" + return [option for option in self.options if option.otype == otype] + + def extended_errors(self) -> List[dns.edns.EDEOption]: + """Return the list of Extended DNS Error (EDE) options in the message""" + return cast(List[dns.edns.EDEOption], self.get_options(dns.edns.OptionType.EDE)) + + 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: dns.rrset.RRset | None, + 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 + ) + srdata = cast(dns.rdtypes.ANY.SOA.SOA, srrset[0]) + min_ttl = min(min_ttl, srrset.ttl, srdata.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 # pyright: ignore + 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, # pyright: ignore + 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: + trd = cast(dns.rdtypes.ANY.TSIG.TSIG, rd) + if self.keyring is None or self.keyring is True: + raise UnknownTSIGKey("got signed message without keyring") + elif isinstance(self.keyring, dict): + key = self.keyring.get(absolute_name) + if isinstance(key, bytes): + key = dns.tsig.Key(absolute_name, key, trd.algorithm) + elif callable(self.keyring): + key = self.keyring(self.message, absolute_name) + else: + key = self.keyring + if key is None: + raise UnknownTSIGKey(f"key '{name}' unknown") + if key: + 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, # pyright: ignore + 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.message.wire = self.parser.wire + 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: Any | None = None, + request_mac: bytes | None = b"", + xfr: bool = False, + origin: dns.name.Name | None = None, + tsig_ctx: dns.tsig.HMACTSig | dns.tsig.GSSTSig | None = 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``, ``dict``, ``bool``, or ``None``, the key or keyring + to use if the message is signed. If ``None`` or ``True``, then trying to decode + a message with a TSIG will fail as it cannot be validated. If ``False``, then + TSIG validation is disabled. + + *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: str, + idna_codec: dns.name.IDNACodec | None, + one_rr_per_rrset: bool = False, + origin: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = None, + ): + self.message: Message | None = None # mypy: ignore + 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.""" + + assert self.message is not None + 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. + """ + + assert self.message is not None + 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: dns.name.IDNACodec | None = None, + one_rr_per_rrset: bool = False, + origin: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = 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: dns.name.IDNACodec | None = 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, encoding="utf-8") + 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + use_edns: int | bool | None = None, + want_dnssec: bool = False, + ednsflags: int | None = None, + payload: int | None = None, + request_payload: int | None = None, + options: List[dns.edns.Option] | None = None, + idna_codec: dns.name.IDNACodec | None = None, + id: int | None = 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) + if want_dnssec: + m.want_dnssec(want_dnssec) + return m + + +class CopyMode(enum.Enum): + """ + How should sections be copied when making an update response? + """ + + NOTHING = 0 + QUESTION = 1 + EVERYTHING = 2 + + +def make_response( + query: Message, + recursion_available: bool = False, + our_payload: int = 8192, + fudge: int = 300, + tsig_error: int = 0, + pad: int | None = None, + copy_mode: CopyMode | None = 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. + + Response section(s) which are copied are shallow copies of the matching section(s) + in the query, so the query's 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. + + *copy_mode*, a ``dns.message.CopyMode`` or ``None``, determines how sections are + copied. The default, ``None`` copies sections according to the default for the + message's opcode, which is currently ``dns.message.CopyMode.QUESTION`` for all + opcodes. ``dns.message.CopyMode.QUESTION`` copies only the question section. + ``dns.message.CopyMode.EVERYTHING`` copies all sections other than OPT or TSIG + records, which are created appropriately if needed. ``dns.message.CopyMode.NOTHING`` + copies no sections; note that this mode is for server testing purposes and is + otherwise not recommended for use. In particular, ``dns.message.is_response()`` + will be ``False`` if you create a response this way and the rcode is not + ``FORMERR``, ``SERVFAIL``, ``NOTIMP``, or ``REFUSED``. + + Returns a ``dns.message.Message`` object whose specific class is appropriate for the + query. For example, if query is a ``dns.update.UpdateMessage``, the response will + be one too. + """ + + if query.flags & dns.flags.QR: + raise dns.exception.FormError("specified query message is not a query") + opcode = query.opcode() + factory = _message_factory_from_opcode(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(opcode) + if copy_mode is None: + copy_mode = CopyMode.QUESTION + if copy_mode != CopyMode.NOTHING: + response.question = list(query.question) + if copy_mode == CopyMode.EVERYTHING: + response.answer = list(query.answer) + response.authority = list(query.authority) + response.additional = list(query.additional) + 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 and query.keyring: + assert query.mac is not None + assert query.keyalgorithm is not None + 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 new file mode 100644 index 0000000..45c8f45 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/name.py @@ -0,0 +1,1289 @@ +# 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 + +import dns._features +import dns.enum +import dns.exception +import dns.immutable +import dns.wire + +# Dnspython will never access idna if the import fails, but pyright can't figure +# that out, so... +# +# pyright: reportAttributeAccessIssue = false, reportPossiblyUnboundVariable = false + +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 # pragma: no cover + + @classmethod + def _short_name(cls): + return cls.__name__ # pragma: no cover + + +# 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: 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 += f"\\{c:03d}" + 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 += f"\\{ord(uc):03d}" + 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: + # pylint: disable=possibly-used-before-assignment + 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: 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[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: IDNACodec | None = 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: Any | None = None, + compress: CompressType | None = None, + origin: Optional["Name"] = None, + canonicalize: bool = False, + ) -> bytes | None: + """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: Name | None = root, idna_codec: IDNACodec | None = 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: bytes | str, + origin: Name | None = root, + idna_codec: IDNACodec | None = 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 new file mode 100644 index 0000000..ca8b197 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/namedict.py @@ -0,0 +1,109 @@ +# 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 new file mode 100644 index 0000000..c9307d3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/nameserver.py @@ -0,0 +1,361 @@ +from urllib.parse import urlparse + +import dns.asyncbackend +import dns.asyncquery +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: str | None, + 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: str | None, + 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: str | None, + 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: str | None, + 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: str | None = None, + verify: bool | str = True, + want_get: bool = False, + http_version: dns.query.HTTPVersion = dns.query.HTTPVersion.DEFAULT, + ): + super().__init__() + self.url = url + self.bootstrap_address = bootstrap_address + self.verify = verify + self.want_get = want_get + self.http_version = http_version + + 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: str | None, + 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), + http_version=self.http_version, + ) + + async def async_query( + self, + request: dns.message.QueryMessage, + timeout: float, + source: str | None, + 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), + http_version=self.http_version, + ) + + +class DoTNameserver(AddressAndPortNameserver): + def __init__( + self, + address: str, + port: int = 853, + hostname: str | None = None, + verify: 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: str | None, + 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: str | None, + 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: bool | str = True, + server_hostname: str | None = 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: str | None, + 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: str | None, + 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 new file mode 100644 index 0000000..b2cbf1b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/node.py @@ -0,0 +1,358 @@ +# 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 + +import dns.immutable +import dns.name +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +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, + ) -> dns.rdataset.Rdataset | None: + """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, + ) -> dns.rdataset.Rdataset | None: + 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 new file mode 100644 index 0000000..3fa610d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/opcode.py @@ -0,0 +1,119 @@ +# 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.""" + +from typing import Type + +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) -> Type[Exception]: + 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 new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/dns/query.py b/.venv/lib/python3.12/site-packages/dns/query.py new file mode 100644 index 0000000..17b1862 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/query.py @@ -0,0 +1,1786 @@ +# 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 random +import selectors +import socket +import struct +import time +import urllib.parse +from typing import Any, Callable, Dict, Optional, Tuple, cast + +import dns._features +import dns._tls_util +import dns.exception +import dns.inet +import dns.message +import dns.name +import dns.quic +import dns.rdata +import dns.rdataclass +import dns.rdatatype +import dns.transaction +import dns.tsig +import dns.xfr + +try: + import ssl +except ImportError: + import dns._no_ssl as ssl # type: ignore + + +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=None, local_address=None, 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: + if local_address is None: + local_address = "0.0.0.0" + source = dns.inet.low_level_address_tuple( + (local_address, self._local_port), af + ) + else: + source = None + try: + sock = make_socket(af, socket.SOCK_STREAM, source) + attempt_expiration = _expiration_for_this_attempt(2.0, expiration) + _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=None, socket_options=None + ): # pylint: disable=signature-differs + raise NotImplementedError + + class _HTTPTransport(httpx.HTTPTransport): # pyright: ignore + def __init__( + self, + *args, + local_port=0, + bootstrap_address=None, + resolver=None, + family=socket.AF_UNSPEC, + **kwargs, + ): + if resolver is None and bootstrap_address 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 __init__( + self, + *args, + local_port=0, + bootstrap_address=None, + resolver=None, + family=socket.AF_UNSPEC, + **kwargs, + ): + pass + + def connect_tcp(self, host, port, timeout, local_address): + raise NotImplementedError + + +have_doh = _have_httpx + + +def default_socket_factory( + af: socket.AddressFamily | int, + kind: socket.SocketKind, + proto: int, +) -> socket.socket: + return socket.socket(af, kind, proto) + + +# Function used to create a socket. Can be overridden if needed in special +# situations. +socket_factory: Callable[ + [socket.AddressFamily | int, socket.SocketKind, int], socket.socket +] = default_socket_factory + + +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 + with selectors.DefaultSelector() as sel: + events = 0 + if readable: + events |= selectors.EVENT_READ + if writable: + events |= selectors.EVENT_WRITE + if events: + sel.register(fd, events) # pyright: ignore + 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 _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: socket.AddressFamily | int, + type: socket.SocketKind, + source: Any | None = None, +) -> socket.socket: + """Make a socket. + + This function uses the module's ``socket_factory`` to make a socket of the + specified address family and type. + + *af*, a ``socket.AddressFamily`` or ``int`` is the address family, either + ``socket.AF_INET`` or ``socket.AF_INET6``. + + *type*, a ``socket.SocketKind`` is the type of socket, e.g. ``socket.SOCK_DGRAM``, + a datagram socket, or ``socket.SOCK_STREAM``, a stream socket. Note that the + ``proto`` attribute of a socket is always zero with this API, so a datagram socket + will always be a UDP socket, and a stream socket will always be a TCP socket. + + *source* is the source address and port to bind to, if any. The default is + ``None`` which will bind to the wildcard address and a randomly chosen port. + If not ``None``, it should be a (low-level) address tuple appropriate for *af*. + """ + s = socket_factory(af, type, 0) + try: + s.setblocking(False) + if source is not None: + s.bind(source) + return s + except Exception: + s.close() + raise + + +def make_ssl_socket( + af: socket.AddressFamily | int, + type: socket.SocketKind, + ssl_context: ssl.SSLContext, + server_hostname: dns.name.Name | str | None = None, + source: Any | None = None, +) -> ssl.SSLSocket: + """Make a socket. + + This function uses the module's ``socket_factory`` to make a socket of the + specified address family and type. + + *af*, a ``socket.AddressFamily`` or ``int`` is the address family, either + ``socket.AF_INET`` or ``socket.AF_INET6``. + + *type*, a ``socket.SocketKind`` is the type of socket, e.g. ``socket.SOCK_DGRAM``, + a datagram socket, or ``socket.SOCK_STREAM``, a stream socket. Note that the + ``proto`` attribute of a socket is always zero with this API, so a datagram socket + will always be a UDP socket, and a stream socket will always be a TCP socket. + + If *ssl_context* is not ``None``, then it specifies the SSL context to use, + typically created with ``make_ssl_context()``. + + If *server_hostname* is not ``None``, then it is the hostname to use for server + certificate validation. A valid hostname must be supplied if *ssl_context* + requires hostname checking. + + *source* is the source address and port to bind to, if any. The default is + ``None`` which will bind to the wildcard address and a randomly chosen port. + If not ``None``, it should be a (low-level) address tuple appropriate for *af*. + """ + sock = make_socket(af, type, source) + if isinstance(server_hostname, dns.name.Name): + server_hostname = server_hostname.to_text() + # LGTM gets a false positive here, as our default context is OK + return ssl_context.wrap_socket( + sock, + do_handshake_on_connect=False, # lgtm[py/insecure-protocol] + server_hostname=server_hostname, + ) + + +# for backwards compatibility +def _make_socket( + af, + type, + source, + ssl_context, + server_hostname, +): + if ssl_context is not None: + return make_ssl_socket(af, type, ssl_context, server_hostname, source) + else: + return make_socket(af, type, source) + + +def _maybe_get_resolver( + resolver: Optional["dns.resolver.Resolver"], # pyright: ignore +) -> "dns.resolver.Resolver": # pyright: ignore + # We need a separate method for this to avoid overriding the global + # variable "dns" with the as-yet undefined local variable "dns" + # in https(). + if resolver is None: + # pylint: disable=import-outside-toplevel,redefined-outer-name + import dns.resolver + + resolver = dns.resolver.Resolver() + return resolver + + +class HTTPVersion(enum.IntEnum): + """Which version of HTTP should be used? + + DEFAULT will select the first version from the list [2, 1.1, 3] that + is available. + """ + + DEFAULT = 0 + HTTP_1 = 1 + H1 = 1 + HTTP_2 = 2 + H2 = 2 + HTTP_3 = 3 + H3 = 3 + + +def https( + q: dns.message.Message, + where: str, + timeout: float | None = None, + port: int = 443, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + session: Any | None = None, + path: str = "/dns-query", + post: bool = True, + bootstrap_address: str | None = None, + verify: bool | str | ssl.SSLContext = True, + resolver: Optional["dns.resolver.Resolver"] = None, # pyright: ignore + family: int = socket.AF_UNSPEC, + http_version: HTTPVersion = HTTPVersion.DEFAULT, +) -> 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. + + *http_version*, a ``dns.query.HTTPVersion``, indicating which HTTP version to use. + + Returns a ``dns.message.Message``. + """ + + (af, _, the_source) = _destination_and_source( + where, port, source, source_port, False + ) + # we bind url and then override as pyright can't figure out all paths bind. + url = where + if af is not None and dns.inet.is_address(where): + if af == socket.AF_INET: + url = f"https://{where}:{port}{path}" + elif af == socket.AF_INET6: + url = f"https://[{where}]:{port}{path}" + + extensions = {} + if bootstrap_address is None: + # pylint: disable=possibly-used-before-assignment + parsed = urllib.parse.urlparse(url) + if parsed.hostname is None: + raise ValueError("no hostname in URL") + if dns.inet.is_address(parsed.hostname): + bootstrap_address = parsed.hostname + extensions["sni_hostname"] = parsed.hostname + if parsed.port is not None: + port = parsed.port + + if http_version == HTTPVersion.H3 or ( + http_version == HTTPVersion.DEFAULT and not have_doh + ): + if bootstrap_address is None: + resolver = _maybe_get_resolver(resolver) + assert parsed.hostname is not None # pyright: ignore + answers = resolver.resolve_name(parsed.hostname, family) # pyright: ignore + bootstrap_address = random.choice(list(answers.addresses())) + if session and not isinstance( + session, dns.quic.SyncQuicConnection + ): # pyright: ignore + raise ValueError("session parameter must be a dns.quic.SyncQuicConnection.") + return _http3( + q, + bootstrap_address, + url, # pyright: ignore + timeout, + port, + source, + source_port, + one_rr_per_rrset, + ignore_trailing, + verify=verify, + post=post, + connection=session, + ) + + if not have_doh: + raise NoDOH # pragma: no cover + if session and not isinstance(session, httpx.Client): # pyright: ignore + raise ValueError("session parameter must be an httpx.Client") + + wire = q.to_wire() + headers = {"accept": "application/dns-message"} + + h1 = http_version in (HTTPVersion.H1, HTTPVersion.DEFAULT) + h2 = http_version in (HTTPVersion.H2, HTTPVersion.DEFAULT) + + # 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] + + if session: + cm: contextlib.AbstractContextManager = contextlib.nullcontext(session) + else: + transport = _HTTPTransport( + local_address=local_address, + http1=h1, + http2=h2, + verify=verify, + local_port=local_port, + bootstrap_address=bootstrap_address, + resolver=resolver, + family=family, # pyright: ignore + ) + + cm = httpx.Client( # type: ignore + http1=h1, http2=h2, verify=verify, transport=transport # type: ignore + ) + with cm as session: + # see https://tools.ietf.org/html/rfc8484#section-4.1.1 for DoH + # GET and POST examples + assert session is not None + if post: + headers.update( + { + "content-type": "application/dns-message", + "content-length": str(len(wire)), + } + ) + response = session.post( + url, + headers=headers, + content=wire, + timeout=timeout, + extensions=extensions, + ) + 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}, + extensions=extensions, + ) + + # 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( + f"{where} responded with status code {response.status_code}" + f"\nResponse body: {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 _find_header(headers: dns.quic.Headers, name: bytes) -> bytes: + if headers is None: + raise KeyError + for header, value in headers: + if header == name: + return value + raise KeyError + + +def _check_status(headers: dns.quic.Headers, peer: str, wire: bytes) -> None: + value = _find_header(headers, b":status") + if value is None: + raise SyntaxError("no :status header in response") + status = int(value) + if status < 0: + raise SyntaxError("status is negative") + if status < 200 or status > 299: + error = "" + if len(wire) > 0: + try: + error = ": " + wire.decode() + except Exception: + pass + raise ValueError(f"{peer} responded with status code {status}{error}") + + +def _http3( + q: dns.message.Message, + where: str, + url: str, + timeout: float | None = None, + port: int = 443, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + verify: bool | str | ssl.SSLContext = True, + post: bool = True, + connection: dns.quic.SyncQuicConnection | None = None, +) -> dns.message.Message: + if not dns.quic.have_quic: + raise NoDOH("DNS-over-HTTP3 is not available.") # pragma: no cover + + url_parts = urllib.parse.urlparse(url) + hostname = url_parts.hostname + assert hostname is not None + if url_parts.port is not None: + port = url_parts.port + + 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) + else: + manager = dns.quic.SyncQuicManager( + verify_mode=verify, server_name=hostname, h3=True # pyright: ignore + ) + the_manager = manager # for type checking happiness + + with manager: + if connection: + the_connection = connection + else: + the_connection = the_manager.connect( # pyright: ignore + where, port, source, source_port + ) + (start, expiration) = _compute_times(timeout) + with the_connection.make_stream(timeout) as stream: # pyright: ignore + stream.send_h3(url, wire, post) + wire = stream.receive(_remaining(expiration)) + _check_status(stream.headers(), where, wire) + 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 _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: dns.message.Message | bytes, + destination: Any, + expiration: float | None = 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: Any | None = None, + expiration: float | None = None, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + keyring: Dict[dns.name.Name, dns.tsig.Key] | None = None, + request_mac: bytes | None = b"", + ignore_trailing: bool = False, + raise_on_truncation: bool = False, + ignore_errors: bool = False, + query: dns.message.Message | None = 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: float | None = None, + port: int = 53, + source: str | None = 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: Any | None = 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, True + ) + (begin_time, expiration) = _compute_times(timeout) + if sock: + cm: contextlib.AbstractContextManager = contextlib.nullcontext(sock) + else: + assert af is not None + 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: float | None = None, + port: int = 53, + source: str | None = None, + source_port: int = 0, + ignore_unexpected: bool = False, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + udp_sock: Any | None = None, + tcp_sock: Any | None = 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("EOF") + 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: dns.message.Message | bytes, + expiration: float | None = 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: float | None = None, + one_rr_per_rrset: bool = False, + keyring: Dict[dns.name.Name, dns.tsig.Key] | None = None, + request_mac: bytes | None = 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: float | None = None, + port: int = 53, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: Any | None = 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, True + ) + assert af is not None + cm = make_socket(af, socket.SOCK_STREAM, source) + with cm as s: + if not sock: + # pylint: disable=possibly-used-before-assignment + _connect(s, destination, expiration) # pyright: ignore + 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_ssl_context( + verify: bool | str = True, + check_hostname: bool = True, + alpns: list[str] | None = None, +) -> ssl.SSLContext: + """Make an SSL context + + If *verify* is ``True``, the default, then certificate verification will occur using + the standard CA roots. If *verify* is ``False``, then certificate verification will + be disabled. If *verify* is a string which is a valid pathname, then if the + pathname is a regular file, the CA roots will be taken from the file, otherwise if + the pathname is a directory roots will be taken from the directory. + + If *check_hostname* is ``True``, the default, then the hostname of the server must + be specified when connecting and the server's certificate must authorize the + hostname. If ``False``, then hostname checking is disabled. + + *aplns* is ``None`` or a list of TLS ALPN (Application Layer Protocol Negotiation) + strings to use in negotiation. For DNS-over-TLS, the right value is `["dot"]`. + """ + cafile, capath = dns._tls_util.convert_verify_to_cafile_and_capath(verify) + ssl_context = ssl.create_default_context(cafile=cafile, capath=capath) + # the pyright ignores below are because it gets confused between the + # _no_ssl compatibility types and the real ones. + ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 # type: ignore + ssl_context.check_hostname = check_hostname + if verify is False: + ssl_context.verify_mode = ssl.CERT_NONE # type: ignore + if alpns is not None: + ssl_context.set_alpn_protocols(alpns) + return ssl_context # type: ignore + + +# for backwards compatibility +def _make_dot_ssl_context( + server_hostname: str | None, verify: bool | str +) -> ssl.SSLContext: + return make_ssl_context(verify, server_hostname is not None, ["dot"]) + + +def tls( + q: dns.message.Message, + where: str, + timeout: float | None = None, + port: int = 853, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + sock: ssl.SSLSocket | None = None, + ssl_context: ssl.SSLContext | None = None, + server_hostname: str | None = None, + verify: 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, True + ) + assert af is not None # where must be an address + if ssl_context is None: + ssl_context = make_ssl_context(verify, server_hostname is not None, ["dot"]) + + with make_ssl_socket( + af, + socket.SOCK_STREAM, + ssl_context=ssl_context, + server_hostname=server_hostname, + source=source, + ) 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: float | None = None, + port: int = 853, + source: str | None = None, + source_port: int = 0, + one_rr_per_rrset: bool = False, + ignore_trailing: bool = False, + connection: dns.quic.SyncQuicConnection | None = None, + verify: bool | str = True, + hostname: str | None = None, + server_hostname: str | None = 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. + + *hostname*, a ``str`` containing the server's hostname or ``None``. The default is + ``None``, which means that no hostname is known, and if an SSL context is created, + hostname checking will be disabled. This value is ignored if *url* is not + ``None``. + + *server_hostname*, a ``str`` or ``None``. This item is for backwards compatibility + only, and has the same meaning as *hostname*. + + Returns a ``dns.message.Message``. + """ + + if not dns.quic.have_quic: + raise NoDOQ("DNS-over-QUIC is not available.") # pragma: no cover + + if server_hostname is not None and hostname is None: + hostname = server_hostname + + 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=hostname # pyright: ignore + ) + the_manager = manager # for type checking happiness + + with manager: + if not connection: + the_connection = the_manager.connect( # pyright: ignore + where, port, source, source_port + ) + (start, expiration) = _compute_times(timeout) + with the_connection.make_stream(timeout) as stream: # pyright: ignore + 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 + + +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( + txn_manager: dns.transaction.TransactionManager, + s: socket.socket | ssl.SSLSocket, + query: dns.message.Message, + serial: int | None, + timeout: float | None, + expiration: float | None, +) -> Any: + """Given a socket, does the zone transfer.""" + rdtype = query.question[0].rdtype + is_ixfr = rdtype == dns.rdatatype.IXFR + origin = txn_manager.from_wire_origin() + wire = query.to_wire() + is_udp = isinstance(s, socket.socket) and s.type == socket.SOCK_DGRAM + 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 + r: dns.message.Message | None = 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, + ) + done = inbound.process_message(r) + yield r + tsig_ctx = r.tsig_ctx + if query.keyring and r is not None and not r.had_tsig: + raise dns.exception.FormError("missing TSIG") + + +def xfr( + where: str, + zone: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.AXFR, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + timeout: float | None = None, + port: int = 53, + keyring: Dict[dns.name.Name, dns.tsig.Key] | None = None, + keyname: dns.name.Name | str | None = None, + relativize: bool = True, + lifetime: float | None = None, + source: str | None = None, + source_port: int = 0, + serial: int = 0, + use_udp: bool = False, + keyalgorithm: 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. + """ + + class DummyTransactionManager(dns.transaction.TransactionManager): + def __init__(self, origin, relativize): + self.info = (origin, relativize, dns.name.empty if relativize else origin) + + def origin_information(self): + return self.info + + def get_class(self) -> dns.rdataclass.RdataClass: + raise NotImplementedError # pragma: no cover + + def reader(self): + raise NotImplementedError # pragma: no cover + + def writer(self, replacement: bool = False) -> dns.transaction.Transaction: + class DummyTransaction: + def nop(self, *args, **kw): + pass + + def __getattr__(self, _): + return self.nop + + return cast(dns.transaction.Transaction, DummyTransaction()) + + 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 = q.find_rrset( + q.authority, zone, dns.rdataclass.IN, dns.rdatatype.SOA, create=True + ) + soa = dns.rdata.from_text("IN", "SOA", f". . {serial} 0 0 0 0") + rrset.add(soa, 0) + if keyring is not None: + q.use_tsig(keyring, keyname, algorithm=keyalgorithm) + (af, destination, source) = _destination_and_source( + where, port, source, source_port, True + ) + assert af is not None + (_, expiration) = _compute_times(lifetime) + tm = DummyTransactionManager(zone, relativize) + 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: + _connect(s, destination, expiration) + yield from _inbound_xfr(tm, s, q, serial, timeout, expiration) + + +def inbound_xfr( + where: str, + txn_manager: dns.transaction.TransactionManager, + query: dns.message.Message | None = None, + port: int = 53, + timeout: float | None = None, + lifetime: float | None = None, + source: str | None = 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.query.UDPMode.NEVER``, i.e. only use + TCP. Other possibilities are ``dns.query.UDPMode.TRY_FIRST``, which + means "try UDP but fallback to TCP if needed", and + ``dns.query.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) + + (af, destination, source) = _destination_and_source( + where, port, source, source_port, True + ) + assert af is not None + (_, expiration) = _compute_times(lifetime) + if query.question[0].rdtype == dns.rdatatype.IXFR and udp_mode != UDPMode.NEVER: + with make_socket(af, socket.SOCK_DGRAM, source) as s: + _connect(s, destination, expiration) + try: + for _ in _inbound_xfr( + txn_manager, s, query, serial, timeout, expiration + ): + pass + return + except dns.xfr.UseTCP: + if udp_mode == UDPMode.ONLY: + raise + + with make_socket(af, socket.SOCK_STREAM, source) as s: + _connect(s, destination, expiration) + for _ in _inbound_xfr(txn_manager, s, query, serial, timeout, expiration): + pass diff --git a/.venv/lib/python3.12/site-packages/dns/quic/__init__.py b/.venv/lib/python3.12/site-packages/dns/quic/__init__.py new file mode 100644 index 0000000..7c2a699 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/quic/__init__.py @@ -0,0 +1,78 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +from typing import Any, Dict, List, Tuple + +import dns._features +import dns.asyncbackend + +if dns._features.have("doq"): + from dns._asyncbackend import NullContext + from dns.quic._asyncio import AsyncioQuicConnection as AsyncioQuicConnection + from dns.quic._asyncio import AsyncioQuicManager + from dns.quic._asyncio import AsyncioQuicStream as AsyncioQuicStream + from dns.quic._common import AsyncQuicConnection # pyright: ignore + from dns.quic._common import AsyncQuicManager as AsyncQuicManager + from dns.quic._sync import SyncQuicConnection # pyright: ignore + from dns.quic._sync import SyncQuicStream # pyright: ignore + from dns.quic._sync import SyncQuicManager as SyncQuicManager + + 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: Dict[str, Tuple[Any, Any]] = { + "asyncio": (null_factory, _asyncio_manager_factory) + } + + if dns._features.have("trio"): + import trio + + # pylint: disable=ungrouped-imports + from dns.quic._trio import TrioQuicConnection as TrioQuicConnection + from dns.quic._trio import TrioQuicManager + from dns.quic._trio import TrioQuicStream as 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 + + 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 + + +Headers = List[Tuple[bytes, bytes]] 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 new file mode 100644 index 0000000000000000000000000000000000000000..581ceb7e2d3652164b125b2cf43b6b6ed22ba88e GIT binary patch literal 3325 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/_common.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/_common.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f28559f8c867593e2d3d8a27b2f1aeec091ab7c GIT binary patch literal 15073 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..c1b23e038ed5fa432a21109fd6c939a9a6181fd9 GIT binary patch literal 19818 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 C;IJG3 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/_trio.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/quic/__pycache__/_trio.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99134dfc6eea9882cf73c899e2ec2be6ae947c37 GIT binary patch literal 16159 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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) + await self._wakeup() + + 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: + if self._manager is not None: + 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() + await self._wakeup() + try: + if self._receiver_task is not None: + await self._receiver_task + except asyncio.CancelledError: + pass + try: + if self._sender_task is not None: + await self._sender_task + except asyncio.CancelledError: + pass + if self._socket is not None: + await self._socket.close() + + +class AsyncioQuicManager(AsyncQuicManager): + def __init__( + self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None, h3=False + ): + super().__init__(conf, verify_mode, AsyncioQuicConnection, server_name, h3) + + 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 new file mode 100644 index 0000000..ba9d245 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/quic/_common.py @@ -0,0 +1,344 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import base64 +import copy +import functools +import socket +import struct +import time +import urllib.parse +from typing import Any + +import aioquic.h3.connection # type: ignore +import aioquic.quic.configuration # type: ignore +import aioquic.quic.connection # type: ignore + +import dns._tls_util +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 + + def get_all(self): + assert self.seen_end() + data = self._buffer + self._buffer = b"" + return data + + +class BaseQuicStream: + def __init__(self, connection, stream_id): + self._connection = connection + self._stream_id = stream_id + self._buffer = Buffer() + self._expecting = 0 + self._headers = None + self._trailers = None + + def id(self): + return self._stream_id + + def headers(self): + return self._headers + + def trailers(self): + return self._trailers + + 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. + + # Subclass must implement send() as sync / async and which takes a message and + # an EOF indicator. + + def send_h3(self, url, datagram, post=True): + if not self._connection.is_h3(): + raise SyntaxError("cannot send H3 to a non-H3 connection") + url_parts = urllib.parse.urlparse(url) + path = url_parts.path.encode() + if post: + method = b"POST" + else: + method = b"GET" + path += b"?dns=" + base64.urlsafe_b64encode(datagram).rstrip(b"=") + headers = [ + (b":method", method), + (b":scheme", url_parts.scheme.encode()), + (b":authority", url_parts.netloc.encode()), + (b":path", path), + (b"accept", b"application/dns-message"), + ] + if post: + headers.extend( + [ + (b"content-type", b"application/dns-message"), + (b"content-length", str(len(datagram)).encode()), + ] + ) + self._connection.send_headers(self._stream_id, headers, not post) + if post: + self._connection.send_data(self._stream_id, datagram, True) + + def _encapsulate(self, datagram): + if self._connection.is_h3(): + return 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) + ) or self._buffer.seen_end + 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 = {} + if manager is not None and manager.is_h3(): + self._h3_conn = aioquic.h3.connection.H3Connection(connection, False) + else: + self._h3_conn = None + 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 is_h3(self): + return self._h3_conn is not None + + def close_stream(self, stream_id): + del self._streams[stream_id] + + def send_headers(self, stream_id, headers, is_end=False): + assert self._h3_conn is not None + self._h3_conn.send_headers(stream_id, headers, is_end) + + def send_data(self, stream_id, data, is_end=False): + assert self._h3_conn is not None + self._h3_conn.send_data(stream_id, data, is_end) + + 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: float | None = None) -> Any: + pass + + +class BaseQuicManager: + def __init__( + self, conf, verify_mode, connection_factory, server_name=None, h3=False + ): + self._connections = {} + self._connection_factory = connection_factory + self._session_tickets = {} + self._tokens = {} + self._h3 = h3 + if conf is None: + verify_path = None + if isinstance(verify_mode, str): + verify_path = verify_mode + verify_mode = True + if h3: + alpn_protocols = ["h3"] + else: + alpn_protocols = ["doq", "doq-i03"] + conf = aioquic.quic.configuration.QuicConfiguration( + alpn_protocols=alpn_protocols, + verify_mode=verify_mode, + server_name=server_name, + ) + if verify_path is not None: + cafile, capath = dns._tls_util.convert_verify_to_cafile_and_capath( + verify_path + ) + conf.load_verify_locations(cafile=cafile, capath=capath) + self._conf = conf + + def _connect( + self, + address, + port=853, + source=None, + source_port=0, + want_session_ticket=True, + want_token=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 + if want_token: + try: + token = self._tokens.pop((address, port)) + # We found a token, so make a configuration that uses it. + conf = copy.copy(conf) + conf.token = token + except KeyError: + # No token + pass + # Whether or not we found a token, we want a handler to save # one. + token_handler = functools.partial(self.save_token, address, port) + else: + token_handler = None + + qconn = aioquic.quic.connection.QuicConnection( + configuration=conf, + session_ticket_handler=session_ticket_handler, + token_handler=token_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 is_h3(self): + return self._h3 + + 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 + + def save_token(self, address, port, token): + # 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._tokens) + if l >= MAX_SESSION_TICKETS: + keys_to_delete = list(self._tokens.keys())[0:SESSIONS_TO_DELETE] + for key in keys_to_delete: + del self._tokens[key] + self._tokens[(address, port)] = token + + +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 new file mode 100644 index 0000000..18f9d05 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/quic/_sync.py @@ -0,0 +1,306 @@ +# 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.h3.connection # type: ignore +import aioquic.h3.events # type: ignore +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, +) + +# Function used to create a socket. Can be overridden if needed in special +# situations. +socket_factory = socket.socket + + +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 wait_for_end(self, expiration): + while True: + timeout = self._timeout_from_expiration(expiration) + with self._lock: + if self._buffer.seen_end(): + return + with self._wake_up: + if not self._wake_up.wait(timeout): + raise dns.exception.Timeout + + def receive(self, timeout=None): + expiration = self._expiration_from_timeout(timeout) + if self._connection.is_h3(): + self.wait_for_end(expiration) + with self._lock: + return self._buffer.get_all() + else: + 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_factory(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: + with selectors.DefaultSelector() as sel: + 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 + except Exception: + # Eat all exceptions as we have no way to pass them back to the + # caller currently. It might be nice to fix this in the future. + pass + finally: + with self._lock: + self._done = True + self._socket.close() + # 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): + if self.is_h3(): + assert self._h3_conn is not None + h3_events = self._h3_conn.handle_event(event) + for h3_event in h3_events: + if isinstance(h3_event, aioquic.h3.events.HeadersReceived): + with self._lock: + stream = self._streams.get(event.stream_id) + if stream: + if stream._headers is None: + stream._headers = h3_event.headers + elif stream._trailers is None: + stream._trailers = h3_event.headers + if h3_event.stream_ended: + stream._add_input(b"", True) + elif isinstance(h3_event, aioquic.h3.events.DataReceived): + with self._lock: + stream = self._streams.get(event.stream_id) + if stream: + stream._add_input(h3_event.data, h3_event.stream_ended) + else: + 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 send_headers(self, stream_id, headers, is_end=False): + with self._lock: + super().send_headers(stream_id, headers, is_end) + if is_end: + self._send_wakeup.send(b"\x01") + + def send_data(self, stream_id, data, is_end=False): + with self._lock: + super().send_data(stream_id, data, is_end) + if 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 + if self._manager is not None: + self._manager.closed(self._peer[0], self._peer[1]) + self._closed = True + self._connection.close() + self._send_wakeup.send(b"\x01") + if self._worker_thread is not None: + self._worker_thread.join() + + +class SyncQuicManager(BaseQuicManager): + def __init__( + self, conf=None, verify_mode=ssl.CERT_REQUIRED, server_name=None, h3=False + ): + super().__init__(conf, verify_mode, SyncQuicConnection, server_name, h3) + self._lock = threading.Lock() + + def connect( + self, + address, + port=853, + source=None, + source_port=0, + want_session_ticket=True, + want_token=True, + ): + with self._lock: + (connection, start) = self._connect( + address, port, source, source_port, want_session_ticket, want_token + ) + 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 save_token(self, address, port, token): + with self._lock: + super().save_token(address, port, token) + + 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 new file mode 100644 index 0000000..046e6aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/quic/_trio.py @@ -0,0 +1,250 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import socket +import ssl +import struct +import time + +import aioquic.h3.connection # type: ignore +import aioquic.h3.events # type: ignore +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 wait_for_end(self): + while True: + if self._buffer.seen_end(): + return + async with self._wake_up: + await self._wake_up.wait() + + async def receive(self, timeout=None): + if timeout is None: + context = NullContext(None) + else: + context = trio.move_on_after(timeout) + with context: + if self._connection.is_h3(): + await self.wait_for_end() + return self._buffer.get_all() + else: + 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 # pyright: ignore + ) 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._socket.close() + 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): + if self.is_h3(): + assert self._h3_conn is not None + h3_events = self._h3_conn.handle_event(event) + for h3_event in h3_events: + if isinstance(h3_event, aioquic.h3.events.HeadersReceived): + stream = self._streams.get(event.stream_id) + if stream: + if stream._headers is None: + stream._headers = h3_event.headers + elif stream._trailers is None: + stream._trailers = h3_event.headers + if h3_event.stream_ended: + await stream._add_input(b"", True) + elif isinstance(h3_event, aioquic.h3.events.DataReceived): + stream = self._streams.get(event.stream_id) + if stream: + await stream._add_input( + h3_event.data, h3_event.stream_ended + ) + else: + 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: + if self._manager is not None: + 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, + h3=False, + ): + super().__init__(conf, verify_mode, TrioQuicConnection, server_name, h3) + 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 new file mode 100644 index 0000000..7bb8467 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rcode.py @@ -0,0 +1,168 @@ +# 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, Type + +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) -> Type[Exception]: + 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 new file mode 100644 index 0000000..c4522e6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdata.py @@ -0,0 +1,935 @@ +# 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 ipaddress +import itertools +import random +from importlib import import_module +from typing import Any, Dict, Tuple + +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 += f"\\{c:03d}" + 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: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + ) -> None: + """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: dns.name.Name | None = 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: Any, + compress: dns.name.CompressType | None = None, + origin: dns.name.Name | None = None, + canonicalize: bool = False, + ) -> None: + raise NotImplementedError # pragma: no cover + + def to_wire( + self, + file: Any | None = None, + compress: dns.name.CompressType | None = None, + origin: dns.name.Name | None = None, + canonicalize: bool = False, + ) -> bytes | None: + """Convert an rdata to wire format. + + Returns a ``bytes`` if no output file was specified, or ``None`` otherwise. + """ + + if file: + # We call _to_wire() and then return None explicitly instead of + # of just returning the None from _to_wire() as mypy's func-returns-value + # unhelpfully errors out with "error: "_to_wire" of "Rdata" does not return + # a value (it only ever returns None)" + self._to_wire(file, compress, origin, canonicalize) + return None + else: + f = io.BytesIO() + self._to_wire(f, compress, origin, canonicalize) + return f.getvalue() + + def to_generic(self, origin: dns.name.Name | None = None) -> "GenericRdata": + """Creates a dns.rdata.GenericRdata equivalent of this rdata. + + Returns a ``dns.rdata.GenericRdata``. + """ + wire = self.to_wire(origin=origin) + assert wire is not None # for type checkers + return GenericRdata(self.rdclass, self.rdtype, wire) + + def to_digestable(self, origin: dns.name.Name | None = None) -> bytes: + """Convert rdata to a format suitable for digesting in hashes. This + is also the DNSSEC canonical form. + + Returns a ``bytes``. + """ + wire = self.to_wire(origin=origin, canonicalize=True) + assert wire is not None # for mypy + return wire + + 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. + """ + # the next two lines are for type checkers, so they are bound + our = b"" + their = b"" + 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: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = 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: dns.name.Name | None = 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( + f"'{self.__class__.__name__}' object has no attribute '{key}'" + ) + if key in ("rdclass", "rdtype"): + raise AttributeError( + f"Cannot overwrite '{self.__class__.__name__}' attribute '{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: int | None = 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) + elif isinstance(value, ipaddress.IPv4Address): + return dns.ipv4.inet_ntoa(value.packed) + 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) + elif isinstance(value, ipaddress.IPv6Address): + return dns.ipv6.inet_ntoa(value.packed) + 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: dns.rdataclass.RdataClass, + rdtype: dns.rdatatype.RdataType, + data: bytes, + ) -> None: + super().__init__(rdclass, rdtype) + self.data = data + + def to_text( + self, + origin: dns.name.Name | None = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + return rf"\# {len(self.data)} " + _hexify(self.data, **kw) # pyright: ignore + + @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) + + def to_generic(self, origin: dns.name.Name | None = None) -> "GenericRdata": + return self + + @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" +_dynamic_load_allowed = True + + +def get_rdata_class(rdclass, rdtype, use_generic=True): + cls = _rdata_classes.get((rdclass, rdtype)) + if not cls: + cls = _rdata_classes.get((dns.rdataclass.ANY, rdtype)) + if not cls and _dynamic_load_allowed: + 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 and use_generic: + cls = GenericRdata + _rdata_classes[(rdclass, rdtype)] = cls + return cls + + +def load_all_types(disable_dynamic_load=True): + """Load all rdata types for which dnspython has a non-generic implementation. + + Normally dnspython loads DNS rdatatype implementations on demand, but in some + specialized cases loading all types at an application-controlled time is preferred. + + If *disable_dynamic_load*, a ``bool``, is ``True`` then dnspython will not attempt + to use its dynamic loading mechanism if an unknown type is subsequently encountered, + and will simply use the ``GenericRdata`` class. + """ + # Load class IN and ANY types. + for rdtype in dns.rdatatype.RdataType: + get_rdata_class(dns.rdataclass.IN, rdtype, False) + # Load the one non-ANY implementation we have in CH. Everything + # else in CH is an ANY type, and we'll discover those on demand but won't + # have to import anything. + get_rdata_class(dns.rdataclass.CH, dns.rdatatype.A, False) + if disable_dynamic_load: + # Now disable dynamic loading so any subsequent unknown type immediately becomes + # GenericRdata without a load attempt. + global _dynamic_load_allowed + _dynamic_load_allowed = False + + +def from_text( + rdclass: dns.rdataclass.RdataClass | str, + rdtype: dns.rdatatype.RdataType | str, + tok: dns.tokenizer.Tokenizer | str, + origin: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = None, + idna_codec: dns.name.IDNACodec | None = 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) + if not isinstance(tok, dns.tokenizer.Tokenizer): + raise ValueError("tok must be a string or a Tokenizer") + rdclass = dns.rdataclass.RdataClass.make(rdclass) + rdtype = dns.rdatatype.RdataType.make(rdtype) + cls = get_rdata_class(rdclass, rdtype) + assert cls is not None # for type checkers + 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: dns.rdataclass.RdataClass | str, + rdtype: dns.rdatatype.RdataType | str, + parser: dns.wire.Parser, + origin: dns.name.Name | None = 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) + assert cls is not None # for type checkers + with dns.exception.ExceptionWrapper(dns.exception.FormError): + return cls.from_wire_parser(rdclass, rdtype, parser, origin) + + +def from_wire( + rdclass: dns.rdataclass.RdataClass | str, + rdtype: dns.rdatatype.RdataType | str, + wire: bytes, + current: int, + rdlen: int, + origin: dns.name.Name | None = 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 subclass of ``dns.rdata.Rdata`` implementing the type, + or a module containing such a class named by its text form. + + *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) + if isinstance(implementation, type) and issubclass(implementation, Rdata): + impclass = implementation + else: + impclass = getattr(implementation, rdtype_text.replace("-", "_")) + _rdata_classes[(rdclass, rdtype)] = impclass + 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 new file mode 100644 index 0000000..89b85a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdataclass.py @@ -0,0 +1,118 @@ +# 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 new file mode 100644 index 0000000..1edf67d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdataset.py @@ -0,0 +1,508 @@ +# 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, 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 = cast(Rdataset, 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 + + # pylint: disable=arguments-differ,arguments-renamed + def add( # pyright: ignore + self, rd: dns.rdata.Rdata, ttl: int | None = 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 "[" + ", ".join(f"<{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: dns.name.Name | None = None, + origin: dns.name.Name | None = None, + relativize: bool = True, + override_rdclass: dns.rdataclass.RdataClass | None = 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( + f"{ntext}{pad}{dns.rdataclass.to_text(rdclass)} " + f"{dns.rdatatype.to_text(self.rdtype)}\n" + ) + else: + for rd in self: + extra = "" + if want_comments: + if rd.rdcomment: + extra = f" ;{rd.rdcomment}" + s.write( + f"{ntext}{pad}{self.ttl} " + f"{dns.rdataclass.to_text(rdclass)} " + f"{dns.rdatatype.to_text(self.rdtype)} " + f"{rd.to_text(origin=origin, relativize=relativize, **kw)}" + f"{extra}\n" + ) + # + # 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: dns.name.CompressType | None = None, + origin: dns.name.Name | None = None, + override_rdclass: dns.rdataclass.RdataClass | None = 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: 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)) # pyright: ignore + + +@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()) # pyright: ignore + + def copy(self): + return ImmutableRdataset(super().copy()) # pyright: ignore + + def union(self, other): + return ImmutableRdataset(super().union(other)) # pyright: ignore + + def intersection(self, other): + return ImmutableRdataset(super().intersection(other)) # pyright: ignore + + def difference(self, other): + return ImmutableRdataset(super().difference(other)) # pyright: ignore + + def symmetric_difference(self, other): + return ImmutableRdataset(super().symmetric_difference(other)) # pyright: ignore + + +def from_text_list( + rdclass: dns.rdataclass.RdataClass | str, + rdtype: dns.rdatatype.RdataType | str, + ttl: int, + text_rdatas: Collection[str], + idna_codec: dns.name.IDNACodec | None = None, + origin: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = 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: dns.rdataclass.RdataClass | str, + rdtype: 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 new file mode 100644 index 0000000..211d810 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdatatype.py @@ -0,0 +1,338 @@ +# 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 + DSYNC = 66 + 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 + RESINFO = 261 + WALLET = 262 + 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: # pragma: no cover + 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 +DSYNC = RdataType.DSYNC +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 +RESINFO = RdataType.RESINFO +WALLET = RdataType.WALLET +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 new file mode 100644 index 0000000..06a3b97 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AFSDB.py @@ -0,0 +1,45 @@ +# 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 new file mode 100644 index 0000000..dc9fa87 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AMTRELAY.py @@ -0,0 +1,89 @@ +# 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.rdata +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 ( + f"{self.precedence} {self.discovery_optional:d} {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 new file mode 100644 index 0000000..a27ae2d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/AVC.py @@ -0,0 +1,26 @@ +# 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 new file mode 100644 index 0000000..8c62e62 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CAA.py @@ -0,0 +1,67 @@ +# 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 f'{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 new file mode 100644 index 0000000..b613409 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDNSKEY.py @@ -0,0 +1,33 @@ +# 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 new file mode 100644 index 0000000..8312b97 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CDS.py @@ -0,0 +1,29 @@ +# 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 new file mode 100644 index 0000000..4d5e5bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CERT.py @@ -0,0 +1,113 @@ +# 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) + algorithm = dns.dnssectypes.Algorithm.to_text(self.algorithm) + certificate = dns.rdata._base64ify(self.certificate, **kw) # pyright: ignore + return f"{certificate_type} {self.key_tag} {algorithm} {certificate}" + + @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 new file mode 100644 index 0000000..665e407 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CNAME.py @@ -0,0 +1,28 @@ +# 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 new file mode 100644 index 0000000..103486d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/CSYNC.py @@ -0,0 +1,68 @@ +# 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 f"{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 new file mode 100644 index 0000000..6c134f1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DLV.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..bbf9186 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNAME.py @@ -0,0 +1,27 @@ +# 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 new file mode 100644 index 0000000..6d961a9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DNSKEY.py @@ -0,0 +1,33 @@ +# 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 new file mode 100644 index 0000000..58b3108 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DS.py @@ -0,0 +1,24 @@ +# 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/DSYNC.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DSYNC.py new file mode 100644 index 0000000..e8d1394 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/DSYNC.py @@ -0,0 +1,72 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.enum +import dns.exception +import dns.immutable +import dns.rdata +import dns.rdatatype +import dns.rdtypes.util + + +class UnknownScheme(dns.exception.DNSException): + """Unknown DSYNC scheme""" + + +class Scheme(dns.enum.IntEnum): + """DSYNC SCHEME""" + + NOTIFY = 1 + + @classmethod + def _maximum(cls): + return 255 + + @classmethod + def _unknown_exception_class(cls): + return UnknownScheme + + +@dns.immutable.immutable +class DSYNC(dns.rdata.Rdata): + """DSYNC record""" + + # see: draft-ietf-dnsop-generalized-notify + + __slots__ = ["rrtype", "scheme", "port", "target"] + + def __init__(self, rdclass, rdtype, rrtype, scheme, port, target): + super().__init__(rdclass, rdtype) + self.rrtype = self._as_rdatatype(rrtype) + self.scheme = Scheme.make(scheme) + 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 ( + f"{dns.rdatatype.to_text(self.rrtype)} {Scheme.to_text(self.scheme)} " + f"{self.port} {target}" + ) + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + rrtype = dns.rdatatype.from_text(tok.get_string()) + scheme = Scheme.make(tok.get_string()) + port = tok.get_uint16() + target = tok.get_name(origin, relativize, relativize_to) + return cls(rdclass, rdtype, rrtype, scheme, port, target) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + three_ints = struct.pack("!HBH", self.rrtype, self.scheme, self.port) + file.write(three_ints) + self.target.to_wire(file, None, origin, False) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + (rrtype, scheme, port) = parser.get_struct("!HBH") + target = parser.get_name(origin) + return cls(rdclass, rdtype, rrtype, scheme, port, target) 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 new file mode 100644 index 0000000..c843be5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI48.py @@ -0,0 +1,30 @@ +# 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 new file mode 100644 index 0000000..f6d7e25 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/EUI64.py @@ -0,0 +1,30 @@ +# 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 new file mode 100644 index 0000000..d79f4a0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/GPOS.py @@ -0,0 +1,126 @@ +# 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 ( + f"{self.latitude.decode()} {self.longitude.decode()} " + f"{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 new file mode 100644 index 0000000..06ad348 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HINFO.py @@ -0,0 +1,64 @@ +# 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 f'"{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 new file mode 100644 index 0000000..dc7948a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/HIP.py @@ -0,0 +1,85 @@ +# 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 f"{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 new file mode 100644 index 0000000..6428a0a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ISDN.py @@ -0,0 +1,78 @@ +# 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 ( + f'"{dns.rdata._escapify(self.address)}" ' + f'"{dns.rdata._escapify(self.subaddress)}"' + ) + else: + return f'"{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 new file mode 100644 index 0000000..f51e5c7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L32.py @@ -0,0 +1,42 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.ipv4 +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 new file mode 100644 index 0000000..a47da19 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/L64.py @@ -0,0 +1,48 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.rdata +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 new file mode 100644 index 0000000..6c7fe5e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LOC.py @@ -0,0 +1,347 @@ +# 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(f"{desc} value out of bounds") + 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(f"bad {desc} exponent") + base = (what & 0xF0) >> 4 + if base > 9: + raise dns.exception.FormError(f"bad {desc} base") + 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) # pyright: ignore + 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) # pyright: ignore + 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 = ( + f"{self.latitude[0]} {self.latitude[1]} " + f"{self.latitude[2]}.{self.latitude[3]:03d} {lat_hemisphere} " + f"{self.longitude[0]} {self.longitude[1]} " + f"{self.longitude[2]}.{self.longitude[3]:03d} {long_hemisphere} " + f"{(self.altitude / 100.0):0.2f}m" + ) + + # do not print default values + if ( + self.size != _default_size + or self.horizontal_precision != _default_hprec + or self.vertical_precision != _default_vprec + ): + text += ( + f" {self.size / 100.0:0.2f}m {self.horizontal_precision / 100.0:0.2f}m" + f" {self.vertical_precision / 100.0:0.2f}m" + ) + 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 new file mode 100644 index 0000000..379c862 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/LP.py @@ -0,0 +1,42 @@ +# 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 f"{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 new file mode 100644 index 0000000..0c300c5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/MX.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..fa0dad5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NID.py @@ -0,0 +1,48 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import struct + +import dns.immutable +import dns.rdata +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 new file mode 100644 index 0000000..b177bdd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NINFO.py @@ -0,0 +1,26 @@ +# 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 new file mode 100644 index 0000000..c3f34ce --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NS.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..3c78b72 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC.py @@ -0,0 +1,67 @@ +# 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 f"{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 new file mode 100644 index 0000000..6899418 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3.py @@ -0,0 +1,120 @@ +# 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.name +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 f"{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 new file mode 100644 index 0000000..e867872 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/NSEC3PARAM.py @@ -0,0 +1,69 @@ +# 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 f"{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 new file mode 100644 index 0000000..ac1841c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPENPGPKEY.py @@ -0,0 +1,53 @@ +# 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) # pyright: ignore + + @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 new file mode 100644 index 0000000..d343dfa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/OPT.py @@ -0,0 +1,77 @@ +# 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 new file mode 100644 index 0000000..98c3616 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/PTR.py @@ -0,0 +1,24 @@ +# 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/RESINFO.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py new file mode 100644 index 0000000..76c8ea2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RESINFO.py @@ -0,0 +1,24 @@ +# 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 RESINFO(dns.rdtypes.txtbase.TXTBase): + """RESINFO 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 new file mode 100644 index 0000000..a66cfc5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RP.py @@ -0,0 +1,58 @@ +# 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 f"{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 new file mode 100644 index 0000000..5556cba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RRSIG.py @@ -0,0 +1,155 @@ +# 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 ( + f"{dns.rdatatype.to_text(self.type_covered)} " + f"{self.algorithm} {self.labels} {self.original_ttl} " + f"{posixtime_to_sigtime(self.expiration)} " + f"{posixtime_to_sigtime(self.inception)} " + f"{self.key_tag} " + f"{self.signer.choose_relativity(origin, relativize)} " + f"{dns.rdata._base64ify(self.signature, **kw)}" # pyright: ignore + ) + + @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) # pyright: ignore 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 new file mode 100644 index 0000000..5a4d45c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/RT.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..55d87bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SMIMEA.py @@ -0,0 +1,9 @@ +# 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 new file mode 100644 index 0000000..3c7cd8c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SOA.py @@ -0,0 +1,78 @@ +# 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 f"{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 new file mode 100644 index 0000000..1df3b70 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SPF.py @@ -0,0 +1,26 @@ +# 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 new file mode 100644 index 0000000..3f08f3a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/SSHFP.py @@ -0,0 +1,67 @@ +# 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) + fingerprint = dns.rdata._hexify( + self.fingerprint, chunksize=chunksize, **kw # pyright: ignore + ) + return f"{self.algorithm} {self.fp_type} {fingerprint}" + + @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 new file mode 100644 index 0000000..f9189b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TKEY.py @@ -0,0 +1,135 @@ +# 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) + key = dns.rdata._base64ify(self.key, 0) + other = "" + if len(self.other) > 0: + other = " " + dns.rdata._base64ify(self.other, 0) + return f"{_algorithm} {self.inception} {self.expiration} {self.mode} {self.error} {key}{other}" + + @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 new file mode 100644 index 0000000..4dffc55 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TLSA.py @@ -0,0 +1,9 @@ +# 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 new file mode 100644 index 0000000..7942382 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TSIG.py @@ -0,0 +1,160 @@ +# 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 new file mode 100644 index 0000000..6d4dae2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/TXT.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..021391d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/URI.py @@ -0,0 +1,79 @@ +# 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 f'{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/WALLET.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py new file mode 100644 index 0000000..ff46476 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/WALLET.py @@ -0,0 +1,9 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import dns.immutable +import dns.rdtypes.txtbase + + +@dns.immutable.immutable +class WALLET(dns.rdtypes.txtbase.TXTBase): + """WALLET record""" 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 new file mode 100644 index 0000000..2436ddb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/X25.py @@ -0,0 +1,57 @@ +# 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 f'"{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 new file mode 100644 index 0000000..acef4f2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/ZONEMD.py @@ -0,0 +1,64 @@ +# 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) + digest = dns.rdata._hexify( + self.digest, chunksize=chunksize, **kw # pyright: ignore + ) + return f"{self.serial} {self.scheme} {self.hash_algorithm} {digest}" + + @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 new file mode 100644 index 0000000..cc39f86 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__init__.py @@ -0,0 +1,71 @@ +# 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", + "DSYNC", + "EUI48", + "EUI64", + "GPOS", + "HINFO", + "HIP", + "ISDN", + "L32", + "L64", + "LOC", + "LP", + "MX", + "NID", + "NINFO", + "NS", + "NSEC", + "NSEC3", + "NSEC3PARAM", + "OPENPGPKEY", + "OPT", + "PTR", + "RESINFO", + "RP", + "RRSIG", + "RT", + "SMIMEA", + "SOA", + "SPF", + "SSHFP", + "TKEY", + "TLSA", + "TSIG", + "TXT", + "URI", + "WALLET", + "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 new file mode 100644 index 0000000000000000000000000000000000000000..ab4d440736b834a2ba6f563f72afa1b5d82f5a77 GIT binary patch literal 1084 zcmbVK&rcIU6rS1bj|I0x36er6VxotdwBwHh7h_T+dLR%ZG0|Q&+ufnv+MQWvwn!TZ znvf_bj`%MS{uC~7B3Vzyled!f#L1c2t(3$QlkB|reedmi-@KXQLSY<;b@y2FwsQdd zl3=(5RxAE4%mHwL3p=0=mDTDH!}c&EO_b(;oCaP3SK9@yF8(n_l2f;>G+gD|=2a}w zk)CizX{es>!E)xeL~BmUDWjzzekhy+fR*aH=EAy;Gg8UF3R!{m5uEE~%GoGed$GQ} z)Hjt`VA!F|b>K(`=}R&3tZMKHv`2zNcO8^r|5n(-3rQW`ZG^o5mtiy>FuZ{oCJsJ? zQHJBr2A3rlE_~FYK1ST;zD>|8M%&m4IeJYSH1r&fRw-%H<)uoEbFWE!OhQ!M#AFk7 zyaoz-VT+O{)klvJ_d;9=Z0Eh*#2mSVBj(EH9M#s|A*H7p^rC#Ksb5H_6y6tF_=MO# zgFz15oZ8>n+c`1jV|lV`&SCO$4lT+S5v8oSKNr=cj9#)@xhyI$f&2FrFsof5zrSKn|BLiEW@m2;+MI-u?k*#Qkd7uca@gGgv%@#Xs6KEPZ>v ezp}S-X3n3Q^JnJbsks;fL5p)~Gv_iv_4o?_R`a6( literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/AMTRELAY.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1927d7e60a306460260f0af972b02d7156d8d347 GIT binary patch literal 4235 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 j_N_2o6{c&Q9cBDQ?RIW9hiO!eKo{vy$OZt z$z%N&q<>0Jds>J$J$b9dQ%}ChX3>Ikc<=kZ_mel5t(F7$?qAXn8UQ~GS-j;MlwS~> zg8)Gy@JQvUMC^TOdgR$>wv zyvq5zi67Mwn{%N8&c8xwCR`t}LlsM=eI5G=dzq?{3^L7Lse#JEK(iMr8LDT!-LBT* zFo|W7G51&|$1Dm5Y@BC@DtYWa+G9G*4$Y27eV{r z%|Q>d<|9{3|7yrBMAyyz`Ej14Ye>zdbq~#zMF=S$0@?TmcdwR#E}gYMv}a^%O16Ge iLN-1=_`LIJXXdo0PJ8BTPo3=&P%bT?cYc|KYQ`V^OqGHF literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..436a37d03f3524360b42afe7b282bf78571a87c0 GIT binary patch literal 3393 zcmbsrOKcm*b@t08B~sMKv0c~}?f4_AwOQMNBqXk5#dXqO)Wi~smIb_C?T+Y`mb>cA z(lQwePzwQ}+5{5O7F5v`ReNayUD96KYk>kSkc$Yqkh=9DMSAf~wR{MCY2WN}Nmfx@ zARUlz=6&Yvn|Z(aARdn)7$-mA&GjfkA2B2xa&xiw4lHWOKn7=_Y%p_K-p7UJEH-B; z%VU1FJ5{+1UvpWpf@U~FSXQ!Tpu4VT>_9Ax{ir!&X~I9nvR_sFSvIcV!Fj}Rr2ZS6v4Vf zjI`iMGnSU4p5SUZPr9pF1+0g6&yFI&pPgp!p8$T!)sPEwFjnL;dK>sq$%5RQ<^n@N3#ogOiVo;^(0!D$HY@q)wE4lRlTUH zQMF*&?igX#2KaQ*#gwqy^dctJEZFbGzQwNi1|be15#XdFo=CAZ<3)&}TNEop{9$^&ujXemcuk-*fbNEK{MkM-^oB^ zU6?PDF8FY%n<3%BPXUE~7r@6$=z$VniPV)N>xVba-dEB)hmO{6Rc~$FXdD`@h;PMq z`VZHxR_(-E~bgO&xzc;=e+XMATZv_n? z>;jlAdw&5?V_^jI{_%4t8E!Ky5t>Hq~6%1`w7%HHX|4cglLf6*ERvkKp zU&xTg;=~g!RpL{po^Yqqau7!Xc*2v2p}87F0H(S&Z_X6kVUZRIt5co~NuD!pFGjEh z8GhF+VUM3%pzPEvQpf<4inxyI;(KnXi}|pL33gO&TpeCJw*LA%^q0k- z6}Ja3)dw%V|J?7#emm9}ys~xTOMkfeC*jZ0KSsB&P1dhXZcTlAd+Jtw>Q>{Ly7l7i zt=rmG&-9iuO%8!Rz4Ln6+OH$Og9W%um~GYrKwBfsAz9n2@)q{2zOx zJOmS~XXQv)DMvS(m1h#Y$4x-3jOMT#WVsh(OX?ES&UrGnb;rQzm?!8KCB0xR=>q^o z)tMv57!E^DFZL7096z!KV3Q13u3UXA~_ zry}hndROfgyOB7toj6lZoM|M^RfL^ne=Si>G?J<9^r zm*1BfJtJGnNZ_?rwFT|0cKKd=4ZHv%_3l7yCdc0Amzmw)54yjJNbEnDfQ#b>(({fv7!50tO9Tr zWjEg$Q3^z!x?mFAtm92=1jMKX0E&Rpy&7AI{bZ!}LiL4pcVp@|*`a_#aD7b=O$!u~C;~}$Qcmq$a{HSBNUS$^_c~2?@`uW0*8cEIq7FL^NAYlD-E=Ay@Prc zHqsXXETIpR&uk}C^<=7%JjHT;U^{WPo;ce`46$Sf-syVsbR&88)3ZFyPfS7lO+i(g zn0PrjZ#R3-*jgULdnTr;dB-SNY~8J@-z&iDCE!F<)o^tG$)i%sajB~M?*cLirg>fd z8zqn7U15+RppeT9j4@uk`GV20WvObKBmF?7tPNcAs~r)UCD;c2WP+12z)dNs3>*+YQBN%^DyVhsNy{{f{Xyf^>= literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..a290ad43c3dac9c85880e00f0c46f8014cff66b3 GIT binary patch literal 744 zcmZvaF>BjE6vyvQlAWrtD1ioNFm1t$hv;a!mO|2q4r!f`Kp|QVSvnlqlFrr10d*?` z+D;ww8A|#^N+wT+>()-)5>zN;>YXg1r8m6ydr$9w@AU4jI~^0q_2e3b-*o_fsi0o1 z3W{$MEcXKbQzO)Gmm`})8 zP^eCK44<)>V(#(SOYl+3Ce+V3K4v49g+9k`STbh&d)vF5hvOusNrvrnnw;Y(9N}4> zow4MZ{d5QOFr(Wu&!2i@%5jizoCaAwle{}T#noBuS)R6K*U~(azzu0Gz)!uKcIy1# zzUWB39merI^F|RBnheFJ%2j*XlBYBud7P$d+NpdfAXR&bdyTAJRSUBz7v^ewdrDd! zMZJzyb9Lu;YtL7Mdy#I+P~8T9EzOMvAuRd;KKucCe_J|4w?LYk4S@HyU j4}x&(lmBJRT12(K@ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..a7b8c809b7a42a02c769b9deb84e2d324264de50 GIT binary patch literal 852 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 KTfcIF+3^=nlixf5 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/CSYNC.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b574dde156743cb3113e4f186fd72c26f2e3432 GIT binary patch literal 3352 zcmb7GUu+}A8J}J6uGdZ++Xr!S>4hdGf-}~|kZaR6hxm8g(NwArkwXZBwCs5|VV$+t zompQl(WVVmBpj(I7qyjBohkyVL{MJnQ(ybe%UM2@u;k%@7kCT!9?(7U&FmkWG*s%y zn)znFZ@w9S-|zR$ew;|e5w!7-dE<{UgubMcph>>ad7MM2i8Q2fCMtR(SLE3|;*Wwq zivB41W3H!FVrg}g%KeJ+`?_lSnwY&@)0x?as6+kVujJpf zDtex%q@q^yFJh~#mu!-M$+~9QMv3HqXH^%h=btFYgLlR8a<5UpDnzaXP2C;En@-BWZfAMNEEttbT>8h z)-3J|G(QM~pB~hC1JrLH3&a9I;C-$rG|)V!Mb_n_sL7hJ&J`s+8bex46W6&gv9cD| zB+$k5sMe=Ndo=L|y1^Cu=1{gDi*U?JKfCT2)=L)FXsn5j4b^nTc~e~=Zgk10YSt1d z1*nHTi2gBp+0Iwc{f%oP8%D2fNDg`j96OxD@A2Lmha6O|Yh_ba(Y6J_VL=tMDev=tHiMjk_YL*K|zr zXH*TF;ZG$>-Kg5~me0U!cO({%bU07ck>5T68#0L7~U z?Cph}zxx&xT+qm&vrxE&9N`xFhXCm23-`IPtmtu&M%s~3U1eVS?{3mXeY z)s17_RBhwBv7)>D;*u*+b3(z!qU{Cyr!ZYfc)|8P=3$9ULkFQw4&Kz7=9bw`KD?jI zwUW7Z5+ZhRD$~qun6x|ujmnH)>mW3d6Dfz` z3``IT!xRO5Zz_C~P%qmwIjog?Px05fEL@H8kS@wxC+f&fY|rP}9K!U~fwCK4&}|>= zT^Vpr4{&4i*s8D)15Y9)lVJKL;Ga-k#I_dk7@dyOX`&lx#kRT`aiwdl(i8gnvY_`w zoO3rxr=eRzM>0A!*nDW~p>}$_AswV1Xbx-*v{PdZ@i0BKpB`_e$9EoX zryprZpC(V;{N?uLoy)tIKALVPXKu?gUJ}B>0uI9@{0AnG1n#6H*kRzyVUWSxZw#j= zAoyhThR>y+Y!2C!QLMoZO7&wcWKme=x&{sxXgLJMi!ZE1#%F(%m0fWO!}A4o-p2Kk z?F#fA>t^5>VU`T62Q(4p4O4gJl2xf;oe-8zx4)!Tt*TK{O<>J2u0WLphENw_b@Jy= z!ad0hbZh89?%#}W#Q!?oys&j)yS}4+u=w8MM?e4f%)e&Z4?J~8e)>=zXxN+1hSQeM z?8_4^d1B}4C-P(eBVi^u{xQ~QVOU;=va~9$N_JSDPAWz{?1+Z!5FN>h_ORm|90*S* zV}}=3j}a*bQyKuJreb(lvobylwmrm1#I$UpDC`x&v^u(d?4_#c@V%sA%3Ayj+R@9q3I81{ z>87bDSq`&rBZ~NEyuU~($i7Typ&*x_`)(b5E@Zw)O|H$}6C`o6lj6nmKz1p8_sZP2 z5lOtziNNHDrxNILO!qAx@fG)|#NfjGW|No}Y&QYlZIk+ql3ue7tLjEz>a7`-O5Ikk zQLIxF`ZejssDw4uRy`Xg(oa4&+{9BmP*pNR!cNF0`t}}tSfiKa6pV43PGn8V&(2<0A@$J#KM)$el z7B_s4=eXg<+~#W=uk9yBTZz&A#6&AG(Lt~b!QxLJQ3Ht64>$dcyU@6}IlD2td1d2D O2f>sXf5U^s?)N>r53kYy literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..3798af0239fd776157d04a7f8b8ff1f61f5db3b2 GIT binary patch literal 654 zcmZutL2DC16n?XtCh6LMSSu~Gpu|fk-9dX5p&{T!gD42@W!UTt>886oac5IlZ$d#m zd8~gy{3$)<6c}%M@>W?-J^5y`K?}~|z3=^Zzslo&a~wfVNOyiQVD4KH0d=4f~24+Pq8UA?$Fq8`tvlH3$7wn%!cii$i&Wn-TK%8T)hJX#umAP5>XL5OwdUYu zA3e)?>*jMi;Rd3s_M_!x9_O1#&6!mn!=+0IsqO>W`UX3fO>oxU@4eewknK6y{_PO5 g^`ZM||Kt9`+nsy63$Hu(x)q>Yx!~;lG6p;24<-GS`Tzg` literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DNAME.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7667d5056bbe9414a96519912696550265ad07a1 GIT binary patch literal 930 zcmZuvF>ljA6uz^a#I#N;A*fPHt0HyDkXXP96)gdVsulthx(AVoaM?|tuk?|k2T_DQig1Ek$J(frp206$bR zv3v%SV+r~I0}KODhiTO6kigcYV{$^q|8W|41hCcvSeIwaspbr}QVkdRmUS^#*+3t2 zgRE39bYR)|sWQjQxHM>4l9VLq1Cpw)YZ%sbY+$_y>qbW_=R~gNY-~R`u+p#^5syXK z@p4nIQWL6MlY7$jK^L@S{MTBUu4%q2_te$9#z1aW83Y|rhE7=%`Phw`Bo>93p;vxH zqz;3VU`H4`en5ojv9KKx&PAR@e$%JI@?6TO@4110K)6!3s_Zy-}8J&C%f}N-1%daQ}e*~SIcg8cUN7rQm%7<@CcdA1OlQjUZegliAd7$OqEx%nJ p!O{?x&NKtAzrTO9b+|RMR)*Hf$f^vjN&+OE=s;Wir5e&Zegm_n)z1I` literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..ac0a2525b88fa3f76505570ddb151aa9ec4489d8 GIT binary patch literal 741 zcmZuvzi-qq6tN4jRmX-sZ}F}!nNv2NKFSTmsrNK%h&{omShh>D`94 zI9+Rb!!ERnUDO5F%;CWJt+L@j6P*L+?cS~;LhyqFRqOhV!`m`94Q67p{DMfI01AwlXB@)Z=S z&rO1l)0krJ@z_i7K1-+6&p19v$7vS&9KT7ENxHMWxy5-nNn)C0*gmDnDUQN1p5@tb znmo6kKEpiB=;qAxPrM1`I7m2VL6*-X-x?m`YE*lcv#xAfhKG`HLxwZ(Q@_uwrarhW zENQpHIG$(TIHE$6saRLFdP`gK6Pk}bPMJDtCSM6)YAoQeW!C%X8r9lXbCjj320s4PCIz-n%hO0GzjF0^f{Uz)zVDArt hu=i>7<>>R#(%M*98%xVxSatzqxzGT5aIFMow|^5pt6cy9 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..dd8a69c6f38baf001b9c12382bd7bf60ef5cc93a GIT binary patch literal 651 zcmZutzi-n(6n=M3lejbzB7j;&m7rqDP@jMmAvLnZM1;tMmy_c=r8c%N^*NQ?tx_dm zVl4jx;!o+6sgl{~#8zacPP}s*K_%Ys-uHd)r`|=cw+U#EFKBpW0bCifwsr%mF9^;- zfFLn=q>j`hl0@r@bqB`(X%t?8pl2W~?CJhf+`y>5;kF)a-R6dU!wc)U3gC4LGHm@c zY#cON9Y{l}y9mxf)>nH}5YH;;&?>1rIkDZzqf7=W6XRj4>~fy^iR8T8+=jk)1!(8sq{ouVnlbVTCvrOd09Pz>U9djqnyvSO(uyzj- z=nZsd@T1kw%;#!-*e!e5>V!$M$o*+7OABdv&uGm!N9d`qWoiGd=`^Xb-Q?QtWP~-V zeYP21Z@7c#ru$;`n3q`}srj)Up}V#SA=MKg55B|BbsOl$huzcNOR~Kr+rKFx_doVO b4?Ybp?ZMJUzqhpaDnPlifbRS<1@(?UXyudF literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DSYNC.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/DSYNC.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..394794ce8e0509d6a679e7b5447d7c9d9a6088c5 GIT binary patch literal 4327 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 ZuXl`gTt7T|coES)EScT-mBq5w_b+S^u3rEE literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..d33acf9a63cade2935a4851d9bfc8e1aa16f4759 GIT binary patch literal 2986 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 SA3sE(!b-W=e^`S($^QU(5XWHv literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e68d544a851a85080f9d6faf6a963a660bc5be1b GIT binary patch literal 2540 zcma)7-A@!(6uE(o-=D1kT{nJ5TUjM2u}kJxRaKD7Jbbey^1!0rsacR-=r zh9)E~NsT43!NxSjCpEnE|L_I6HqfyTHa_)@x;_}6dhX2ZuvOCbCVTEZANTIPzwRyHob0J6GGcsna)z3k_4)?|=&shlW$>jx$ zw}c5kFP70bXNikSUb2#ww8-UUoJb-oWyy=&&OcZURswuUoUj@#rOuNoqibBg;Q~_A zzHn~v=zI!955cBOEI&0(@Hi&eF|nU4xTfK`Wbmj7=*B~Y6}xBkYY^XaE69soOHv8l zfY=gO;@9}l%%KvRtlu9+mxMJgT$YetdpCd5rl2{iTpEZXba5+`>B|0;WiLdCo z?bx2K`zhU^`i$*(M}`TD_FI_2o}L}j4a*{!Qo`r~TSbZs<9>ozX2GD;mx<-g!C7om z5aK6wJ#gtdWm|gKSa><5U2%(8qXsP+j`j(0C$Z^K?Yw*0^=y-BAGywid*g${vuv4DP8(o2aArQ`PA)&56{y4Cg8PudUK{ezXRV`cH%=B>`|&CY?1 z&Vl=LmCkp{;*F*%>_cV@I4Er00Ke)eh!vIsm|q=tpn$*=CAHpr2mL4jnfTmGu1Do} zuv0~nhB#k#i9KOEev06N;n}nHJofpitCaDG!AUYKD&e`hhp&0_4eX_nvKKtG8^i+I zYHeT9mbLZF{?&b}XYS?h=GJ=$*PDmdl_A3LFcTnZgQY5<=%=uN7YUOQ)oKB3u9eDH z3GmnB5e3D5g$GO+rAvvDvc|3r;{qZIOe*iECa@Q=&2M6B+k%AK<2EL0(ihAEC9EZo zW)QyMxl^Q#sqIWn?_i=sr3GY_9oNFPMOhs%e1Z{zlpH5+F@!12V2af)h@~2c1+=Z8 z_Rf_{%a4$Is-NsyY=5mMygi^5OcS#8zp_`|K=D? z=lOl_tBNmOB~X37Og%DVdcMfYFid~|h3Jc{UHD1cfrLU6bi$4yg_IV@?E>}{(=9@~ zgl@tT>NlE(<2tr!6oBEBVOp0t1=6cuwF!d~2(C2VOf98u4X=zXkKOiG zFW%Gd>Xkjm9x1~$*Wk+N^62KCzKuP7tJXc|t^@90mEnI$;{W2m^dJ0}WByAe`8EC% z#yvmHh);0QfEMgbs0s2WSnGH%PaA|{5>h{Iuap&SfKsguv;>r9K0K4>1en-1^q-FV|ZR zEqw4?6r|(T6ek_qmXVx!^6`adf+USr1yHtmwrb*~5!fMTp7DY-7C4@>Rq*cMcr{6S z;c!0YT{1~;s^?B)f_cz^}T*n62@tl8~8!PwTRF~A7+LBg9 Ppu*kx&cB!=_=mp%_4gc` literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..227c566e1aabfa2a9ce8a30678d7ea5f3e0dbec0 GIT binary patch literal 2998 zcmb7GOKcm*8J>NSON!LPqU1*`L$VrKn>bUu!Y(Rh=ew945W~QpojtlNCI7QR8Q^EKrUkFfX+sT6zRo44{qo~;8Xs8A0*=_ z2r`F%=6}rW%>VfQ{WzOV6Br{O^VV|-LcT;M9>52q`6f&o#3UwXlR`Li1wP<~Xch}R z<%_{t(XMFPOU9F55tCmfrT}{~-?m4t*!^AEmJrIeB2_`IaEf>rxx5j{f+?~i&EEt2 z8r++b0%sDqCszxp$HAM5_idA+f;xG!F{=i)$n9j~gQDVSm$Gq_z>tf`QW(+**L(=~Bg`t}ocM?5BUL+e`G9 zj5*8~%(ClC%+x*IPimLxHEXWIFxGyOnO1FjL<`k*nx#|VylH*y1 ztG?)z=bdxUJaN`_t@(0^mOXXi3N2qzZR?V{Sn)19ba<}a#{;S-A$hRL&>K_>PUriA+!AYZSLT{*j+tjPy%?%y1{Eh~2p9eGE6Oa0)n z`k|>c@ozo5T?21e?+x7UI=0)h?~R}R^^co;XGw1IN zjiFH!u0f1eDFB0OOupvRKsK5s^$rI&f| z34%>ES(W2rnjs%@d4>1W^VEx~!0*PjWkU8?a~5Sv$`=gVWmvB45g@+cISXtA*`vs2 z+aA#X0kMdJBz6&=&^5sb{gawwPAT+I7CqErhr!lWfvk{wG8sI$@$!wA>jRIjrSIhS zH*#aO+*my~zLxq7vgzc_^PA6ql&bff+L2F%Uh9NRL<00f^M63VYusL5^ID!NVgMf+ zL5`sJ9~v>V#()V>wD;+FhhXg06!&a|EjW&MBGeaKaNL$cV}sTf$FF^p3Q@%TGC%Sx zL$@=m1c;n;l|ilh3D;vwhUbe|^kIbh(P|LMek2IcLDUo}%y)6lvZ*f{P6@i4>jv@m zGlpJv%9a5jyG&j5dPi(!h`x5!Vl+aHh#Lnh_gNs2{c`4ddOiK-^v3BMr*C?jv+rx~ zYW4o9+wyd*I)SQ2|7fj$w9$XE)_-za{8#rsyKix~j^8rs{TDu!XTBTqA6F>S4-`tG zRVY$55j%Q@urYW8Aj2`3{4CZ8qa_`>NO@jKG6kd^C6WkmpM_P@t_ZB|z+*99^acfh zPSe62%ehurbVwhEeNlCMG4M!ZV5~MURv#G0>NwQMjn{JH_1t6MR~>+9vx<&7w z51*024p`PpH0Xd@$uXB~TxT@xl_lMdG)YY}9RtMBj$GUETulqw0{aR2peyK5B?@iD zWOze1i3H)-&qn>rpth}P3datE)J6C@6956jstXAIx416?`FfT7N6bO-?>lny)t%m- zt~_^NOi9O@gPb&VFF_K0U;Jj~D?yS@Hw9qs@wn>drCAQPB>KMMg-CNBS3!;h=PN0O zu;OQ-%}rRP(vqiNvMIy#_fsefyLfN740`uO(7ID?;~W}G7qHz2XMRY+^AXb@$UhIg z_X~C&SbU;;2FN{u$+~=hCbFv>^!Aq|V{b{Jd9jI{!?(>gwr`JZWE9=U2 SbzN-|VBu~2!T+No_`)}bN_d$7 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LOC.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eefd04283d25286ecdb5bf14baa0d56319913d3d GIT binary patch literal 14147 zcmdTrTXY-8b-Q>j-T(oLAjJpx7AQU>k(MP{mPJdl%}AoGr)}0Y0&$lVC=#F-pd>LNp4T7pZ4Ba z>|#klvh!$8JBOH^xpQac&di-V_ud)))M7DG5So9g3jBR9Mg0;pQqY$Y4=I|W7ATft z=}Brt{L&*Tl2@0$nh}+X;fVkc{d*u|+Py2Q4WaV40FdYNJ! z*C@6M>U1ly#q

G@6M7S6MD2@AN1H68b;ZJ?}QL) zr!0F8kXu{Lk^c%kaM(k zta4na>yH;9Pn~$bFM`us3H|?6eFt7fAskLS@w(3*%?OUxa0i(9y zZ{q_bPs8(6gr0ItiN@?z3M(1v4kYtL-bV*ph`x5yl~-CS|7IQe&m?nwNj5e{CH$55 z++FAK+#!&fX~^^!{2~wJ^CWrD{>seG=ua8P!$EOaj^gJShyc?m3*lOYdkMcYg)iuB zdMqll9yEb$h|g(Vh_!W`pQQmE0=1z@p*7R0==Tm3(mD`0f3C0p@|j+ECYYOw_F8$1 z5*GVUqtUMGTmF~0+q~s;q**8^PSBQS+ZeGfE&!6tNScsU?Rxqa$wt3N_`N3MAT& zgQ1}z{DcPT0I{LqBK&#>){GLCfa&O2)KXw6z@-w4L<*umu1<-?F>ikHDg1~B4?|<% z5%w#S>~lI6bJIyc${QrMV%A3Ox$S574N2^=;GL8U;c6aQ75Sw||Da%_w(^ zC*>T8w~x`2iMKCnxgCwS_uSfZ+keZS@*TU=yIS8p>%QmpuX^{-x<0F@z8RT|EM0tm z+s_-`ZAdp9NmU&A#C`-?t$pu1V{eV6n&G=QwK&#YJ3eupeB1Pn?Je6o6>n86zq%@( zMA-ckj+A}p7go;I@|nlGxMyL{Qsn)^KR@>Fv2@dsl;_ANt|Rd9yK8Qa&W$eZdwb%Y z#9N7U{lQel!B6Z5?>p;L&fWL$oNvLm6iYSwQy%}S%TFUw&E@&dRoBknGvoI4)5lMr z;ERHKNQ0X3w;>{V6!^v)}D-%r`=b+k?g&_zH!x9ER{777s~6h!!_^kO2~p`mn3 zDZkis!71ewH^sfn_kqOd^iH9eHUrw^PvEM^O_K{xq#w3!5(U@r*k)0%C5&c@mW(74 zPF@=RB4>cV2V((k@?W&y%0Jjw3|W_2gE%Bw@tpl33(X zbXm%01CIyUjr9Dc>nA43^fQ4`+(W+OA@2k6Tt*)XyC&Ns5MW0nn{7L4rem2vU~9Cq zUjGVxI++mobWn(Y!N2D5r9Hk>)t=>D%e`y+j;Hq>PwhQ<=X)u4cgo(aQ^mi<1K5}* zWPQFi<(D)QM}WeTCa@g=%@qiIZz34cXNK_YfD{X%TDTgEMHIqQ3b8l~!&aWBhs|(6 zD1SnA#BvH*)Cw)34tFIg#VAoJd4-Z;B$^%C4Z-K}DTUTdUxF)C`Y|Qs++!XOg-|Rr za})x;3c0})QW6Rs#p)p&L@3G~BwBekY&;}KB7p!T9W68qa6P4i=N{@x2~A!7FId5^ z-{8J5IW6`t>Z>j1_+(9nqkN^~&Y@rU_43EPzhiY9dj!j#jLT-JOLidn^4YSd(QM=7@ zGUK;fYLhrBMz)N>kgdGBL0P7z8ZD?owu~xd^g)h{%Vntt^v{?nZ`r$iW~F6CUFmqY zKivW@LFN6j^4ZY*W3wZ3O&b>6XBw=QRxH8cmFktyO5M9B)6GwAaHz|8nk-`Sag5M< zN4PWatFo1@^uC^S%jpdc4VhEC$LQ?L8YDDl!db%XetX$X5DU_OY-*cNqsyi`()QN4ZYew5z^-%6~+r{D_i8N*JCw zijroHPtmE6PMeJ87`-xrB1=;zZX#c;j(*{u`$e>0emNww7$8-T6LM0Y>f%0dEJRfO zH;wfrTJU@MG5n}e|Bd2~QXMG5fu>hcVghV%|5zCR!vPliK=zl;C;y2`j3LRlqx@@1 z@^GyfpDE0}L?^h)Q5<<-*IQxxU*^Q>{o)BH~r4e@g2YA{J-T6{2h1rzNcx; z<4=41H{8FrRm@(WS66L2ZU}$BbN7O8`N9qNdQDr3s{-08D_<{juRE(LwU@8EwteZe zmYHr?G8G)KQ^P{T(!}k=t;8}zVk;BBochJo2W@}T`C(_Os{cmCdKRh4w=wRqsU^c9OSWuKmZ`MyO-qkuIAqB~%jUP90E_cYnVL%8 jnyg7m%e^br@19S_(z}jqaH#ug_lUsv@DDhavGw|Y7_s0H literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/exceptions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..870d5e0ea21b07f5cc5c8a3c5b62b8cb567f02d5 GIT binary patch literal 33305 zcmbV#3ve9AdFIT%cd>ZC0RkKVB!MM?Bt;Ma-vntAlqE;IhNq z)!p}Z&&F3}7yL?RR*%hrCs~)Hxs~M;n zs~xCiY3^vR_o=NUc^n%+w%DtwCz7C3Q1PtwU

IS5? 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 L-xa^(Z^rT$k(;~n literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..104375cf7744260b6a81617b01344b4c5d1eb867 GIT binary patch literal 4992 zcmbUlTWl29_0D6Tvk!aMZ)_N_!QKG75Q9jNF$y&p2p9@ct5}UzD)UJ|9q^(*asKh59{b(z-iQ1oaOvQHMDpXC?_HV&RX~|E|y|XiGyGfl+8`aP>ww;}XbvT%n`ds+VtyeuIZ$&7|Z8Tw>LS$MJy6JK-88DmqnA1l^0)^iMzyEb@ua`0O}3)2#^yt5**YDPiGHP0-A z6AZF^HC%AuTd0VJ(4E@Gof`IKlwwCVu^}k^@LL~%>1D(Kt0H=>3850RfhF_bEPu!> zF${XUg-3Jf7u*b!Vn)D(VHI=*3+Xw<=1dF27Mng`^QxufO(M1^J~J|tMqbI7l9`wE zOwDL!=8`dHSZdD9oHz6dW9Y>Gm%wflI`Kj#eO1w~W;AsyQ<$?R4ZS~o@cE1hME4h@ z+!bj;F*60VkjbjLrC{(XfE65PqziM;LIV?uuFMwj;QX$vYGyTvfUGf|1^Lngnvs(< zb1?0}G=h|7D@^}D|5`ww^j-MXp}QUThJM-oFg3W+cj4bA@yxZ3PA0`+0&DYxp3Om2 zq!=6}*u>f*ZYGEEEetV<)dm8z1EvMECN$s7y?1C;5I+$v{PZ#~__+T;--XrP7s~vF zbpSzj`?OB9ROGB_OyiuAl_5BDmVxIsdD07*6MzH(LvZPs^1{X4L1@YgmHJ*0!p03K z8YuIzx*181K>V|YY4ZiinzZ>TL)AfHC?=Z}PUfX6O4dWn5@0L>5X2NRiS|V4=sQPl z9C>Hp#z494V7dQWtHMy3AHp4QYA0y^T%I!_WQG(=QZ+Nh+d=FkrD+Goh%t28_T?s( z+!Z@gTT@PUc-jQ+O5yOqBX(W0XR&z%HUTDgF4D&a#Amb)l ziT?)nXFY6RkvAKPJ~L(!ndm5T?}C5c_h7CuMIOIwg}ie{X76&Y{|=*)-*W3Znu@qk z^=HvP6|b+{x2*-E6p(rH=h9`+n}I13^;?GQ^JEX|tjw2!AYBt^kSL-bM2pB2>YB)Y z7xo$>2TDlC#USpJgT>%ggnIyf2f%l2fDcV2x3Gg{F(?N~G`Y0gXN~6RB3d|Ao>p* z4WZX!fEf-UYo`b0=?S&Gfd{L8rFVlahf?HgAett;%cvBcK&4nQvfz`Wr3lfo6ti}F z97DxOF;#q8X+eS-V`k9)w#;!Y2Ka#qhiA z`)pmyqHvAGp74EPhM7SQja0LA0@C@MA<3dOsfb=>N|Tx(j;orI4u?e;7bOf80WJ{p zC|#4idNmREi>D=%l7LMtl6s^-jgrNSCohUnZbZ0oatgickBcLQu6RTUA#darN#8(b zQZn^kON84=Q*)}N&533~$*JS2BBwn9p3Jn4fh6bP(jpy*XpB2bIhS3p`Bmz9WZd%i zKM7DExHYL|JXFw@)=NNr1&l~ar^~xmn8`gY&M3VYOzas#>>D?*N5r#%C@Q3N0vi?# zY(aCBay)Q}wm}=rPs}K)<3Lw5Cyi-MCJvz*Ys!R_n-g<}4E*4V*(Vy{T|A@0xg^q# z&*R(Yy%7mE$ZLw@*vN0O*}hHCuBNV&YnRPtpiLy}veNrWl)xgiC(<+s7HJ#;Oavnn zwKs7hvP~DLa$}@}6rtr&u|XT4O@Q9@PZft)GDI~`4C&xHx1*z3Urk(SldQ?qW}zKh zkWTh0nAF%9MjqOVV8POhW14%t;Pf`n(*+3YSDr;3$~DW(kNw}I-2^p(tmCN~Es_y$lt7j( z5oL!dcgrv|)Ar3wDvCCrsBaA(J9HrZLi)hGBP0b&Q^Hv&N7B)3)(Q4(HpOC65Ai-S zkvFhepw6`C8&Eu(vlK!pv48FnQ6ex06h89kW53L?*ZVPp+k2C#Yoa!`)y2) zqarxL2_`3el9*gMcsr$|nxt}K@=$+AOE zj6x4@^V629*`aY&hn5zq9VYDwZHzH#Sup8oaF9&oo=U~>9`fExram&IV6vl7IqPkv z9U@C=Q=1tFqZFK$&k3Wf6Gqfzwy+^vZ67T}xSNnAR}}7n$s~;tNskS~>E@F}y*gLQ z1HS~_e3XtWp5(*vlvb!m8UcxhyD-DKBV%m?v6dhdWxOljVxKQ|U7zLZxqK!Axw~suC_QPpt-@Vt@TDC1+ zS-f&<=g;?iu;(Xzt1Wx44}ZbI(WBPwOK&W`0sLB@zY%%Vy6t-8aRX5J(W@1qxh(Ab z@VT`_ca>!t`+-F=1}vhnY7n(_+z9_E)V!Rz{k@fr7dL^jjXi(wdG3Q#w+HUWSN5J> z?K!g^;0Y!Vm|==pZA0Ova%kJ5Nc$7SMh~wgJC_C*2OlQ)t|a%~KDU~Du^fN#2?r;i zZ#o%XO&%)84^;ywl&r>3qy+>l^E+XF9E4RRo_rKYmIK?WO{gPXZcA6Xjz9KsZQ<*Y zY63;t%0hRU?}oV=LBYgw=SpDrT5RvV(+^_D%E4n*KWgY)KD!d{T{yiabS%H{KgVgdGM*vwJ+ediBC|#*M427vi?x}TBLPZ_+zA}>VrMt6W{hQwtXeGefiDRSY~0k zlHBny*}anNzBO|1#A@=$!nsOo`@+deAhz%(*dclNdZ5y{yWFtny6p$!#jN+35~;@zK=S6@SbY3V_uS_Nsi$o zfJ(F07IFsMKRMDgn3#iY+i%Skpf+IAhhg$Li2Y<+fDh?(7QVM-13NjrYr{_3sWyO@ z-jGJ7ldU9g?KTSSeu$@)*_=ZE)nd|G;UqZ8cbgxBdgx?*f^ZkfL=v7R)Q5-%!_QXygl?XV}LGrfQ5~ zE;7qW$RBv#YFekyYLH=0GWSSU!SjAU**(cX@U@5)zU`Bav+Eq&!j@5@8vYK;oMI{s nZOcuIu`1`R4)nWk$4|I#Bj2vQJ@Q4%F>%GW^Qn(QQ$79z@tp~W literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5473a426342714dcec29a3a86b5b2df19926897 GIT binary patch literal 10573 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0db8d974b4c93cf1781871a6d080173e8afe2f4d GIT binary patch literal 13670 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 Union[Tuple[str], Tuple[str, str]]: + return (a, b) if a != b else (a,) + + +class _Prefix: + def __init__(self, path: str) -> None: + self.path = path + self.setup = False + scheme = get_scheme("", prefix=path) + self.bin_dir = scheme.scripts + self.lib_dirs = _dedup(scheme.purelib, scheme.platlib) + + +def get_runnable_pip() -> str: + """Get a file to pass to a Python executable, to run the currently-running pip. + + This is used to run a pip subprocess, for installing requirements into the build + environment. + """ + source = pathlib.Path(pip_location).resolve().parent + + if not source.is_dir(): + # This would happen if someone is using pip from inside a zip file. In that + # case, we can use that directly. + return str(source) + + return os.fsdecode(source / "__pip-runner__.py") + + +def _get_system_sitepackages() -> Set[str]: + """Get system site packages + + Usually from site.getsitepackages, + but fallback on `get_purelib()/get_platlib()` if unavailable + (e.g. in a virtualenv created by virtualenv<20) + + Returns normalized set of strings. + """ + if hasattr(site, "getsitepackages"): + system_sites = site.getsitepackages() + else: + # virtualenv < 20 overwrites site.py without getsitepackages + # fallback on get_purelib/get_platlib. + # this is known to miss things, but shouldn't in the cases + # where getsitepackages() has been removed (inside a virtualenv) + system_sites = [get_purelib(), get_platlib()] + return {os.path.normcase(path) for path in system_sites} + + +class BuildEnvironment: + """Creates and manages an isolated environment to install build deps""" + + def __init__(self) -> None: + temp_dir = TempDirectory(kind=tempdir_kinds.BUILD_ENV, globally_managed=True) + + self._prefixes = OrderedDict( + (name, _Prefix(os.path.join(temp_dir.path, name))) + for name in ("normal", "overlay") + ) + + self._bin_dirs: List[str] = [] + self._lib_dirs: List[str] = [] + for prefix in reversed(list(self._prefixes.values())): + self._bin_dirs.append(prefix.bin_dir) + self._lib_dirs.extend(prefix.lib_dirs) + + # Customize site to: + # - ensure .pth files are honored + # - prevent access to system site packages + system_sites = _get_system_sitepackages() + + self._site_dir = os.path.join(temp_dir.path, "site") + if not os.path.exists(self._site_dir): + os.mkdir(self._site_dir) + with open( + os.path.join(self._site_dir, "sitecustomize.py"), "w", encoding="utf-8" + ) as fp: + fp.write( + textwrap.dedent( + """ + import os, site, sys + + # First, drop system-sites related paths. + original_sys_path = sys.path[:] + known_paths = set() + for path in {system_sites!r}: + site.addsitedir(path, known_paths=known_paths) + system_paths = set( + os.path.normcase(path) + for path in sys.path[len(original_sys_path):] + ) + original_sys_path = [ + path for path in original_sys_path + if os.path.normcase(path) not in system_paths + ] + sys.path = original_sys_path + + # Second, add lib directories. + # ensuring .pth file are processed. + for path in {lib_dirs!r}: + assert not path in sys.path + site.addsitedir(path) + """ + ).format(system_sites=system_sites, lib_dirs=self._lib_dirs) + ) + + def __enter__(self) -> None: + self._save_env = { + name: os.environ.get(name, None) + for name in ("PATH", "PYTHONNOUSERSITE", "PYTHONPATH") + } + + path = self._bin_dirs[:] + old_path = self._save_env["PATH"] + if old_path: + path.extend(old_path.split(os.pathsep)) + + pythonpath = [self._site_dir] + + os.environ.update( + { + "PATH": os.pathsep.join(path), + "PYTHONNOUSERSITE": "1", + "PYTHONPATH": os.pathsep.join(pythonpath), + } + ) + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + for varname, old_value in self._save_env.items(): + if old_value is None: + os.environ.pop(varname, None) + else: + os.environ[varname] = old_value + + def check_requirements( + self, reqs: Iterable[str] + ) -> Tuple[Set[Tuple[str, str]], Set[str]]: + """Return 2 sets: + - conflicting requirements: set of (installed, wanted) reqs tuples + - missing requirements: set of reqs + """ + missing = set() + conflicting = set() + if reqs: + env = ( + get_environment(self._lib_dirs) + if hasattr(self, "_lib_dirs") + else get_default_environment() + ) + for req_str in reqs: + req = Requirement(req_str) + # We're explicitly evaluating with an empty extra value, since build + # environments are not provided any mechanism to select specific extras. + if req.marker is not None and not req.marker.evaluate({"extra": ""}): + continue + dist = env.get_distribution(req.name) + if not dist: + missing.add(req_str) + continue + if isinstance(dist.version, Version): + installed_req_str = f"{req.name}=={dist.version}" + else: + installed_req_str = f"{req.name}==={dist.version}" + if not req.specifier.contains(dist.version, prereleases=True): + conflicting.add((installed_req_str, req_str)) + # FIXME: Consider direct URL? + return conflicting, missing + + def install_requirements( + self, + finder: "PackageFinder", + requirements: Iterable[str], + prefix_as_string: str, + *, + kind: str, + ) -> None: + prefix = self._prefixes[prefix_as_string] + assert not prefix.setup + prefix.setup = True + if not requirements: + return + self._install_requirements( + get_runnable_pip(), + finder, + requirements, + prefix, + kind=kind, + ) + + @staticmethod + def _install_requirements( + pip_runnable: str, + finder: "PackageFinder", + requirements: Iterable[str], + prefix: _Prefix, + *, + kind: str, + ) -> None: + args: List[str] = [ + sys.executable, + pip_runnable, + "install", + "--ignore-installed", + "--no-user", + "--prefix", + prefix.path, + "--no-warn-script-location", + ] + if logger.getEffectiveLevel() <= logging.DEBUG: + args.append("-v") + for format_control in ("no_binary", "only_binary"): + formats = getattr(finder.format_control, format_control) + args.extend( + ( + "--" + format_control.replace("_", "-"), + ",".join(sorted(formats or {":none:"})), + ) + ) + + index_urls = finder.index_urls + if index_urls: + args.extend(["-i", index_urls[0]]) + for extra_index in index_urls[1:]: + args.extend(["--extra-index-url", extra_index]) + else: + args.append("--no-index") + for link in finder.find_links: + args.extend(["--find-links", link]) + + for host in finder.trusted_hosts: + args.extend(["--trusted-host", host]) + if finder.allow_all_prereleases: + args.append("--pre") + if finder.prefer_binary: + args.append("--prefer-binary") + args.append("--") + args.extend(requirements) + extra_environ = {"_PIP_STANDALONE_CERT": where()} + with open_spinner(f"Installing {kind}") as spinner: + call_subprocess( + args, + command_desc=f"pip subprocess to install {kind}", + spinner=spinner, + extra_environ=extra_environ, + ) + + +class NoOpBuildEnvironment(BuildEnvironment): + """A no-op drop-in replacement for BuildEnvironment""" + + def __init__(self) -> None: + pass + + def __enter__(self) -> None: + pass + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + pass + + def cleanup(self) -> None: + pass + + def install_requirements( + self, + finder: "PackageFinder", + requirements: Iterable[str], + prefix_as_string: str, + *, + kind: str, + ) -> None: + raise NotImplementedError() diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cache.py b/.venv/lib/python3.12/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000..f45ac23 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cache.py @@ -0,0 +1,290 @@ +"""Cache Management +""" + +import hashlib +import json +import logging +import os +from pathlib import Path +from typing import Any, Dict, List, Optional + +from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.models.direct_url import DirectUrl +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.urls import path_to_url + +logger = logging.getLogger(__name__) + +ORIGIN_JSON_NAME = "origin.json" + + +def _hash_dict(d: Dict[str, str]) -> str: + """Return a stable sha224 of a dictionary.""" + s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + return hashlib.sha224(s.encode("ascii")).hexdigest() + + +class Cache: + """An abstract class - provides cache directories for data from links + + :param cache_dir: The root of the cache. + """ + + def __init__(self, cache_dir: str) -> None: + super().__init__() + assert not cache_dir or os.path.isabs(cache_dir) + self.cache_dir = cache_dir or None + + def _get_cache_path_parts(self, link: Link) -> List[str]: + """Get parts of part that must be os.path.joined with cache_dir""" + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = {"url": link.url_without_fragment} + if link.hash_name is not None and link.hash is not None: + key_parts[link.hash_name] = link.hash + if link.subdirectory_fragment: + key_parts["subdirectory"] = link.subdirectory_fragment + + # Include interpreter name, major and minor version in cache key + # to cope with ill-behaved sdists that build a different wheel + # depending on the python version their setup.py is being run on, + # and don't encode the difference in compatibility tags. + # https://github.com/pypa/pip/issues/7296 + key_parts["interpreter_name"] = interpreter_name() + key_parts["interpreter_version"] = interpreter_version() + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = _hash_dict(key_parts) + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_candidates(self, link: Link, canonical_package_name: str) -> List[Any]: + can_not_cache = not self.cache_dir or not canonical_package_name or not link + if can_not_cache: + return [] + + path = self.get_path_for_link(link) + if os.path.isdir(path): + return [(candidate, path) for candidate in os.listdir(path)] + return [] + + def get_path_for_link(self, link: Link) -> str: + """Return a directory to store cached items in for link.""" + raise NotImplementedError() + + def get( + self, + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Link: + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + +class SimpleWheelCache(Cache): + """A cache of wheels for future installs.""" + + def __init__(self, cache_dir: str) -> None: + super().__init__(cache_dir) + + def get_path_for_link(self, link: Link) -> str: + """Return a directory to store cached wheels for link + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were + not unique. E.g. ./package might have dozens of installs done for it + and build a version of 0.0...and if we built and cached a wheel, we'd + end up using the same wheel even if the source has been edited. + + :param link: The link of the sdist for which this will cache wheels. + """ + parts = self._get_cache_path_parts(link) + assert self.cache_dir + # Store wheels within the root cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get( + self, + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Link: + candidates = [] + + if not package_name: + return link + + canonical_package_name = canonicalize_name(package_name) + for wheel_name, wheel_dir in self._get_candidates(link, canonical_package_name): + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if canonicalize_name(wheel.name) != canonical_package_name: + logger.debug( + "Ignoring cached wheel %s for %s as it " + "does not match the expected distribution name %s.", + wheel_name, + link, + package_name, + ) + continue + if not wheel.supported(supported_tags): + # Built for a different python/arch/etc + continue + candidates.append( + ( + wheel.support_index_min(supported_tags), + wheel_name, + wheel_dir, + ) + ) + + if not candidates: + return link + + _, wheel_name, wheel_dir = min(candidates) + return Link(path_to_url(os.path.join(wheel_dir, wheel_name))) + + +class EphemWheelCache(SimpleWheelCache): + """A SimpleWheelCache that creates it's own temporary cache directory""" + + def __init__(self) -> None: + self._temp_dir = TempDirectory( + kind=tempdir_kinds.EPHEM_WHEEL_CACHE, + globally_managed=True, + ) + + super().__init__(self._temp_dir.path) + + +class CacheEntry: + def __init__( + self, + link: Link, + persistent: bool, + ): + self.link = link + self.persistent = persistent + self.origin: Optional[DirectUrl] = None + origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME + if origin_direct_url_path.exists(): + try: + self.origin = DirectUrl.from_json( + origin_direct_url_path.read_text(encoding="utf-8") + ) + except Exception as e: + logger.warning( + "Ignoring invalid cache entry origin file %s for %s (%s)", + origin_direct_url_path, + link.filename, + e, + ) + + +class WheelCache(Cache): + """Wraps EphemWheelCache and SimpleWheelCache into a single Cache + + This Cache allows for gracefully degradation, using the ephem wheel cache + when a certain link is not found in the simple wheel cache first. + """ + + def __init__(self, cache_dir: str) -> None: + super().__init__(cache_dir) + self._wheel_cache = SimpleWheelCache(cache_dir) + self._ephem_cache = EphemWheelCache() + + def get_path_for_link(self, link: Link) -> str: + return self._wheel_cache.get_path_for_link(link) + + def get_ephem_path_for_link(self, link: Link) -> str: + return self._ephem_cache.get_path_for_link(link) + + def get( + self, + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Link: + cache_entry = self.get_cache_entry(link, package_name, supported_tags) + if cache_entry is None: + return link + return cache_entry.link + + def get_cache_entry( + self, + link: Link, + package_name: Optional[str], + supported_tags: List[Tag], + ) -> Optional[CacheEntry]: + """Returns a CacheEntry with a link to a cached item if it exists or + None. The cache entry indicates if the item was found in the persistent + or ephemeral cache. + """ + retval = self._wheel_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=True) + + retval = self._ephem_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=False) + + return None + + @staticmethod + def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None: + origin_path = Path(cache_dir) / ORIGIN_JSON_NAME + if origin_path.exists(): + try: + origin = DirectUrl.from_json(origin_path.read_text(encoding="utf-8")) + except Exception as e: + logger.warning( + "Could not read origin file %s in cache entry (%s). " + "Will attempt to overwrite it.", + origin_path, + e, + ) + else: + # TODO: use DirectUrl.equivalent when + # https://github.com/pypa/pip/pull/10564 is merged. + if origin.url != download_info.url: + logger.warning( + "Origin URL %s in cache entry %s does not match download URL " + "%s. This is likely a pip bug or a cache corruption issue. " + "Will overwrite it with the new value.", + origin.url, + cache_dir, + download_info.url, + ) + origin_path.write_text(download_info.to_json(), encoding="utf-8") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000..e589bb9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__init__.py @@ -0,0 +1,4 @@ +"""Subpackage containing all of pip's command line interface related code +""" + +# This file intentionally does not import submodules 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 new file mode 100644 index 0000000000000000000000000000000000000000..453b0e30df99ca04e3eec8e3cf9f2acb9607b61a GIT binary patch literal 299 zcmXw#!Ab)`42E~sqNUJxn49#_2K6d}B6w0nZ(fGknRFYS%`7w1!g}@{dF2Vg_Ausp(`dHR5u+qd80Olb4Cr)1o7)ygHxu!Bpp^ASskdU&Z`%e~ z_y!^Bn8)EZ_(wD;!kw>tA`{`-bL}rLrspwI%?)yb*$R0Djw)!owD5c~J359)38y>A v2$>1g9|da%#u2D&DJeB;yY29_@pkF(EbM25{_5C2WxXea_$h@Lgh95yD=b=^ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..45412fde681abed26366456b03c29cc0782b89d7 GIT binary patch literal 8486 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 Czo2CR literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..57f4412f232a92adf16a9f74540898943ed7b4fe GIT binary patch literal 10476 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..3ab1514a8862bd9df9c6f66b17783606ace5df1b GIT binary patch literal 2319 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..4f22be3c8f8cc31ebdd2e2eef8c914a8761f423c GIT binary patch literal 15043 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 g|L7Qx`Tzg` literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..aafe7ee4ae2397b99c91168639c4fb9dc2fbc93f GIT binary patch literal 2641 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 F{a@@MR@DFi literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d65888bf12b93a049af25e7178f8b0f674625f3c GIT binary patch literal 7861 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 aO9Yy?N0){U7Y2@ejt^}110Gr|koiB<`q$|I literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db981648e35673a7b33c2e6ce15c86b4d4d0f666 GIT binary patch literal 396 zcmXw#v1-Cl6ozjSqt+6t)S^)5f`eY#uBD(RrJ(R9TWaht^qu4c4@djLA0+%rvmo-_J zGdVYB=3L(7SNUdPn?<+Tz?RLxWyocyrBIsrq@WwUo}}vuFKL>pC4!+mDeALg9c*f5ltA=iM4Y@B`qO2YBWkt0Hol);m(hu2puuC_A{SwZw zk07y$ZwGJ~;TPmk0yR9r)N=?N;9!cyZmUCxHw}CgP}p9gU@Mc$KB}Z=WS<~cDSkPI lVdB#1x)2vn)_h#9tcAE%-+*d-+1P-3e17o-%F$UG{R5#DaMl0- literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py new file mode 100644 index 0000000..e5950b9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/autocompletion.py @@ -0,0 +1,172 @@ +"""Logic that powers autocompletion installed by ``pip completion``. +""" + +import optparse +import os +import sys +from itertools import chain +from typing import Any, Iterable, List, Optional + +from pip._internal.cli.main_parser import create_main_parser +from pip._internal.commands import commands_dict, create_command +from pip._internal.metadata import get_default_environment + + +def autocomplete() -> None: + """Entry Point for completion of main and subcommand options.""" + # Don't complete if user hasn't sourced bash_completion file. + if "PIP_AUTO_COMPLETE" not in os.environ: + return + cwords = os.environ["COMP_WORDS"].split()[1:] + cword = int(os.environ["COMP_CWORD"]) + try: + current = cwords[cword - 1] + except IndexError: + current = "" + + parser = create_main_parser() + subcommands = list(commands_dict) + options = [] + + # subcommand + subcommand_name: Optional[str] = None + for word in cwords: + if word in subcommands: + subcommand_name = word + break + # subcommand options + if subcommand_name is not None: + # special case: 'help' subcommand has no options + if subcommand_name == "help": + sys.exit(1) + # special case: list locally installed dists for show and uninstall + should_list_installed = not current.startswith("-") and subcommand_name in [ + "show", + "uninstall", + ] + if should_list_installed: + env = get_default_environment() + lc = current.lower() + installed = [ + dist.canonical_name + for dist in env.iter_installed_distributions(local_only=True) + if dist.canonical_name.startswith(lc) + and dist.canonical_name not in cwords[1:] + ] + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + should_list_installables = ( + not current.startswith("-") and subcommand_name == "install" + ) + if should_list_installables: + for path in auto_complete_paths(current, "path"): + print(path) + sys.exit(1) + + subcommand = create_command(subcommand_name) + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + options += [ + (opt_str, opt.nargs) for opt_str in opt._long_opts + opt._short_opts + ] + + # filter out previously specified options from available options + prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + # get completion type given cwords and available subcommand options + completion_type = get_path_completion_type( + cwords, + cword, + subcommand.parser.option_list_all, + ) + # get completion files and directories if ``completion_type`` is + # ````, ``

`` or ```` + if completion_type: + paths = auto_complete_paths(current, completion_type) + options = [(path, 0) for path in paths] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1] and option[0][:2] == "--": + opt_label += "=" + print(opt_label) + else: + # show main parser options only when necessary + + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + flattened_opts = chain.from_iterable(opts) + if current.startswith("-"): + for opt in flattened_opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts + else: + # get completion type given cwords and all available options + completion_type = get_path_completion_type(cwords, cword, flattened_opts) + if completion_type: + subcommands = list(auto_complete_paths(current, completion_type)) + + print(" ".join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def get_path_completion_type( + cwords: List[str], cword: int, opts: Iterable[Any] +) -> Optional[str]: + """Get the type of path completion (``file``, ``dir``, ``path`` or None) + + :param cwords: same as the environmental variable ``COMP_WORDS`` + :param cword: same as the environmental variable ``COMP_CWORD`` + :param opts: The available options to check + :return: path completion type (``file``, ``dir``, ``path`` or None) + """ + if cword < 2 or not cwords[cword - 2].startswith("-"): + return None + for opt in opts: + if opt.help == optparse.SUPPRESS_HELP: + continue + for o in str(opt).split("/"): + if cwords[cword - 2].split("=")[0] == o: + if not opt.metavar or any( + x in ("path", "file", "dir") for x in opt.metavar.split("/") + ): + return opt.metavar + return None + + +def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]: + """If ``completion_type`` is ``file`` or ``path``, list all regular files + and directories starting with ``current``; otherwise only list directories + starting with ``current``. + + :param current: The word to be completed + :param completion_type: path completion type(``file``, ``path`` or ``dir``) + :return: A generator of regular files and/or directories + """ + directory, filename = os.path.split(current) + current_path = os.path.abspath(directory) + # Don't complete paths if they can't be accessed + if not os.access(current_path, os.R_OK): + return + filename = os.path.normcase(filename) + # list all files that start with ``filename`` + file_list = ( + x for x in os.listdir(current_path) if os.path.normcase(x).startswith(filename) + ) + for f in file_list: + opt = os.path.join(current_path, f) + comp_file = os.path.normcase(os.path.join(directory, f)) + # complete regular files when there is not ```` after option + # complete directories when there is ````, ```` or + # ````after option + if completion_type != "dir" and os.path.isfile(opt): + yield comp_file + elif os.path.isdir(opt): + yield os.path.join(comp_file, "") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000..db9d5cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,236 @@ +"""Base Command class, and related routines""" + +import functools +import logging +import logging.config +import optparse +import os +import sys +import traceback +from optparse import Values +from typing import Any, Callable, List, Optional, Tuple + +from pip._vendor.rich import traceback as rich_traceback + +from pip._internal.cli import cmdoptions +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip._internal.cli.status_codes import ( + ERROR, + PREVIOUS_BUILD_DIR_ERROR, + UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, +) +from pip._internal.exceptions import ( + BadCommand, + CommandError, + DiagnosticPipError, + InstallationError, + NetworkConnectionError, + PreviousBuildDirError, + UninstallationError, +) +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging +from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry +from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry +from pip._internal.utils.virtualenv import running_under_virtualenv + +__all__ = ["Command"] + +logger = logging.getLogger(__name__) + + +class Command(CommandContextMixIn): + usage: str = "" + ignore_require_venv: bool = False + + def __init__(self, name: str, summary: str, isolated: bool = False) -> None: + super().__init__() + + self.name = name + self.summary = summary + self.parser = ConfigOptionParser( + usage=self.usage, + prog=f"{get_prog()} {name}", + formatter=UpdatingDefaultsHelpFormatter(), + add_help_option=False, + name=name, + description=self.__doc__, + isolated=isolated, + ) + + self.tempdir_registry: Optional[TempDirRegistry] = None + + # Commands should add options to this option group + optgroup_name = f"{self.name.capitalize()} Options" + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + self.add_options() + + def add_options(self) -> None: + pass + + def handle_pip_version_check(self, options: Values) -> None: + """ + This is a no-op so that commands by default do not do the pip version + check. + """ + # Make sure we do the pip version check if the index_group options + # are present. + assert not hasattr(options, "no_index") + + def run(self, options: Values, args: List[str]) -> int: + raise NotImplementedError + + def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]: + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args: List[str]) -> int: + try: + with self.main_context(): + return self._main(args) + finally: + logging.shutdown() + + def _main(self, args: List[str]) -> int: + # We must initialize this before the tempdir manager, otherwise the + # configuration would not be accessible by the time we clean up the + # tempdir manager. + self.tempdir_registry = self.enter_context(tempdir_registry()) + # Intentionally set as early as possible so globally-managed temporary + # directories are available to the rest of the code. + self.enter_context(global_tempdir_manager()) + + options, args = self.parse_args(args) + + # Set verbosity so that it can be used elsewhere. + self.verbosity = options.verbose - options.quiet + + level_number = setup_logging( + verbosity=self.verbosity, + no_color=options.no_color, + user_log_file=options.log, + ) + + always_enabled_features = set(options.features_enabled) & set( + cmdoptions.ALWAYS_ENABLED_FEATURES + ) + if always_enabled_features: + logger.warning( + "The following features are always enabled: %s. ", + ", ".join(sorted(always_enabled_features)), + ) + + # Make sure that the --python argument isn't specified after the + # subcommand. We can tell, because if --python was specified, + # we should only reach this point if we're running in the created + # subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment + # variable set. + if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ: + logger.critical( + "The --python option must be placed before the pip subcommand name" + ) + sys.exit(ERROR) + + # TODO: Try to get these passing down from the command? + # without resorting to os.environ to hold these. + # This also affects isolated builds and it should. + + if options.no_input: + os.environ["PIP_NO_INPUT"] = "1" + + if options.exists_action: + os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action) + + if options.require_venv and not self.ignore_require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical("Could not find an activated virtualenv (required).") + sys.exit(VIRTUALENV_NOT_FOUND) + + if options.cache_dir: + options.cache_dir = normalize_path(options.cache_dir) + if not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "or is not writable by the current user. The cache " + "has been disabled. Check the permissions and owner of " + "that directory. If executing pip with sudo, you should " + "use sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + def intercepts_unhandled_exc( + run_func: Callable[..., int] + ) -> Callable[..., int]: + @functools.wraps(run_func) + def exc_logging_wrapper(*args: Any) -> int: + try: + status = run_func(*args) + assert isinstance(status, int) + return status + except DiagnosticPipError as exc: + logger.error("%s", exc, extra={"rich": True}) + logger.debug("Exception information:", exc_info=True) + + return ERROR + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug("Exception information:", exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except ( + InstallationError, + UninstallationError, + BadCommand, + NetworkConnectionError, + ) as exc: + logger.critical(str(exc)) + logger.debug("Exception information:", exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical("%s", exc) + logger.debug("Exception information:", exc_info=True) + + return ERROR + except BrokenStdoutLoggingError: + # Bypass our logger and write any remaining messages to + # stderr because stdout no longer works. + print("ERROR: Pipe to stdout was broken", file=sys.stderr) + if level_number <= logging.DEBUG: + traceback.print_exc(file=sys.stderr) + + return ERROR + except KeyboardInterrupt: + logger.critical("Operation cancelled by user") + logger.debug("Exception information:", exc_info=True) + + return ERROR + except BaseException: + logger.critical("Exception:", exc_info=True) + + return UNKNOWN_ERROR + + return exc_logging_wrapper + + try: + if not options.debug_mode: + run = intercepts_unhandled_exc(self.run) + else: + run = self.run + rich_traceback.install(show_locals=True) + return run(options, args) + finally: + self.handle_pip_version_check(options) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000..d643256 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,1074 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import importlib.util +import logging +import os +import textwrap +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values +from textwrap import dedent +from typing import Any, Callable, Dict, Optional, Tuple + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.parser import ConfigOptionParser +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, get_src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.target_python import TargetPython +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.misc import strtobool + +logger = logging.getLogger(__name__) + + +def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None: + """ + Raise an option parsing error using parser.error(). + + Args: + parser: an OptionParser instance. + option: an Option instance. + msg: the error text. + """ + msg = f"{option} error: {msg}" + msg = textwrap.fill(" ".join(msg.split())) + parser.error(msg) + + +def make_option_group(group: Dict[str, Any], parser: ConfigOptionParser) -> OptionGroup: + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group["name"]) + for option in group["options"]: + option_group.add_option(option()) + return option_group + + +def check_dist_restriction(options: Values, check_target: bool = False) -> None: + """Function for determining if custom platform options are allowed. + + :param options: The OptionParser options. + :param check_target: Whether or not to check if --target is being used. + """ + dist_restriction_set = any( + [ + options.python_version, + options.platforms, + options.abis, + options.implementation, + ] + ) + + binary_only = FormatControl(set(), {":all:"}) + sdist_dependencies_allowed = ( + options.format_control != binary_only and not options.ignore_dependencies + ) + + # Installations or downloads using dist restrictions must not combine + # source distributions and dist-specific wheels, as they are not + # guaranteed to be locally compatible. + if dist_restriction_set and sdist_dependencies_allowed: + raise CommandError( + "When restricting platform and interpreter constraints using " + "--python-version, --platform, --abi, or --implementation, " + "either --no-deps must be set, or --only-binary=:all: must be " + "set and --no-binary must not be set (or must be set to " + ":none:)." + ) + + if check_target: + if not options.dry_run and dist_restriction_set and not options.target_dir: + raise CommandError( + "Can not use any platform or abi specific options unless " + "installing via '--target' or using '--dry-run'" + ) + + +def _path_option_check(option: Option, opt: str, value: str) -> str: + return os.path.expanduser(value) + + +def _package_name_option_check(option: Option, opt: str, value: str) -> str: + return canonicalize_name(value) + + +class PipOption(Option): + TYPES = Option.TYPES + ("path", "package_name") + TYPE_CHECKER = Option.TYPE_CHECKER.copy() + TYPE_CHECKER["package_name"] = _package_name_option_check + TYPE_CHECKER["path"] = _path_option_check + + +########### +# options # +########### + +help_: Callable[..., Option] = partial( + Option, + "-h", + "--help", + dest="help", + action="help", + help="Show help.", +) + +debug_mode: Callable[..., Option] = partial( + Option, + "--debug", + dest="debug_mode", + action="store_true", + default=False, + help=( + "Let unhandled exceptions propagate outside the main subroutine, " + "instead of logging them to stderr." + ), +) + +isolated_mode: Callable[..., Option] = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) + +require_virtualenv: Callable[..., Option] = partial( + Option, + "--require-virtualenv", + "--require-venv", + dest="require_venv", + action="store_true", + default=False, + help=( + "Allow pip to only run in a virtual environment; " + "exit with an error otherwise." + ), +) + +override_externally_managed: Callable[..., Option] = partial( + Option, + "--break-system-packages", + dest="override_externally_managed", + action="store_true", + help="Allow pip to modify an EXTERNALLY-MANAGED Python installation", +) + +python: Callable[..., Option] = partial( + Option, + "--python", + dest="python", + help="Run pip with the specified Python interpreter.", +) + +verbose: Callable[..., Option] = partial( + Option, + "-v", + "--verbose", + dest="verbose", + action="count", + default=0, + help="Give more output. Option is additive, and can be used up to 3 times.", +) + +no_color: Callable[..., Option] = partial( + Option, + "--no-color", + dest="no_color", + action="store_true", + default=False, + help="Suppress colored output.", +) + +version: Callable[..., Option] = partial( + Option, + "-V", + "--version", + dest="version", + action="store_true", + help="Show version and exit.", +) + +quiet: Callable[..., Option] = partial( + Option, + "-q", + "--quiet", + dest="quiet", + action="count", + default=0, + help=( + "Give less output. Option is additive, and can be used up to 3" + " times (corresponding to WARNING, ERROR, and CRITICAL logging" + " levels)." + ), +) + +progress_bar: Callable[..., Option] = partial( + Option, + "--progress-bar", + dest="progress_bar", + type="choice", + choices=["on", "off"], + default="on", + help="Specify whether the progress bar should be used [on, off] (default: on)", +) + +log: Callable[..., Option] = partial( + PipOption, + "--log", + "--log-file", + "--local-log", + dest="log", + metavar="path", + type="path", + help="Path to a verbose appending log.", +) + +no_input: Callable[..., Option] = partial( + Option, + # Don't ask for input + "--no-input", + dest="no_input", + action="store_true", + default=False, + help="Disable prompting for input.", +) + +keyring_provider: Callable[..., Option] = partial( + Option, + "--keyring-provider", + dest="keyring_provider", + choices=["auto", "disabled", "import", "subprocess"], + default="auto", + help=( + "Enable the credential lookup via the keyring library if user input is allowed." + " Specify which mechanism to use [disabled, import, subprocess]." + " (default: disabled)" + ), +) + +proxy: Callable[..., Option] = partial( + Option, + "--proxy", + dest="proxy", + type="str", + default="", + help="Specify a proxy in the form scheme://[user:passwd@]proxy.server:port.", +) + +retries: Callable[..., Option] = partial( + Option, + "--retries", + dest="retries", + type="int", + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).", +) + +timeout: Callable[..., Option] = partial( + Option, + "--timeout", + "--default-timeout", + metavar="sec", + dest="timeout", + type="float", + default=15, + help="Set the socket timeout (default %default seconds).", +) + + +def exists_action() -> Option: + return Option( + # Option when path already exist + "--exists-action", + dest="exists_action", + type="choice", + choices=["s", "i", "w", "b", "a"], + default=[], + action="append", + metavar="action", + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", + ) + + +cert: Callable[..., Option] = partial( + PipOption, + "--cert", + dest="cert", + type="path", + metavar="path", + help=( + "Path to PEM-encoded CA certificate bundle. " + "If provided, overrides the default. " + "See 'SSL Certificate Verification' in pip documentation " + "for more information." + ), +) + +client_cert: Callable[..., Option] = partial( + PipOption, + "--client-cert", + dest="client_cert", + type="path", + default=None, + metavar="path", + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.", +) + +index_url: Callable[..., Option] = partial( + Option, + "-i", + "--index-url", + "--pypi-url", + dest="index_url", + metavar="URL", + default=PyPI.simple_url, + help="Base URL of the Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) + + +def extra_index_url() -> Option: + return Option( + "--extra-index-url", + dest="extra_index_urls", + metavar="URL", + action="append", + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url.", + ) + + +no_index: Callable[..., Option] = partial( + Option, + "--no-index", + dest="no_index", + action="store_true", + default=False, + help="Ignore package index (only looking at --find-links URLs instead).", +) + + +def find_links() -> Option: + return Option( + "-f", + "--find-links", + dest="find_links", + action="append", + default=[], + metavar="url", + help="If a URL or path to an html file, then parse for links to " + "archives such as sdist (.tar.gz) or wheel (.whl) files. " + "If a local path or file:// URL that's a directory, " + "then look for archives in the directory listing. " + "Links to VCS project URLs are not supported.", + ) + + +def trusted_host() -> Option: + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host or host:port pair as trusted, even though it " + "does not have valid or any HTTPS.", + ) + + +def constraints() -> Option: + return Option( + "-c", + "--constraint", + dest="constraints", + action="append", + default=[], + metavar="file", + help="Constrain versions using the given constraints file. " + "This option can be used multiple times.", + ) + + +def requirements() -> Option: + return Option( + "-r", + "--requirement", + dest="requirements", + action="append", + default=[], + metavar="file", + help="Install from the given requirements file. " + "This option can be used multiple times.", + ) + + +def editable() -> Option: + return Option( + "-e", + "--editable", + dest="editables", + action="append", + default=[], + metavar="path/url", + help=( + "Install a project in editable mode (i.e. setuptools " + '"develop mode") from a local project path or a VCS url.' + ), + ) + + +def _handle_src(option: Option, opt_str: str, value: str, parser: OptionParser) -> None: + value = os.path.abspath(value) + setattr(parser.values, option.dest, value) + + +src: Callable[..., Option] = partial( + PipOption, + "--src", + "--source", + "--source-dir", + "--source-directory", + dest="src_dir", + type="path", + metavar="dir", + default=get_src_prefix(), + action="callback", + callback=_handle_src, + help="Directory to check out editable projects into. " + 'The default in a virtualenv is "/src". ' + 'The default for global installs is "/src".', +) + + +def _get_format_control(values: Values, option: Option) -> Any: + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, + existing.no_binary, + existing.only_binary, + ) + + +def _handle_only_binary( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, + existing.only_binary, + existing.no_binary, + ) + + +def no_binary() -> Option: + format_control = FormatControl(set(), set()) + return Option( + "--no-binary", + dest="format_control", + action="callback", + callback=_handle_no_binary, + type="str", + default=format_control, + help="Do not use binary packages. Can be supplied multiple times, and " + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all binary packages, ":none:" to empty the set (notice ' + "the colons), or one or more package names with commas between " + "them (no colons). Note that some packages are tricky to compile " + "and may fail to install when this option is used on them.", + ) + + +def only_binary() -> Option: + format_control = FormatControl(set(), set()) + return Option( + "--only-binary", + dest="format_control", + action="callback", + callback=_handle_only_binary, + type="str", + default=format_control, + help="Do not use source packages. Can be supplied multiple times, and " + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all source packages, ":none:" to empty the set, or one ' + "or more package names with commas between them. Packages " + "without binary distributions will fail to install when this " + "option is used on them.", + ) + + +platforms: Callable[..., Option] = partial( + Option, + "--platform", + dest="platforms", + metavar="platform", + action="append", + default=None, + help=( + "Only use wheels compatible with . Defaults to the " + "platform of the running system. Use this option multiple times to " + "specify multiple platforms supported by the target interpreter." + ), +) + + +# This was made a separate function for unit-testing purposes. +def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]]: + """ + Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. + + :return: A 2-tuple (version_info, error_msg), where `error_msg` is + non-None if and only if there was a parsing error. + """ + if not value: + # The empty string is the same as not providing a value. + return (None, None) + + parts = value.split(".") + if len(parts) > 3: + return ((), "at most three version parts are allowed") + + if len(parts) == 1: + # Then we are in the case of "3" or "37". + value = parts[0] + if len(value) > 1: + parts = [value[0], value[1:]] + + try: + version_info = tuple(int(part) for part in parts) + except ValueError: + return ((), "each version part must be an integer") + + return (version_info, None) + + +def _handle_python_version( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: + """ + Handle a provided --python-version value. + """ + version_info, error_msg = _convert_python_version(value) + if error_msg is not None: + msg = f"invalid --python-version value: {value!r}: {error_msg}" + raise_option_error(parser, option=option, msg=msg) + + parser.values.python_version = version_info + + +python_version: Callable[..., Option] = partial( + Option, + "--python-version", + dest="python_version", + metavar="python_version", + action="callback", + callback=_handle_python_version, + type="str", + default=None, + help=dedent( + """\ + The Python interpreter version to use for wheel and "Requires-Python" + compatibility checks. Defaults to a version derived from the running + interpreter. The version can be specified using up to three dot-separated + integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor + version can also be given as a string without dots (e.g. "37" for 3.7.0). + """ + ), +) + + +implementation: Callable[..., Option] = partial( + Option, + "--implementation", + dest="implementation", + metavar="implementation", + default=None, + help=( + "Only use wheels compatible with Python " + "implementation , e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels." + ), +) + + +abis: Callable[..., Option] = partial( + Option, + "--abi", + dest="abis", + metavar="abi", + action="append", + default=None, + help=( + "Only use wheels compatible with Python abi , e.g. 'pypy_41'. " + "If not specified, then the current interpreter abi tag is used. " + "Use this option multiple times to specify multiple abis supported " + "by the target interpreter. Generally you will need to specify " + "--implementation, --platform, and --python-version when using this " + "option." + ), +) + + +def add_target_python_options(cmd_opts: OptionGroup) -> None: + cmd_opts.add_option(platforms()) + cmd_opts.add_option(python_version()) + cmd_opts.add_option(implementation()) + cmd_opts.add_option(abis()) + + +def make_target_python(options: Values) -> TargetPython: + target_python = TargetPython( + platforms=options.platforms, + py_version_info=options.python_version, + abis=options.abis, + implementation=options.implementation, + ) + + return target_python + + +def prefer_binary() -> Option: + return Option( + "--prefer-binary", + dest="prefer_binary", + action="store_true", + default=False, + help=( + "Prefer binary packages over source packages, even if the " + "source packages are newer." + ), + ) + + +cache_dir: Callable[..., Option] = partial( + PipOption, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + type="path", + help="Store the cache data in .", +) + + +def _handle_no_cache_dir( + option: Option, opt: str, value: str, parser: OptionParser +) -> None: + """ + Process a value provided for the --no-cache-dir option. + + This is an optparse.Option callback for the --no-cache-dir option. + """ + # The value argument will be None if --no-cache-dir is passed via the + # command-line, since the option doesn't accept arguments. However, + # the value can be non-None if the option is triggered e.g. by an + # environment variable, like PIP_NO_CACHE_DIR=true. + if value is not None: + # Then parse the string value to get argument error-checking. + try: + strtobool(value) + except ValueError as exc: + raise_option_error(parser, option=option, msg=str(exc)) + + # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool() + # converted to 0 (like "false" or "no") caused cache_dir to be disabled + # rather than enabled (logic would say the latter). Thus, we disable + # the cache directory not just on values that parse to True, but (for + # backwards compatibility reasons) also on values that parse to False. + # In other words, always set it to False if the option is provided in + # some (valid) form. + parser.values.cache_dir = False + + +no_cache: Callable[..., Option] = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="callback", + callback=_handle_no_cache_dir, + help="Disable the cache.", +) + +no_deps: Callable[..., Option] = partial( + Option, + "--no-deps", + "--no-dependencies", + dest="ignore_dependencies", + action="store_true", + default=False, + help="Don't install package dependencies.", +) + +ignore_requires_python: Callable[..., Option] = partial( + Option, + "--ignore-requires-python", + dest="ignore_requires_python", + action="store_true", + help="Ignore the Requires-Python information.", +) + +no_build_isolation: Callable[..., Option] = partial( + Option, + "--no-build-isolation", + dest="build_isolation", + action="store_false", + default=True, + help="Disable isolation when building a modern source distribution. " + "Build dependencies specified by PEP 518 must be already installed " + "if this option is used.", +) + +check_build_deps: Callable[..., Option] = partial( + Option, + "--check-build-dependencies", + dest="check_build_deps", + action="store_true", + default=False, + help="Check the build dependencies when PEP517 is used.", +) + + +def _handle_no_use_pep517( + option: Option, opt: str, value: str, parser: OptionParser +) -> None: + """ + Process a value provided for the --no-use-pep517 option. + + This is an optparse.Option callback for the no_use_pep517 option. + """ + # Since --no-use-pep517 doesn't accept arguments, the value argument + # will be None if --no-use-pep517 is passed via the command-line. + # However, the value can be non-None if the option is triggered e.g. + # by an environment variable, for example "PIP_NO_USE_PEP517=true". + if value is not None: + msg = """A value was passed for --no-use-pep517, + probably using either the PIP_NO_USE_PEP517 environment variable + or the "no-use-pep517" config file option. Use an appropriate value + of the PIP_USE_PEP517 environment variable or the "use-pep517" + config file option instead. + """ + raise_option_error(parser, option=option, msg=msg) + + # If user doesn't wish to use pep517, we check if setuptools and wheel are installed + # and raise error if it is not. + packages = ("setuptools", "wheel") + if not all(importlib.util.find_spec(package) for package in packages): + msg = ( + f"It is not possible to use --no-use-pep517 " + f"without {' and '.join(packages)} installed." + ) + raise_option_error(parser, option=option, msg=msg) + + # Otherwise, --no-use-pep517 was passed via the command-line. + parser.values.use_pep517 = False + + +use_pep517: Any = partial( + Option, + "--use-pep517", + dest="use_pep517", + action="store_true", + default=None, + help="Use PEP 517 for building source distributions " + "(use --no-use-pep517 to force legacy behaviour).", +) + +no_use_pep517: Any = partial( + Option, + "--no-use-pep517", + dest="use_pep517", + action="callback", + callback=_handle_no_use_pep517, + default=None, + help=SUPPRESS_HELP, +) + + +def _handle_config_settings( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: + key, sep, val = value.partition("=") + if sep != "=": + parser.error(f"Arguments to {opt_str} must be of the form KEY=VAL") + dest = getattr(parser.values, option.dest) + if dest is None: + dest = {} + setattr(parser.values, option.dest, dest) + if key in dest: + if isinstance(dest[key], list): + dest[key].append(val) + else: + dest[key] = [dest[key], val] + else: + dest[key] = val + + +config_settings: Callable[..., Option] = partial( + Option, + "-C", + "--config-settings", + dest="config_settings", + type=str, + action="callback", + callback=_handle_config_settings, + metavar="settings", + help="Configuration settings to be passed to the PEP 517 build backend. " + "Settings take the form KEY=VALUE. Use multiple --config-settings options " + "to pass multiple keys to the backend.", +) + +build_options: Callable[..., Option] = partial( + Option, + "--build-option", + dest="build_options", + metavar="options", + action="append", + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", +) + +global_options: Callable[..., Option] = partial( + Option, + "--global-option", + dest="global_options", + action="append", + metavar="options", + help="Extra global options to be supplied to the setup.py " + "call before the install or bdist_wheel command.", +) + +no_clean: Callable[..., Option] = partial( + Option, + "--no-clean", + action="store_true", + default=False, + help="Don't clean up build directories.", +) + +pre: Callable[..., Option] = partial( + Option, + "--pre", + action="store_true", + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.", +) + +disable_pip_version_check: Callable[..., Option] = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=True, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.", +) + +root_user_action: Callable[..., Option] = partial( + Option, + "--root-user-action", + dest="root_user_action", + default="warn", + choices=["warn", "ignore"], + help="Action if pip is run as a root user. By default, a warning message is shown.", +) + + +def _handle_merge_hash( + option: Option, opt_str: str, value: str, parser: OptionParser +) -> None: + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(":", 1) + except ValueError: + parser.error( + f"Arguments to {opt_str} must be a hash name " + "followed by a value, like --hash=sha256:" + "abcde..." + ) + if algo not in STRONG_HASHES: + parser.error( + "Allowed hash algorithms for {} are {}.".format( + opt_str, ", ".join(STRONG_HASHES) + ) + ) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash: Callable[..., Option] = partial( + Option, + "--hash", + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest="hashes", + action="callback", + callback=_handle_merge_hash, + type="string", + help="Verify that the package's archive matches this " + "hash before installing. Example: --hash=sha256:abcdef...", +) + + +require_hashes: Callable[..., Option] = partial( + Option, + "--require-hashes", + dest="require_hashes", + action="store_true", + default=False, + help="Require a hash to check each requirement against, for " + "repeatable installs. This option is implied when any package in a " + "requirements file has a --hash option.", +) + + +list_path: Callable[..., Option] = partial( + PipOption, + "--path", + dest="path", + type="path", + action="append", + help="Restrict to the specified installation path for listing " + "packages (can be used multiple times).", +) + + +def check_list_path_option(options: Values) -> None: + if options.path and (options.user or options.local): + raise CommandError("Cannot combine '--path' with '--user' or '--local'") + + +list_exclude: Callable[..., Option] = partial( + PipOption, + "--exclude", + dest="excludes", + action="append", + metavar="package", + type="package_name", + help="Exclude specified package from the output", +) + + +no_python_version_warning: Callable[..., Option] = partial( + Option, + "--no-python-version-warning", + dest="no_python_version_warning", + action="store_true", + default=False, + help="Silence deprecation warnings for upcoming unsupported Pythons.", +) + + +# Features that are now always on. A warning is printed if they are used. +ALWAYS_ENABLED_FEATURES = [ + "no-binary-enable-wheel-cache", # always on since 23.1 +] + +use_new_feature: Callable[..., Option] = partial( + Option, + "--use-feature", + dest="features_enabled", + metavar="feature", + action="append", + default=[], + choices=[ + "fast-deps", + "truststore", + ] + + ALWAYS_ENABLED_FEATURES, + help="Enable new functionality, that may be backward incompatible.", +) + +use_deprecated_feature: Callable[..., Option] = partial( + Option, + "--use-deprecated", + dest="deprecated_features_enabled", + metavar="feature", + action="append", + default=[], + choices=[ + "legacy-resolver", + ], + help=("Enable deprecated functionality, that will be removed in the future."), +) + + +########## +# groups # +########## + +general_group: Dict[str, Any] = { + "name": "General Options", + "options": [ + help_, + debug_mode, + isolated_mode, + require_virtualenv, + python, + verbose, + version, + quiet, + log, + no_input, + keyring_provider, + proxy, + retries, + timeout, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + no_python_version_warning, + use_new_feature, + use_deprecated_feature, + ], +} + +index_group: Dict[str, Any] = { + "name": "Package Index Options", + "options": [ + index_url, + extra_index_url, + no_index, + find_links, + ], +} diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py new file mode 100644 index 0000000..139995a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/command_context.py @@ -0,0 +1,27 @@ +from contextlib import ExitStack, contextmanager +from typing import ContextManager, Generator, TypeVar + +_T = TypeVar("_T", covariant=True) + + +class CommandContextMixIn: + def __init__(self) -> None: + super().__init__() + self._in_main_context = False + self._main_context = ExitStack() + + @contextmanager + def main_context(self) -> Generator[None, None, None]: + assert not self._in_main_context + + self._in_main_context = True + try: + with self._main_context: + yield + finally: + self._in_main_context = False + + def enter_context(self, context_provider: ContextManager[_T]) -> _T: + assert self._in_main_context + + return self._main_context.enter_context(context_provider) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/main.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/main.py new file mode 100644 index 0000000..7e061f5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/main.py @@ -0,0 +1,79 @@ +"""Primary application entrypoint. +""" +import locale +import logging +import os +import sys +import warnings +from typing import List, Optional + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import create_command +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation + +logger = logging.getLogger(__name__) + + +# Do not import and use main() directly! Using it directly is actively +# discouraged by pip's maintainers. The name, location and behavior of +# this function is subject to change, so calling it directly is not +# portable across different pip versions. + +# In addition, running pip in-process is unsupported and unsafe. This is +# elaborated in detail at +# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program. +# That document also provides suggestions that should work for nearly +# all users that are considering importing and using main() directly. + +# However, we know that certain users will still want to invoke pip +# in-process. If you understand and accept the implications of using pip +# in an unsupported manner, the best approach is to use runpy to avoid +# depending on the exact location of this entry point. + +# The following example shows how to use runpy to invoke pip in that +# case: +# +# sys.argv = ["pip", your, args, here] +# runpy.run_module("pip", run_name="__main__") +# +# Note that this will exit the process after running, unlike a direct +# call to main. As it is not safe to do any processing after calling +# main, this should not be an issue in practice. + + +def main(args: Optional[List[str]] = None) -> int: + if args is None: + args = sys.argv[1:] + + # Suppress the pkg_resources deprecation warning + # Note - we use a module of .*pkg_resources to cover + # the normal case (pip._vendor.pkg_resources) and the + # devendored case (a bare pkg_resources) + warnings.filterwarnings( + action="ignore", category=DeprecationWarning, module=".*pkg_resources" + ) + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parse_command(args) + except PipError as exc: + sys.stderr.write(f"ERROR: {exc}") + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip._internal.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, "") + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = create_command(cmd_name, isolated=("--isolated" in cmd_args)) + + return command.main(cmd_args) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000..5ade356 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,134 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import subprocess +import sys +from typing import List, Optional, Tuple + +from pip._internal.build_env import get_runnable_pip +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip._internal.commands import commands_dict, get_similar_commands +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_pip_version, get_prog + +__all__ = ["create_main_parser", "parse_command"] + + +def create_main_parser() -> ConfigOptionParser: + """Creates and returns the main parser for pip's CLI""" + + parser = ConfigOptionParser( + usage="\n%prog [options]", + add_help_option=False, + formatter=UpdatingDefaultsHelpFormatter(), + name="global", + prog=get_prog(), + ) + parser.disable_interspersed_args() + + parser.version = get_pip_version() + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + # so the help formatter knows + parser.main = True # type: ignore + + # create command listing for description + description = [""] + [ + f"{name:27} {command_info.summary}" + for name, command_info in commands_dict.items() + ] + parser.description = "\n".join(description) + + return parser + + +def identify_python_interpreter(python: str) -> Optional[str]: + # If the named file exists, use it. + # If it's a directory, assume it's a virtual environment and + # look for the environment's Python executable. + if os.path.exists(python): + if os.path.isdir(python): + # bin/python for Unix, Scripts/python.exe for Windows + # Try both in case of odd cases like cygwin. + for exe in ("bin/python", "Scripts/python.exe"): + py = os.path.join(python, exe) + if os.path.exists(py): + return py + else: + return python + + # Could not find the interpreter specified + return None + + +def parse_command(args: List[str]) -> Tuple[str, List[str]]: + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --python + if general_options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ: + # Re-invoke pip using the specified Python interpreter + interpreter = identify_python_interpreter(general_options.python) + if interpreter is None: + raise CommandError( + f"Could not locate Python interpreter {general_options.python}" + ) + + pip_cmd = [ + interpreter, + get_runnable_pip(), + ] + pip_cmd.extend(args) + + # Set a flag so the child doesn't re-invoke itself, causing + # an infinite loop. + os.environ["_PIP_RUNNING_IN_SUBPROCESS"] = "1" + returncode = 0 + try: + proc = subprocess.run(pip_cmd) + returncode = proc.returncode + except (subprocess.SubprocessError, OSError) as exc: + raise CommandError(f"Failed to run pip under {interpreter}: {exc}") + sys.exit(returncode) + + # --version + if general_options.version: + sys.stdout.write(parser.version) + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == "help" and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = [f'unknown command "{cmd_name}"'] + if guess: + msg.append(f'maybe you meant "{guess}"') + + raise CommandError(" - ".join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000..ae554b2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,294 @@ +"""Base option parser setup""" + +import logging +import optparse +import shutil +import sys +import textwrap +from contextlib import suppress +from typing import Any, Dict, Generator, List, Tuple + +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.configuration import Configuration, ConfigurationError +from pip._internal.utils.misc import redact_auth_from_url, strtobool + +logger = logging.getLogger(__name__) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + # help position must be aligned with __init__.parseopts.description + kwargs["max_help_position"] = 30 + kwargs["indent_increment"] = 1 + kwargs["width"] = shutil.get_terminal_size()[0] - 2 + super().__init__(*args, **kwargs) + + def format_option_strings(self, option: optparse.Option) -> str: + return self._format_option_strings(option) + + def _format_option_strings( + self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", " + ) -> str: + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + assert option.dest is not None + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt.format(metavar.lower())) + + return "".join(opts) + + def format_heading(self, heading: str) -> str: + if heading == "Options": + return "" + return heading + ":\n" + + def format_usage(self, usage: str) -> str: + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " ")) + return msg + + def format_description(self, description: str) -> str: + # leave full control over description to us + if description: + if hasattr(self.parser, "main"): + label = "Commands" + else: + label = "Description" + # some doc strings have initial newlines, some don't + description = description.lstrip("\n") + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = f"{label}:\n{description}\n" + return description + else: + return "" + + def format_epilog(self, epilog: str) -> str: + # leave full control over epilog to us + if epilog: + return epilog + else: + return "" + + def indent_lines(self, text: str, indent: str) -> str: + new_lines = [indent + line for line in text.split("\n")] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + + Also redact auth from url type options + """ + + def expand_default(self, option: optparse.Option) -> str: + default_values = None + if self.parser is not None: + assert isinstance(self.parser, ConfigOptionParser) + self.parser._update_defaults(self.parser.defaults) + assert option.dest is not None + default_values = self.parser.defaults.get(option.dest) + help_text = super().expand_default(option) + + if default_values and option.metavar == "URL": + if isinstance(default_values, str): + default_values = [default_values] + + # If its not a list, we should abort and just return the help text + if not isinstance(default_values, list): + default_values = [] + + for val in default_values: + help_text = help_text.replace(val, redact_auth_from_url(val)) + + return help_text + + +class CustomOptionParser(optparse.OptionParser): + def insert_option_group( + self, idx: int, *args: Any, **kwargs: Any + ) -> optparse.OptionGroup: + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self) -> List[optparse.Option]: + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + def __init__( + self, + *args: Any, + name: str, + isolated: bool = False, + **kwargs: Any, + ) -> None: + self.name = name + self.config = Configuration(isolated) + + assert self.name + super().__init__(*args, **kwargs) + + def check_default(self, option: optparse.Option, key: str, val: Any) -> Any: + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print(f"An error occurred during configuration: {exc}") + sys.exit(3) + + def _get_ordered_configuration_items( + self, + ) -> Generator[Tuple[str, Any], None, None]: + # Configuration gives keys in an unordered manner. Order them. + override_order = ["global", self.name, ":env:"] + + # Pool the options into different groups + section_items: Dict[str, List[Tuple[str, Any]]] = { + name: [] for name in override_order + } + for section_key, val in self.config.items(): + # ignore empty values + if not val: + logger.debug( + "Ignoring configuration key '%s' as it's value is empty.", + section_key, + ) + continue + + section, key = section_key.split(".", 1) + if section in override_order: + section_items[section].append((key, val)) + + # Yield each group in their override order + for section in override_order: + for key, val in section_items[section]: + yield key, val + + def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]: + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in self._get_ordered_configuration_items(): + # '--' because configuration supports only long names + option = self.get_option("--" + key) + + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + assert option.dest is not None + + if option.action in ("store_true", "store_false"): + try: + val = strtobool(val) + except ValueError: + self.error( + f"{val} is not a valid value for {key} option, " + "please specify a boolean value like yes/no, " + "true/false or 1/0 instead." + ) + elif option.action == "count": + with suppress(ValueError): + val = strtobool(val) + with suppress(ValueError): + val = int(val) + if not isinstance(val, int) or val < 0: + self.error( + f"{val} is not a valid value for {key} option, " + "please instead specify either a non-negative integer " + "or a boolean value like yes/no or false/true " + "which is equivalent to 1/0." + ) + elif option.action == "append": + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == "callback": + assert option.callback is not None + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def get_default_values(self) -> optparse.Values: + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + # Load the configuration, or error out in case of an error + try: + self.config.load() + except ConfigurationError as err: + self.exit(UNKNOWN_ERROR, str(err)) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + assert option.dest is not None + default = defaults.get(option.dest) + if isinstance(default, str): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg: str) -> None: + self.print_usage(sys.stderr) + self.exit(UNKNOWN_ERROR, f"{msg}\n") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py new file mode 100644 index 0000000..0ad1403 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/progress_bars.py @@ -0,0 +1,68 @@ +import functools +from typing import Callable, Generator, Iterable, Iterator, Optional, Tuple + +from pip._vendor.rich.progress import ( + BarColumn, + DownloadColumn, + FileSizeColumn, + Progress, + ProgressColumn, + SpinnerColumn, + TextColumn, + TimeElapsedColumn, + TimeRemainingColumn, + TransferSpeedColumn, +) + +from pip._internal.utils.logging import get_indentation + +DownloadProgressRenderer = Callable[[Iterable[bytes]], Iterator[bytes]] + + +def _rich_progress_bar( + iterable: Iterable[bytes], + *, + bar_type: str, + size: int, +) -> Generator[bytes, None, None]: + assert bar_type == "on", "This should only be used in the default mode." + + if not size: + total = float("inf") + columns: Tuple[ProgressColumn, ...] = ( + TextColumn("[progress.description]{task.description}"), + SpinnerColumn("line", speed=1.5), + FileSizeColumn(), + TransferSpeedColumn(), + TimeElapsedColumn(), + ) + else: + total = size + columns = ( + TextColumn("[progress.description]{task.description}"), + BarColumn(), + DownloadColumn(), + TransferSpeedColumn(), + TextColumn("eta"), + TimeRemainingColumn(), + ) + + progress = Progress(*columns, refresh_per_second=30) + task_id = progress.add_task(" " * (get_indentation() + 2), total=total) + with progress: + for chunk in iterable: + yield chunk + progress.update(task_id, advance=len(chunk)) + + +def get_download_progress_renderer( + *, bar_type: str, size: Optional[int] = None +) -> DownloadProgressRenderer: + """Get an object that can be used to render the download progress. + + Returns a callable, that takes an iterable to "wrap". + """ + if bar_type == "on": + return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size) + else: + return iter # no-op, when passed an iterator diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py new file mode 100644 index 0000000..6f2f79c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/req_command.py @@ -0,0 +1,505 @@ +"""Contains the Command base classes that depend on PipSession. + +The classes in this module are in a separate module so the commands not +needing download / PackageFinder capability don't unnecessarily import the +PackageFinder machinery and all its vendored dependencies, etc. +""" + +import logging +import os +import sys +from functools import partial +from optparse import Values +from typing import TYPE_CHECKING, Any, List, Optional, Tuple + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.network.session import PipSession +from pip._internal.operations.build.build_tracker import BuildTracker +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, + install_req_from_parsed_requirement, + install_req_from_req_string, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.req.req_install import InstallRequirement +from pip._internal.resolution.base import BaseResolver +from pip._internal.self_outdated_check import pip_self_version_check +from pip._internal.utils.temp_dir import ( + TempDirectory, + TempDirectoryTypeRegistry, + tempdir_kinds, +) +from pip._internal.utils.virtualenv import running_under_virtualenv + +if TYPE_CHECKING: + from ssl import SSLContext + +logger = logging.getLogger(__name__) + + +def _create_truststore_ssl_context() -> Optional["SSLContext"]: + if sys.version_info < (3, 10): + raise CommandError("The truststore feature is only available for Python 3.10+") + + try: + import ssl + except ImportError: + logger.warning("Disabling truststore since ssl support is missing") + return None + + try: + from pip._vendor import truststore + except ImportError as e: + raise CommandError(f"The truststore feature is unavailable: {e}") + + return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + + +class SessionCommandMixin(CommandContextMixIn): + + """ + A class mixin for command classes needing _build_session(). + """ + + def __init__(self) -> None: + super().__init__() + self._session: Optional[PipSession] = None + + @classmethod + def _get_index_urls(cls, options: Values) -> Optional[List[str]]: + """Return a list of index urls from user-provided options.""" + index_urls = [] + if not getattr(options, "no_index", False): + url = getattr(options, "index_url", None) + if url: + index_urls.append(url) + urls = getattr(options, "extra_index_urls", None) + if urls: + index_urls.extend(urls) + # Return None rather than an empty list + return index_urls or None + + def get_default_session(self, options: Values) -> PipSession: + """Get a default-managed session.""" + if self._session is None: + self._session = self.enter_context(self._build_session(options)) + # there's no type annotation on requests.Session, so it's + # automatically ContextManager[Any] and self._session becomes Any, + # then https://github.com/python/mypy/issues/7696 kicks in + assert self._session is not None + return self._session + + def _build_session( + self, + options: Values, + retries: Optional[int] = None, + timeout: Optional[int] = None, + fallback_to_certifi: bool = False, + ) -> PipSession: + cache_dir = options.cache_dir + assert not cache_dir or os.path.isabs(cache_dir) + + if "truststore" in options.features_enabled: + try: + ssl_context = _create_truststore_ssl_context() + except Exception: + if not fallback_to_certifi: + raise + ssl_context = None + else: + ssl_context = None + + session = PipSession( + cache=os.path.join(cache_dir, "http-v2") if cache_dir else None, + retries=retries if retries is not None else options.retries, + trusted_hosts=options.trusted_hosts, + index_urls=self._get_index_urls(options), + ssl_context=ssl_context, + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = timeout if timeout is not None else options.timeout + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + session.auth.keyring_provider = options.keyring_provider + + return session + + +class IndexGroupCommand(Command, SessionCommandMixin): + + """ + Abstract base class for commands with the index_group options. + + This also corresponds to the commands that permit the pip version check. + """ + + def handle_pip_version_check(self, options: Values) -> None: + """ + Do the pip version check if not disabled. + + This overrides the default behavior of not doing the check. + """ + # Make sure the index_group options are present. + assert hasattr(options, "no_index") + + if options.disable_pip_version_check or options.no_index: + return + + # Otherwise, check if we're using the latest version of pip available. + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout), + # This is set to ensure the function does not fail when truststore is + # specified in use-feature but cannot be loaded. This usually raises a + # CommandError and shows a nice user-facing error, but this function is not + # called in that try-except block. + fallback_to_certifi=True, + ) + with session: + pip_self_version_check(session, options) + + +KEEPABLE_TEMPDIR_TYPES = [ + tempdir_kinds.BUILD_ENV, + tempdir_kinds.EPHEM_WHEEL_CACHE, + tempdir_kinds.REQ_BUILD, +] + + +def warn_if_run_as_root() -> None: + """Output a warning for sudo users on Unix. + + In a virtual environment, sudo pip still writes to virtualenv. + On Windows, users may run pip as Administrator without issues. + This warning only applies to Unix root users outside of virtualenv. + """ + if running_under_virtualenv(): + return + if not hasattr(os, "getuid"): + return + # On Windows, there are no "system managed" Python packages. Installing as + # Administrator via pip is the correct way of updating system environments. + # + # We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform + # checks: https://mypy.readthedocs.io/en/stable/common_issues.html + if sys.platform == "win32" or sys.platform == "cygwin": + return + + if os.getuid() != 0: + return + + logger.warning( + "Running pip as the 'root' user can result in broken permissions and " + "conflicting behaviour with the system package manager. " + "It is recommended to use a virtual environment instead: " + "https://pip.pypa.io/warnings/venv" + ) + + +def with_cleanup(func: Any) -> Any: + """Decorator for common logic related to managing temporary + directories. + """ + + def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None: + for t in KEEPABLE_TEMPDIR_TYPES: + registry.set_delete(t, False) + + def wrapper( + self: RequirementCommand, options: Values, args: List[Any] + ) -> Optional[int]: + assert self.tempdir_registry is not None + if options.no_clean: + configure_tempdir_registry(self.tempdir_registry) + + try: + return func(self, options, args) + except PreviousBuildDirError: + # This kind of conflict can occur when the user passes an explicit + # build directory with a pre-existing folder. In that case we do + # not want to accidentally remove it. + configure_tempdir_registry(self.tempdir_registry) + raise + + return wrapper + + +class RequirementCommand(IndexGroupCommand): + def __init__(self, *args: Any, **kw: Any) -> None: + super().__init__(*args, **kw) + + self.cmd_opts.add_option(cmdoptions.no_clean()) + + @staticmethod + def determine_resolver_variant(options: Values) -> str: + """Determines which resolver should be used, based on the given options.""" + if "legacy-resolver" in options.deprecated_features_enabled: + return "legacy" + + return "resolvelib" + + @classmethod + def make_requirement_preparer( + cls, + temp_build_dir: TempDirectory, + options: Values, + build_tracker: BuildTracker, + session: PipSession, + finder: PackageFinder, + use_user_site: bool, + download_dir: Optional[str] = None, + verbosity: int = 0, + ) -> RequirementPreparer: + """ + Create a RequirementPreparer instance for the given parameters. + """ + temp_build_dir_path = temp_build_dir.path + assert temp_build_dir_path is not None + legacy_resolver = False + + resolver_variant = cls.determine_resolver_variant(options) + if resolver_variant == "resolvelib": + lazy_wheel = "fast-deps" in options.features_enabled + if lazy_wheel: + logger.warning( + "pip is using lazily downloaded wheels using HTTP " + "range requests to obtain dependency information. " + "This experimental feature is enabled through " + "--use-feature=fast-deps and it is not ready for " + "production." + ) + else: + legacy_resolver = True + lazy_wheel = False + if "fast-deps" in options.features_enabled: + logger.warning( + "fast-deps has no effect when used with the legacy resolver." + ) + + return RequirementPreparer( + build_dir=temp_build_dir_path, + src_dir=options.src_dir, + download_dir=download_dir, + build_isolation=options.build_isolation, + check_build_deps=options.check_build_deps, + build_tracker=build_tracker, + session=session, + progress_bar=options.progress_bar, + finder=finder, + require_hashes=options.require_hashes, + use_user_site=use_user_site, + lazy_wheel=lazy_wheel, + verbosity=verbosity, + legacy_resolver=legacy_resolver, + ) + + @classmethod + def make_resolver( + cls, + preparer: RequirementPreparer, + finder: PackageFinder, + options: Values, + wheel_cache: Optional[WheelCache] = None, + use_user_site: bool = False, + ignore_installed: bool = True, + ignore_requires_python: bool = False, + force_reinstall: bool = False, + upgrade_strategy: str = "to-satisfy-only", + use_pep517: Optional[bool] = None, + py_version_info: Optional[Tuple[int, ...]] = None, + ) -> BaseResolver: + """ + Create a Resolver instance for the given parameters. + """ + make_install_req = partial( + install_req_from_req_string, + isolated=options.isolated_mode, + use_pep517=use_pep517, + ) + resolver_variant = cls.determine_resolver_variant(options) + # The long import name and duplicated invocation is needed to convince + # Mypy into correctly typechecking. Otherwise it would complain the + # "Resolver" class being redefined. + if resolver_variant == "resolvelib": + import pip._internal.resolution.resolvelib.resolver + + return pip._internal.resolution.resolvelib.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + import pip._internal.resolution.legacy.resolver + + return pip._internal.resolution.legacy.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + + def get_requirements( + self, + args: List[str], + options: Values, + finder: PackageFinder, + session: PipSession, + ) -> List[InstallRequirement]: + """ + Parse command-line arguments into the corresponding requirements. + """ + requirements: List[InstallRequirement] = [] + for filename in options.constraints: + for parsed_req in parse_requirements( + filename, + constraint=True, + finder=finder, + options=options, + session=session, + ): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + user_supplied=False, + ) + requirements.append(req_to_add) + + for req in args: + req_to_add = install_req_from_line( + req, + comes_from=None, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + user_supplied=True, + config_settings=getattr(options, "config_settings", None), + ) + requirements.append(req_to_add) + + for req in options.editables: + req_to_add = install_req_from_editable( + req, + user_supplied=True, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + config_settings=getattr(options, "config_settings", None), + ) + requirements.append(req_to_add) + + # NOTE: options.require_hashes may be set if --require-hashes is True + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, finder=finder, options=options, session=session + ): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + user_supplied=True, + config_settings=parsed_req.options.get("config_settings") + if parsed_req.options + else None, + ) + requirements.append(req_to_add) + + # If any requirement has hash options, enable hash checking. + if any(req.has_hash_options for req in requirements): + options.require_hashes = True + + if not (args or options.editables or options.requirements): + opts = {"name": self.name} + if options.find_links: + raise CommandError( + "You must give at least one requirement to {name} " + '(maybe you meant "pip {name} {links}"?)'.format( + **dict(opts, links=" ".join(options.find_links)) + ) + ) + else: + raise CommandError( + "You must give at least one requirement to {name} " + '(see "pip help {name}")'.format(**opts) + ) + + return requirements + + @staticmethod + def trace_basic_info(finder: PackageFinder) -> None: + """ + Trace basic information about the provided objects. + """ + # Display where finder is looking for packages + search_scope = finder.search_scope + locations = search_scope.get_formatted_locations() + if locations: + logger.info(locations) + + def _build_package_finder( + self, + options: Values, + session: PipSession, + target_python: Optional[TargetPython] = None, + ignore_requires_python: Optional[bool] = None, + ) -> PackageFinder: + """ + Create a package finder appropriate to this requirement command. + + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + link_collector = LinkCollector.create(session, options=options) + selection_prefs = SelectionPreferences( + allow_yanked=True, + format_control=options.format_control, + allow_all_prereleases=options.pre, + prefer_binary=options.prefer_binary, + ignore_requires_python=ignore_requires_python, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + target_python=target_python, + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py new file mode 100644 index 0000000..cf2b976 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py @@ -0,0 +1,159 @@ +import contextlib +import itertools +import logging +import sys +import time +from typing import IO, Generator, Optional + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation + +logger = logging.getLogger(__name__) + + +class SpinnerInterface: + def spin(self) -> None: + raise NotImplementedError() + + def finish(self, final_status: str) -> None: + raise NotImplementedError() + + +class InteractiveSpinner(SpinnerInterface): + def __init__( + self, + message: str, + file: Optional[IO[str]] = None, + spin_chars: str = "-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds: float = 0.125, + ): + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status: str) -> None: + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self) -> None: + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status: str) -> None: + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(SpinnerInterface): + def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None: + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status: str) -> None: + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self) -> None: + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status: str) -> None: + if self._finished: + return + self._update(f"finished with status '{final_status}'") + self._finished = True + + +class RateLimiter: + def __init__(self, min_update_interval_seconds: float) -> None: + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update: float = 0 + + def ready(self) -> bool: + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self) -> None: + self._last_update = time.time() + + +@contextlib.contextmanager +def open_spinner(message: str) -> Generator[SpinnerInterface, None, None]: + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner: SpinnerInterface = InteractiveSpinner(message) + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") + + +HIDE_CURSOR = "\x1b[?25l" +SHOW_CURSOR = "\x1b[?25h" + + +@contextlib.contextmanager +def hidden_cursor(file: IO[str]) -> Generator[None, None, None]: + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py b/.venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000..5e29502 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/cli/status_codes.py @@ -0,0 +1,6 @@ +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000..858a410 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,132 @@ +""" +Package containing all pip commands +""" + +import importlib +from collections import namedtuple +from typing import Any, Dict, Optional + +from pip._internal.cli.base_command import Command + +CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary") + +# This dictionary does a bunch of heavy lifting for help output: +# - Enables avoiding additional (costly) imports for presenting `--help`. +# - The ordering matters for help display. +# +# Even though the module path starts with the same "pip._internal.commands" +# prefix, the full path makes testing easier (specifically when modifying +# `commands_dict` in test setup / teardown). +commands_dict: Dict[str, CommandInfo] = { + "install": CommandInfo( + "pip._internal.commands.install", + "InstallCommand", + "Install packages.", + ), + "download": CommandInfo( + "pip._internal.commands.download", + "DownloadCommand", + "Download packages.", + ), + "uninstall": CommandInfo( + "pip._internal.commands.uninstall", + "UninstallCommand", + "Uninstall packages.", + ), + "freeze": CommandInfo( + "pip._internal.commands.freeze", + "FreezeCommand", + "Output installed packages in requirements format.", + ), + "inspect": CommandInfo( + "pip._internal.commands.inspect", + "InspectCommand", + "Inspect the python environment.", + ), + "list": CommandInfo( + "pip._internal.commands.list", + "ListCommand", + "List installed packages.", + ), + "show": CommandInfo( + "pip._internal.commands.show", + "ShowCommand", + "Show information about installed packages.", + ), + "check": CommandInfo( + "pip._internal.commands.check", + "CheckCommand", + "Verify installed packages have compatible dependencies.", + ), + "config": CommandInfo( + "pip._internal.commands.configuration", + "ConfigurationCommand", + "Manage local and global configuration.", + ), + "search": CommandInfo( + "pip._internal.commands.search", + "SearchCommand", + "Search PyPI for packages.", + ), + "cache": CommandInfo( + "pip._internal.commands.cache", + "CacheCommand", + "Inspect and manage pip's wheel cache.", + ), + "index": CommandInfo( + "pip._internal.commands.index", + "IndexCommand", + "Inspect information available from package indexes.", + ), + "wheel": CommandInfo( + "pip._internal.commands.wheel", + "WheelCommand", + "Build wheels from your requirements.", + ), + "hash": CommandInfo( + "pip._internal.commands.hash", + "HashCommand", + "Compute hashes of package archives.", + ), + "completion": CommandInfo( + "pip._internal.commands.completion", + "CompletionCommand", + "A helper command used for command completion.", + ), + "debug": CommandInfo( + "pip._internal.commands.debug", + "DebugCommand", + "Show information useful for debugging.", + ), + "help": CommandInfo( + "pip._internal.commands.help", + "HelpCommand", + "Show help for commands.", + ), +} + + +def create_command(name: str, **kwargs: Any) -> Command: + """ + Create an instance of the Command class with the given name. + """ + module_path, class_name, summary = commands_dict[name] + module = importlib.import_module(module_path) + command_class = getattr(module, class_name) + command = command_class(name=name, summary=summary, **kwargs) + + return command + + +def get_similar_commands(name: str) -> Optional[str]: + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return None diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c23342796a3cc1da73a2006bb57057933bf5aa39 GIT binary patch literal 4023 zcmaJ^&2!tv6<>fP2#F%8Pg}Mu8@46clr7ToN8HMeU0I4^*|DTpjngX0a3JoIgarc3 z0+dXKn&F{`YBJNt(}Q*BL7nL#9rxg4|AzKrjSfLE>C~Q14!v=zoO0^h1tCMJS&Rtm z{(f(N`}XbI5B)73??mvt`m3moClUG=tN2f}RoItBgr1>~5JoEq3s}^J6#>2>T`YxG zLIT6Wdbku>iIk)jsT5s_ax9|DrH+*j0f~r&u!N(}L5Dpnof^U*N;+`|?!>X@ArJqZ zfV(&v#|fMapxqqp!M(UIfcA5A03X3e1Lz<}kKyBZD1e^e=rBHsPX$ngqo?s1d^UiN zaC8)p;d24>JV#S_9A5~a7dbkC(>N1AFL87dU&dDg=v9th!|&kh0rV#v{VATpHv;HQ zj=qa;;oAZ9XB?fzckoOAo#p6zcn-fGK<{#N9^b?F-=HfoB9SiAnHP^i$VnW3a1*}x z0qMdEB!U-7M_$59zY4D;IJQWlq>EYR!}vo$lT|VGur}Jcq~_Mt0#S0NVXK;<83jev zb)~G80VH?-pBcdtl6f*)fKx)lycQBnubD*b&Z+^>mluibs`5>JA4l-CNik#?PNxi_pco5*Vjo{^)K3xSChQi>Zy5SHOW7zA0xmOSP}U$sPuePT*6o^S zM!xbDU-{4^udAA_vasi=S@K-J0xVMk>0%X$rI`5xXW5)->&)BE92n_@r9$m=+l28= z+#aUsnfu*LOe;mAmkISZYo%hrmcXVJwAt>>Q`Kt(;lX6JQfM0<&bnQ*%*gLy?0*&- z^pLOUybs=>0A~gZ5cttPKtMu(;O*-k<+`j<&|3&fyydu+#q4AWvqQp_);3gHuw01} zyF!hez)qyrBf&QYzx^lhdWP&4j8Gl1^X^l)k!*^aq0O+}x|<;MIDBAqP^gQ~TdJE8 z+rKZNdZZrm?>d{3i0Tp+>!=>a!tcep@JHb{!e+D{T}G);ZLU*+XND3Lrd>6>y~D^6 zHur1;ZH?SJ`;-mME^@d4xnL-4lxZ%I66r;7%8jeFmKWt=*W{G4h z)q<4DEK&0@oC;Rvp}A_>TF%NmFpYvaduL+Wva|v}8!~Aa)p`be&3FgLmGtE0jHTIR z!at=mknx#=V?N_|oXG;F+1YHmTy+yU9?oowq&L8;68x-t@M@s%Wpw`9i=XeFo7xSJ zH12y6#S^k2r{ z)p-9^thceS4{GR8Qbhn@>9Wt#>6v#`MkQfW5Fo7{GxkT%^HzNmuE=$vj%=2e{CV8r zm)?AR>x0x1>mrTRMJ%wCT}IVVDqK76XADaYMXlK8M9!oXLZ7DW`VDuj9+-HTInyFp zIO<`mwH8tlH;T1<9zsuVft4H4%?(1m6bi4As+9_PS;EQ1#Rd&iPN!JYekN~e zCD?PRKL-|_0jdf=>m7JC(0Ad~t61{4Q(IGC-fYai9vIuP|NPlE6SsFKZtqP@?+x5( zEWC~mILYB{bvLQJl$BS>KFaQ@sctu#&0;f`&C<(IcVjtI*I{_DA6k?-;!1Y44BHE3 z>y@&oQx;bD$2UY>ORuUB13yjJ@}(?;y-|lOo4UztRy7P0)(Mk`E?F+|vCv6YVjl5P z{~+J=*2XQ$vW#sC)`t-9W=If(|A?Xx-j|Wk^AB|9pJ;3sjeUnMzfKHp55GKrZ!dAb z5o`875>lcQj&FV8$mbn-(2(aSNPKI}kQT2jbyq-^8;pLFC) zK!PXB9{H(HdTyTe$j>}7e^@r-$re1ae3*RKBR}`Z+F>&7kzaTu{Ld5|Iq literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..18766f1f36780ef925038fdcf6aa469c2b6d2550 GIT binary patch literal 9732 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 MocM1FYqIkH0fmvRmjD0& literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..b46894ddaaf464a3173456e2d6e306f8cf6ae58a GIT binary patch literal 2111 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 z zKlvc^LGi=VuSyU5laKl@{V{g=@xcC@lb^G$JP_a9-R2) zsh^+vp!VqaxH z*^bI`$y6&k(lJ?nv!dvYk+3YQW*)i+AhF}o$>^IExG>9@-q}GwVwi#$p4qTb7PAAe zk?c4$-xCn-6gV)m2$haZZ2pNNCOv8`|VdV&Y;p4bxLb8pwZ z{Ch7ry>qK&}IjzD^>pJS+o9kN!+P>j~qISdC(+B;y={#N1AY9O&*in29;Co|A2U-cPko+N4ENVVv()GQK0th e9*F$&qHk=E|Dn+TwCel1@4(&gzXkX>ll>RZjv65V literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..5f007d7b7a55da52ee22ed60d0f7df42e5f6cd03 GIT binary patch literal 13233 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..036351f7c59e107cbf891b790c56d928369c9079 GIT binary patch literal 4437 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 DyNSbx literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..01604680b1da486508425b8b9e833d52b9b4394e GIT binary patch literal 1694 zcmZux&2Jk;6rc5e+DYuhalX~CO$v>X;-#qs9GZ$WL=*`kAW*5ktTww7XWjkk%sPoJ zTU02AfRLc41XYL}UFDL0p&a3oDwSwfL<*uDxET@z%88j>Zxbp;tM`8I?VC4me)DF2 z?CMGZGLC*om=*`XuhfVK-v-CG2-pM$Fc=HiOp#$IPFM+>EwUkIEzahPe26(qu*ISn zV&0PMWHHHr1b7D+!a6WS$nUUG-eRgM<&(ctBhLiFEzPPxOc3{;iG8BQS=Y8T$DnZG zliAt1g#`jT+uk{ZT%<>tA}O?u<0%3*0R%ut$=BNc>mLWA>-nbZ z;DFOm2?qj#eg!#td$(b6lu>FQUt&T=8NwoAw87em24}Mk!eA2ANy)UlhENyk>WSMVVil~h zx4|lt7w1uegyz|xD?S0$Fm*pj>j-K-RNLMlSAxEZP203I6gzRPr{dgk+*Kz&K6&hv z8LRc!T6Gc1RktGBP;-2Ftj5Zda=s(z_e`&#nvM^VqgjP`Nt7lvAj%x-CL&0^>lkn? zY(YfZE)X=&gN}hjYH(%ObOOQC5QZp7d5GjwX(Q!R0f(WrgeVuNgNhdtOS)~Sj%LGv zFOl1T1J=f+K#c34^P=vf@)Rq}t_>BeVRBED50G1ix{sBQ+(p+nb*#MaIwkk*8<1_AV5fm1b%8!@ZF=o^-$YEIYbY`L6a& ztvUYS)BUWn*QxxHmA41?v#)N5Po>OP>Ce+YNf#eW7oSMu&occR>3>dM0^P%Z0wy`~ zbMNpM^DUO3Q%qa_OMYe=Jetnj$gtn186xM&(MxJbys8E%RkdBCVnKpCRrPK~v*?i! zOH@_E)d}D$m~a*J5?WmZRRr%=Orj~dh+c3+-ck^-*hc|NP9KdD^C37U5}*rIBx6AV zevOEAaKOnej_De@H+M8Vy2lN)xFLRE>-82Os+nu*4~Igyi%+NrO~fg}>dE61H5^?& z5d5lVI;E(L(HEN9G7F0uetvZjeU*a$3BtbSS1`Fu2ED yE3A!f^i;YQad{mTh)JI?EE7RagJBNA$RQZ`4NM)<@5+fFGXq~={tFNlX80EuF1Y#t literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/index.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4aa5ecc3103dbdc15906372b6b65c28883689bf GIT binary patch literal 6741 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/inspect.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15f32e6d2ce82b8818eb6d407f1a55a6d067b86b GIT binary patch literal 3996 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�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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..9f7e4cd833be3a736bb0d2eaf9eab16e192436c0 GIT binary patch literal 15677 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..49258b01e4fb4ea9e400d5ea35e87f2f18f718d2 GIT binary patch literal 7642 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/show.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..217f4d9be3ba40b7611a134e11ef5f514168d586 GIT binary patch literal 9749 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5a5d69ce773f9c330438b0b65c9b119c03412af GIT binary patch literal 4747 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ead22d484793276da7b90776ed23d5173f6a6fbf GIT binary patch literal 8977 zcmcIqOKclQnr^%$)Qy0<~*qBdoasMr_->pkpE6MJ?R*~|V_-7V@h zo}C~fpzgowe^vceUG@D{`du^{65#jZYj@#FmmvHHD>y%&O}zSVAnppPpt>|6XT4oH z7caYWZg{&jPtlw6x){%+`HKFWpVM9~P!w|_r+r$m7|Mk>?bpJ^NG?*0=AyhD&{~SI zT#VDA7B41p2~G#K)?zZ3)puATK#DI6``Yb})IVC@fSSepr^|DdWOC;l_tskIIDg}&+sAQbei$$fR(#WlX zF)Qaaq?9UU&v1Kk`tYauAW{@G@s1!6+Hn5Vvi7>NysYDD#(>9o< zd%=1BrmUeECBGmOWK_y>c|mSAB!o(&f|0BiFl{O6xTt7_MI@IMV;1~GKSagyd1zk=ibtP{_$a)6s!mBet+=XDXsR7f%mGh|XoL3F1 zzR$clAM&ey6i@?5L_wHoFi}uw+OLYAc^}#aa^VGUCPX7vSS?S=9T=mK1i!N^8WQJ8 z3eL|K<`I#gWzvFP!4htUn@A9BNkwH;Qc)>iKx8z;Em*n*;7hDu#z+|=tIQx~rKlJO zxt=~fjWCgnStKdyyizilfIcmibb}j#Q0c{TK`WGIEYC!rHf|{xNoobNN;Hq~f>f}E zYE-%iEiCGcjSQurk%JOJh?(cscvK}-&yz`M)-cLsGMnXfqZlbFNHNxELC>O6R#OZ_ zjI7m{!E&K2amhicLLd^X!F;qNvOo+}gccYGPb+!o2Lb0Kj|(@DD$OE{Cb@6NA6ObK zFO(JA4NS2c8p>h>ae(llEVnvhS+%_{{*hadq=7QlXQZFn)9q){)P`}8NEzh|(*=Z2 zOQWNse@0^jZ&HFGEokUX3O^k|;AcKhB=9{46IZHW?Jp~6dS21iRoL`@lg^Yq!)DWL8ORbkGa&l_KJ_M)<}PIakn)uVb1uTwkccc|uD z^*!=GwCi1eg>@t7*qGx>yi>B?&Nuha?#cJG9NBD@m3|=0ExTqpzH63Scg=EY*DSa1 zn&r-2v)sLFmiO$M<=$PhEbW@*zFo82|HE2V1F8r(Bcz5OMIPF)^?R;p2Y%R=ZCHhW zv}=}!ept(Dl*h8=QS70;Gkwok9{m5bYeR%ax62!BcjdpiLk`t^!(KbyEIHIew?!?v zcIM9=btIc_HC}Z&dc3zg*!?qlJMQ;QTMI%aadpu>a%(X>GQttt2*ct*PI6f8f%4)N z3*Yiu!oZs@O+o$XgAC;Xd}R1eMbk<}GbDhpteCH0Kxl?^OUE}E@&s`U$B$;b)C-W$ zpn)Qw&UppXfUQftvq&qmrj+y%K&pl51sW{rvQ35ve?tubk>*%6yWTcGI;>VXtd99)%0AFYXBS?P;U{Vgz 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 can be a glob expression or a package name. + """ + + ignore_require_venv = True + usage = """ + %prog dir + %prog info + %prog list [] [--format=[human, abspath]] + %prog remove + %prog purge + """ + + def add_options(self) -> None: + self.cmd_opts.add_option( + "--format", + action="store", + dest="list_format", + default="human", + choices=("human", "abspath"), + help="Select the output format among: human (default) or abspath", + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + handlers = { + "dir": self.get_cache_dir, + "info": self.get_cache_info, + "list": self.list_cache_items, + "remove": self.remove_cache_items, + "purge": self.purge_cache, + } + + if not options.cache_dir: + logger.error("pip cache commands can not function since cache is disabled.") + return ERROR + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def get_cache_dir(self, options: Values, args: List[Any]) -> None: + if args: + raise CommandError("Too many arguments") + + logger.info(options.cache_dir) + + def get_cache_info(self, options: Values, args: List[Any]) -> None: + if args: + raise CommandError("Too many arguments") + + num_http_files = len(self._find_http_files(options)) + num_packages = len(self._find_wheels(options, "*")) + + http_cache_location = self._cache_dir(options, "http-v2") + old_http_cache_location = self._cache_dir(options, "http") + wheels_cache_location = self._cache_dir(options, "wheels") + http_cache_size = filesystem.format_size( + filesystem.directory_size(http_cache_location) + + filesystem.directory_size(old_http_cache_location) + ) + wheels_cache_size = filesystem.format_directory_size(wheels_cache_location) + + message = ( + textwrap.dedent( + """ + Package index page cache location (pip v23.3+): {http_cache_location} + Package index page cache location (older pips): {old_http_cache_location} + Package index page cache size: {http_cache_size} + Number of HTTP files: {num_http_files} + Locally built wheels location: {wheels_cache_location} + Locally built wheels size: {wheels_cache_size} + Number of locally built wheels: {package_count} + """ # noqa: E501 + ) + .format( + http_cache_location=http_cache_location, + old_http_cache_location=old_http_cache_location, + http_cache_size=http_cache_size, + num_http_files=num_http_files, + wheels_cache_location=wheels_cache_location, + package_count=num_packages, + wheels_cache_size=wheels_cache_size, + ) + .strip() + ) + + logger.info(message) + + def list_cache_items(self, options: Values, args: List[Any]) -> None: + if len(args) > 1: + raise CommandError("Too many arguments") + + if args: + pattern = args[0] + else: + pattern = "*" + + files = self._find_wheels(options, pattern) + if options.list_format == "human": + self.format_for_human(files) + else: + self.format_for_abspath(files) + + def format_for_human(self, files: List[str]) -> None: + if not files: + logger.info("No locally built wheels cached.") + return + + results = [] + for filename in files: + wheel = os.path.basename(filename) + size = filesystem.format_file_size(filename) + results.append(f" - {wheel} ({size})") + logger.info("Cache contents:\n") + logger.info("\n".join(sorted(results))) + + def format_for_abspath(self, files: List[str]) -> None: + if files: + logger.info("\n".join(sorted(files))) + + def remove_cache_items(self, options: Values, args: List[Any]) -> None: + if len(args) > 1: + raise CommandError("Too many arguments") + + if not args: + raise CommandError("Please provide a pattern") + + files = self._find_wheels(options, args[0]) + + no_matching_msg = "No matching packages" + if args[0] == "*": + # Only fetch http files if no specific pattern given + files += self._find_http_files(options) + else: + # Add the pattern to the log message + no_matching_msg += f' for pattern "{args[0]}"' + + if not files: + logger.warning(no_matching_msg) + + for filename in files: + os.unlink(filename) + logger.verbose("Removed %s", filename) + logger.info("Files removed: %s", len(files)) + + def purge_cache(self, options: Values, args: List[Any]) -> None: + if args: + raise CommandError("Too many arguments") + + return self.remove_cache_items(options, ["*"]) + + def _cache_dir(self, options: Values, subdir: str) -> str: + return os.path.join(options.cache_dir, subdir) + + def _find_http_files(self, options: Values) -> List[str]: + old_http_dir = self._cache_dir(options, "http") + new_http_dir = self._cache_dir(options, "http-v2") + return filesystem.find_files(old_http_dir, "*") + filesystem.find_files( + new_http_dir, "*" + ) + + def _find_wheels(self, options: Values, pattern: str) -> List[str]: + wheel_dir = self._cache_dir(options, "wheels") + + # The wheel filename format, as specified in PEP 427, is: + # {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl + # + # Additionally, non-alphanumeric values in the distribution are + # normalized to underscores (_), meaning hyphens can never occur + # before `-{version}`. + # + # Given that information: + # - If the pattern we're given contains a hyphen (-), the user is + # providing at least the version. Thus, we can just append `*.whl` + # to match the rest of it. + # - If the pattern we're given doesn't contain a hyphen (-), the + # user is only providing the name. Thus, we append `-*.whl` to + # match the hyphen before the version, followed by anything else. + # + # PEP 427: https://www.python.org/dev/peps/pep-0427/ + pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl") + + return filesystem.find_files(wheel_dir, pattern) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/check.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000..5efd0a3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,54 @@ +import logging +from optparse import Values +from typing import List + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.operations.check import ( + check_package_set, + create_package_set_from_installed, + warn_legacy_versions_and_specifiers, +) +from pip._internal.utils.misc import write_output + +logger = logging.getLogger(__name__) + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + + usage = """ + %prog [options]""" + + def run(self, options: Values, args: List[str]) -> int: + package_set, parsing_probs = create_package_set_from_installed() + warn_legacy_versions_and_specifiers(package_set) + missing, conflicting = check_package_set(package_set) + + for project_name in missing: + version = package_set[project_name].version + for dependency in missing[project_name]: + write_output( + "%s %s requires %s, which is not installed.", + project_name, + version, + dependency[0], + ) + + for project_name in conflicting: + version = package_set[project_name].version + for dep_name, dep_version, req in conflicting[project_name]: + write_output( + "%s %s has requirement %s, but you have %s %s.", + project_name, + version, + req, + dep_name, + dep_version, + ) + + if missing or conflicting or parsing_probs: + return ERROR + else: + write_output("No broken requirements found.") + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000..9e89e27 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,130 @@ +import sys +import textwrap +from optparse import Values +from typing import List + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.misc import get_prog + +BASE_COMPLETION = """ +# pip {shell} completion start{script}# pip {shell} completion end +""" + +COMPLETION_SCRIPTS = { + "bash": """ + _pip_completion() + {{ + COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 2>/dev/null ) ) + }} + complete -o default -F _pip_completion {prog} + """, + "zsh": """ + #compdef -P pip[0-9.]# + __pip() {{ + compadd $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$((CURRENT-1)) \\ + PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null ) + }} + if [[ $zsh_eval_context[-1] == loadautofunc ]]; then + # autoload from fpath, call function directly + __pip "$@" + else + # eval/source/. command, register function for later + compdef __pip -P 'pip[0-9.]#' + fi + """, + "fish": """ + function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) + end + complete -fa "(__fish_complete_pip)" -c {prog} + """, + "powershell": """ + if ((Test-Path Function:\\TabExpansion) -and -not ` + (Test-Path Function:\\_pip_completeBackup)) {{ + Rename-Item Function:\\TabExpansion _pip_completeBackup + }} + function TabExpansion($line, $lastWord) {{ + $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart() + if ($lastBlock.StartsWith("{prog} ")) {{ + $Env:COMP_WORDS=$lastBlock + $Env:COMP_CWORD=$lastBlock.Split().Length - 1 + $Env:PIP_AUTO_COMPLETE=1 + (& {prog}).Split() + Remove-Item Env:COMP_WORDS + Remove-Item Env:COMP_CWORD + Remove-Item Env:PIP_AUTO_COMPLETE + }} + elseif (Test-Path Function:\\_pip_completeBackup) {{ + # Fall back on existing tab expansion + _pip_completeBackup $line $lastWord + }} + }} + """, +} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + + ignore_require_venv = True + + def add_options(self) -> None: + self.cmd_opts.add_option( + "--bash", + "-b", + action="store_const", + const="bash", + dest="shell", + help="Emit completion code for bash", + ) + self.cmd_opts.add_option( + "--zsh", + "-z", + action="store_const", + const="zsh", + dest="shell", + help="Emit completion code for zsh", + ) + self.cmd_opts.add_option( + "--fish", + "-f", + action="store_const", + const="fish", + dest="shell", + help="Emit completion code for fish", + ) + self.cmd_opts.add_option( + "--powershell", + "-p", + action="store_const", + const="powershell", + dest="shell", + help="Emit completion code for powershell", + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ["--" + shell for shell in sorted(shells)] + if options.shell in shells: + script = textwrap.dedent( + COMPLETION_SCRIPTS.get(options.shell, "").format(prog=get_prog()) + ) + print(BASE_COMPLETION.format(script=script, shell=options.shell)) + return SUCCESS + else: + sys.stderr.write( + "ERROR: You must pass {}\n".format(" or ".join(shell_options)) + ) + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000..1a1dc6b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,280 @@ +import logging +import os +import subprocess +from optparse import Values +from typing import Any, List, Optional + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.configuration import ( + Configuration, + Kind, + get_configuration_files, + kinds, +) +from pip._internal.exceptions import PipError +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_prog, write_output + +logger = logging.getLogger(__name__) + + +class ConfigurationCommand(Command): + """ + Manage local and global configuration. + + Subcommands: + + - list: List the active configuration (or from the file specified) + - edit: Edit the configuration file in an editor + - get: Get the value associated with command.option + - set: Set the command.option=value + - unset: Unset the value associated with command.option + - debug: List the configuration files and values defined under them + + Configuration keys should be dot separated command and option name, + with the special prefix "global" affecting any command. For example, + "pip config set global.index-url https://example.org/" would configure + the index url for all commands, but "pip config set download.timeout 10" + would configure a 10 second timeout only for "pip download" commands. + + If none of --user, --global and --site are passed, a virtual + environment configuration file is used if one is active and the file + exists. Otherwise, all modifications happen to the user file by + default. + """ + + ignore_require_venv = True + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get command.option + %prog [] set command.option value + %prog [] unset command.option + %prog [] debug + """ + + def add_options(self) -> None: + self.cmd_opts.add_option( + "--editor", + dest="editor", + action="store", + default=None, + help=( + "Editor to use to edit the file. Uses VISUAL or EDITOR " + "environment variables if not provided." + ), + ) + + self.cmd_opts.add_option( + "--global", + dest="global_file", + action="store_true", + default=False, + help="Use the system-wide configuration file only", + ) + + self.cmd_opts.add_option( + "--user", + dest="user_file", + action="store_true", + default=False, + help="Use the user configuration file only", + ) + + self.cmd_opts.add_option( + "--site", + dest="site_file", + action="store_true", + default=False, + help="Use the current environment configuration file only", + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + handlers = { + "list": self.list_values, + "edit": self.open_in_editor, + "get": self.get_name, + "set": self.set_name_value, + "unset": self.unset_name, + "debug": self.list_config_values, + } + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Determine which configuration files are to be loaded + # Depends on whether the command is modifying. + try: + load_only = self._determine_file( + options, need_value=(action in ["get", "set", "unset", "edit"]) + ) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + # Load a new configuration + self.configuration = Configuration( + isolated=options.isolated_mode, load_only=load_only + ) + self.configuration.load() + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _determine_file(self, options: Values, need_value: bool) -> Optional[Kind]: + file_options = [ + key + for key, value in ( + (kinds.USER, options.user_file), + (kinds.GLOBAL, options.global_file), + (kinds.SITE, options.site_file), + ) + if value + ] + + if not file_options: + if not need_value: + return None + # Default to user, unless there's a site file. + elif any( + os.path.exists(site_config_file) + for site_config_file in get_configuration_files()[kinds.SITE] + ): + return kinds.SITE + else: + return kinds.USER + elif len(file_options) == 1: + return file_options[0] + + raise PipError( + "Need exactly one file to operate upon " + "(--user, --site, --global) to perform." + ) + + def list_values(self, options: Values, args: List[str]) -> None: + self._get_n_args(args, "list", n=0) + + for key, value in sorted(self.configuration.items()): + write_output("%s=%r", key, value) + + def get_name(self, options: Values, args: List[str]) -> None: + key = self._get_n_args(args, "get [name]", n=1) + value = self.configuration.get_value(key) + + write_output("%s", value) + + def set_name_value(self, options: Values, args: List[str]) -> None: + key, value = self._get_n_args(args, "set [name] [value]", n=2) + self.configuration.set_value(key, value) + + self._save_configuration() + + def unset_name(self, options: Values, args: List[str]) -> None: + key = self._get_n_args(args, "unset [name]", n=1) + self.configuration.unset_value(key) + + self._save_configuration() + + def list_config_values(self, options: Values, args: List[str]) -> None: + """List config key-value pairs across different config files""" + self._get_n_args(args, "debug", n=0) + + self.print_env_var_values() + # Iterate over config files and print if they exist, and the + # key-value pairs present in them if they do + for variant, files in sorted(self.configuration.iter_config_files()): + write_output("%s:", variant) + for fname in files: + with indent_log(): + file_exists = os.path.exists(fname) + write_output("%s, exists: %r", fname, file_exists) + if file_exists: + self.print_config_file_values(variant) + + def print_config_file_values(self, variant: Kind) -> None: + """Get key-value pairs from the file of a variant""" + for name, value in self.configuration.get_values_in_config(variant).items(): + with indent_log(): + write_output("%s: %s", name, value) + + def print_env_var_values(self) -> None: + """Get key-values pairs present as environment variables""" + write_output("%s:", "env_var") + with indent_log(): + for key, value in sorted(self.configuration.get_environ_vars()): + env_var = f"PIP_{key.upper()}" + write_output("%s=%r", env_var, value) + + def open_in_editor(self, options: Values, args: List[str]) -> None: + editor = self._determine_editor(options) + + fname = self.configuration.get_file_to_edit() + if fname is None: + raise PipError("Could not determine appropriate file.") + elif '"' in fname: + # This shouldn't happen, unless we see a username like that. + # If that happens, we'd appreciate a pull request fixing this. + raise PipError( + f'Can not open an editor for a file name containing "\n{fname}' + ) + + try: + subprocess.check_call(f'{editor} "{fname}"', shell=True) + except FileNotFoundError as e: + if not e.filename: + e.filename = editor + raise + except subprocess.CalledProcessError as e: + raise PipError(f"Editor Subprocess exited with exit code {e.returncode}") + + def _get_n_args(self, args: List[str], example: str, n: int) -> Any: + """Helper to make sure the command got the right number of arguments""" + if len(args) != n: + msg = ( + f"Got unexpected number of arguments, expected {n}. " + f'(example: "{get_prog()} config {example}")' + ) + raise PipError(msg) + + if n == 1: + return args[0] + else: + return args + + def _save_configuration(self) -> None: + # We successfully ran a modifying command. Need to save the + # configuration. + try: + self.configuration.save() + except Exception: + logger.exception( + "Unable to save configuration. Please report this as a bug." + ) + raise PipError("Internal Error.") + + def _determine_editor(self, options: Values) -> str: + if options.editor is not None: + return options.editor + elif "VISUAL" in os.environ: + return os.environ["VISUAL"] + elif "EDITOR" in os.environ: + return os.environ["EDITOR"] + else: + raise PipError("Could not determine editor to use.") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py new file mode 100644 index 0000000..7e5271c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/debug.py @@ -0,0 +1,201 @@ +import importlib.resources +import locale +import logging +import os +import sys +from optparse import Values +from types import ModuleType +from typing import Any, Dict, List, Optional + +import pip._vendor +from pip._vendor.certifi import where +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.configuration import Configuration +from pip._internal.metadata import get_environment +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_pip_version + +logger = logging.getLogger(__name__) + + +def show_value(name: str, value: Any) -> None: + logger.info("%s: %s", name, value) + + +def show_sys_implementation() -> None: + logger.info("sys.implementation:") + implementation_name = sys.implementation.name + with indent_log(): + show_value("name", implementation_name) + + +def create_vendor_txt_map() -> Dict[str, str]: + with importlib.resources.open_text("pip._vendor", "vendor.txt") as f: + # Purge non version specifying lines. + # Also, remove any space prefix or suffixes (including comments). + lines = [ + line.strip().split(" ", 1)[0] for line in f.readlines() if "==" in line + ] + + # Transform into "module" -> version dict. + return dict(line.split("==", 1) for line in lines) + + +def get_module_from_module_name(module_name: str) -> Optional[ModuleType]: + # Module name can be uppercase in vendor.txt for some reason... + module_name = module_name.lower().replace("-", "_") + # PATCH: setuptools is actually only pkg_resources. + if module_name == "setuptools": + module_name = "pkg_resources" + + try: + __import__(f"pip._vendor.{module_name}", globals(), locals(), level=0) + return getattr(pip._vendor, module_name) + except ImportError: + # We allow 'truststore' to fail to import due + # to being unavailable on Python 3.9 and earlier. + if module_name == "truststore" and sys.version_info < (3, 10): + return None + raise + + +def get_vendor_version_from_module(module_name: str) -> Optional[str]: + module = get_module_from_module_name(module_name) + version = getattr(module, "__version__", None) + + if module and not version: + # Try to find version in debundled module info. + assert module.__file__ is not None + env = get_environment([os.path.dirname(module.__file__)]) + dist = env.get_distribution(module_name) + if dist: + version = str(dist.version) + + return version + + +def show_actual_vendor_versions(vendor_txt_versions: Dict[str, str]) -> None: + """Log the actual version and print extra info if there is + a conflict or if the actual version could not be imported. + """ + for module_name, expected_version in vendor_txt_versions.items(): + extra_message = "" + actual_version = get_vendor_version_from_module(module_name) + if not actual_version: + extra_message = ( + " (Unable to locate actual module version, using" + " vendor.txt specified version)" + ) + actual_version = expected_version + elif parse_version(actual_version) != parse_version(expected_version): + extra_message = ( + " (CONFLICT: vendor.txt suggests version should" + f" be {expected_version})" + ) + logger.info("%s==%s%s", module_name, actual_version, extra_message) + + +def show_vendor_versions() -> None: + logger.info("vendored library versions:") + + vendor_txt_versions = create_vendor_txt_map() + with indent_log(): + show_actual_vendor_versions(vendor_txt_versions) + + +def show_tags(options: Values) -> None: + tag_limit = 10 + + target_python = make_target_python(options) + tags = target_python.get_sorted_tags() + + # Display the target options that were explicitly provided. + formatted_target = target_python.format_given() + suffix = "" + if formatted_target: + suffix = f" (target: {formatted_target})" + + msg = f"Compatible tags: {len(tags)}{suffix}" + logger.info(msg) + + if options.verbose < 1 and len(tags) > tag_limit: + tags_limited = True + tags = tags[:tag_limit] + else: + tags_limited = False + + with indent_log(): + for tag in tags: + logger.info(str(tag)) + + if tags_limited: + msg = f"...\n[First {tag_limit} tags shown. Pass --verbose to show all.]" + logger.info(msg) + + +def ca_bundle_info(config: Configuration) -> str: + levels = {key.split(".", 1)[0] for key, _ in config.items()} + if not levels: + return "Not specified" + + levels_that_override_global = ["install", "wheel", "download"] + global_overriding_level = [ + level for level in levels if level in levels_that_override_global + ] + if not global_overriding_level: + return "global" + + if "global" in levels: + levels.remove("global") + return ", ".join(levels) + + +class DebugCommand(Command): + """ + Display debug information. + """ + + usage = """ + %prog """ + ignore_require_venv = True + + def add_options(self) -> None: + cmdoptions.add_target_python_options(self.cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) + self.parser.config.load() + + def run(self, options: Values, args: List[str]) -> int: + logger.warning( + "This command is only meant for debugging. " + "Do not use this with automation for parsing and getting these " + "details, since the output and options of this command may " + "change without notice." + ) + show_value("pip version", get_pip_version()) + show_value("sys.version", sys.version) + show_value("sys.executable", sys.executable) + show_value("sys.getdefaultencoding", sys.getdefaultencoding()) + show_value("sys.getfilesystemencoding", sys.getfilesystemencoding()) + show_value( + "locale.getpreferredencoding", + locale.getpreferredencoding(), + ) + show_value("sys.platform", sys.platform) + show_sys_implementation() + + show_value("'cert' config value", ca_bundle_info(self.parser.config)) + show_value("REQUESTS_CA_BUNDLE", os.environ.get("REQUESTS_CA_BUNDLE")) + show_value("CURL_CA_BUNDLE", os.environ.get("CURL_CA_BUNDLE")) + show_value("pip._vendor.certifi.where()", where()) + show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED) + + show_vendor_versions() + + show_tags(options) + + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/download.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000..54247a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,147 @@ +import logging +import os +from optparse import Values +from typing import List + +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.operations.build.build_tracker import get_build_tracker +from pip._internal.req.req_install import check_legacy_setup_py_options +from pip._internal.utils.misc import ensure_dir, normalize_path, write_output +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] ... + %prog [options] ... + %prog [options] ...""" + + def add_options(self) -> None: + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.pre()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.check_build_deps()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + + self.cmd_opts.add_option( + "-d", + "--dest", + "--destination-dir", + "--destination-directory", + dest="download_dir", + metavar="dir", + default=os.curdir, + help="Download packages into

.", + ) + + cmdoptions.add_target_python_options(self.cmd_opts) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options: Values, args: List[str]) -> int: + options.ignore_installed = True + # editable doesn't really make sense for `pip download`, but the bowels + # of the RequirementSet code require that property. + options.editables = [] + + cmdoptions.check_dist_restriction(options) + + options.download_dir = normalize_path(options.download_dir) + ensure_dir(options.download_dir) + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=options.ignore_requires_python, + ) + + build_tracker = self.enter_context(get_build_tracker()) + + directory = TempDirectory( + delete=not options.no_clean, + kind="download", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + check_legacy_setup_py_options(options, reqs) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + build_tracker=build_tracker, + session=session, + finder=finder, + download_dir=options.download_dir, + use_user_site=False, + verbosity=self.verbosity, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + ignore_requires_python=options.ignore_requires_python, + use_pep517=options.use_pep517, + py_version_info=options.python_version, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve(reqs, check_supported_wheels=True) + + downloaded: List[str] = [] + for req in requirement_set.requirements.values(): + if req.satisfied_by is None: + assert req.name is not None + preparer.save_linked_requirement(req) + downloaded.append(req.name) + + preparer.prepare_linked_requirements_more(requirement_set.requirements.values()) + requirement_set.warn_legacy_versions_and_specifiers() + + if downloaded: + write_output("Successfully downloaded %s", " ".join(downloaded)) + + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000..e64cb3d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,109 @@ +import sys +from optparse import Values +from typing import AbstractSet, List + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.operations.freeze import freeze +from pip._internal.utils.compat import stdlib_pkgs + + +def _should_suppress_build_backends() -> bool: + return sys.version_info < (3, 12) + + +def _dev_pkgs() -> AbstractSet[str]: + pkgs = {"pip"} + + if _should_suppress_build_backends(): + pkgs |= {"setuptools", "distribute", "wheel"} + pkgs |= {"setuptools", "distribute", "wheel", "pkg-resources"} + + return pkgs + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + + usage = """ + %prog [options]""" + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-r", + "--requirement", + dest="requirements", + action="append", + default=[], + metavar="file", + help=( + "Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times." + ), + ) + self.cmd_opts.add_option( + "-l", + "--local", + dest="local", + action="store_true", + default=False, + help=( + "If in a virtualenv that has global access, do not output " + "globally-installed packages." + ), + ) + self.cmd_opts.add_option( + "--user", + dest="user", + action="store_true", + default=False, + help="Only output packages installed in user-site.", + ) + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + "--all", + dest="freeze_all", + action="store_true", + help=( + "Do not skip these packages in the output:" + " {}".format(", ".join(_dev_pkgs())) + ), + ) + self.cmd_opts.add_option( + "--exclude-editable", + dest="exclude_editable", + action="store_true", + help="Exclude editable package from output.", + ) + self.cmd_opts.add_option(cmdoptions.list_exclude()) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(_dev_pkgs()) + + if options.excludes: + skip.update(options.excludes) + + cmdoptions.check_list_path_option(options) + + for line in freeze( + requirement=options.requirements, + local_only=options.local, + user_only=options.user, + paths=options.path, + isolated=options.isolated_mode, + skip=skip, + exclude_editable=options.exclude_editable, + ): + sys.stdout.write(line + "\n") + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000..042dac8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,59 @@ +import hashlib +import logging +import sys +from optparse import Values +from typing import List + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES +from pip._internal.utils.misc import read_chunks, write_output + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + """ + + usage = "%prog [options] ..." + ignore_require_venv = True + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-a", + "--algorithm", + dest="algorithm", + choices=STRONG_HASHES, + action="store", + default=FAVORITE_HASH, + help="The hash algorithm to use: one of {}".format( + ", ".join(STRONG_HASHES) + ), + ) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + write_output( + "%s:\n--hash=%s:%s", path, algorithm, _hash_of_file(path, algorithm) + ) + return SUCCESS + + +def _hash_of_file(path: str, algorithm: str) -> str: + """Return the hash digest of a file.""" + with open(path, "rb") as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/help.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000..6206631 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,41 @@ +from optparse import Values +from typing import List + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError + + +class HelpCommand(Command): + """Show help for commands""" + + usage = """ + %prog """ + ignore_require_venv = True + + def run(self, options: Values, args: List[str]) -> int: + from pip._internal.commands import ( + commands_dict, + create_command, + get_similar_commands, + ) + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = [f'unknown command "{cmd_name}"'] + if guess: + msg.append(f'maybe you meant "{guess}"') + + raise CommandError(" - ".join(msg)) + + command = create_command(cmd_name) + command.parser.print_help() + + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/index.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/index.py new file mode 100644 index 0000000..f55e9e4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/index.py @@ -0,0 +1,139 @@ +import logging +from optparse import Values +from typing import Any, Iterable, List, Optional, Union + +from pip._vendor.packaging.version import LegacyVersion, Version + +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.commands.search import print_dist_installation_info +from pip._internal.exceptions import CommandError, DistributionNotFound, PipError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.network.session import PipSession +from pip._internal.utils.misc import write_output + +logger = logging.getLogger(__name__) + + +class IndexCommand(IndexGroupCommand): + """ + Inspect information available from package indexes. + """ + + ignore_require_venv = True + usage = """ + %prog versions + """ + + def add_options(self) -> None: + cmdoptions.add_target_python_options(self.cmd_opts) + + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.pre()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + handlers = { + "versions": self.get_available_package_versions, + } + + logger.warning( + "pip index is currently an experimental command. " + "It may be removed/changed in a future release " + "without prior warning." + ) + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _build_package_finder( + self, + options: Values, + session: PipSession, + target_python: Optional[TargetPython] = None, + ignore_requires_python: Optional[bool] = None, + ) -> PackageFinder: + """ + Create a package finder appropriate to the index command. + """ + link_collector = LinkCollector.create(session, options=options) + + # Pass allow_yanked=False to ignore yanked versions. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=options.pre, + ignore_requires_python=ignore_requires_python, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + target_python=target_python, + ) + + def get_available_package_versions(self, options: Values, args: List[Any]) -> None: + if len(args) != 1: + raise CommandError("You need to specify exactly one argument") + + target_python = cmdoptions.make_target_python(options) + query = args[0] + + with self._build_session(options) as session: + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=options.ignore_requires_python, + ) + + versions: Iterable[Union[LegacyVersion, Version]] = ( + candidate.version for candidate in finder.find_all_candidates(query) + ) + + if not options.pre: + # Remove prereleases + versions = ( + version for version in versions if not version.is_prerelease + ) + versions = set(versions) + + if not versions: + raise DistributionNotFound( + f"No matching distribution found for {query}" + ) + + formatted_versions = [str(ver) for ver in sorted(versions, reverse=True)] + latest = formatted_versions[0] + + write_output(f"{query} ({latest})") + write_output("Available versions: {}".format(", ".join(formatted_versions))) + print_dist_installation_info(query, latest) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py new file mode 100644 index 0000000..27c8fa3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/inspect.py @@ -0,0 +1,92 @@ +import logging +from optparse import Values +from typing import Any, Dict, List + +from pip._vendor.packaging.markers import default_environment +from pip._vendor.rich import print_json + +from pip import __version__ +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.metadata import BaseDistribution, get_environment +from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.urls import path_to_url + +logger = logging.getLogger(__name__) + + +class InspectCommand(Command): + """ + Inspect the content of a Python environment and produce a report in JSON format. + """ + + ignore_require_venv = True + usage = """ + %prog [options]""" + + def add_options(self) -> None: + self.cmd_opts.add_option( + "--local", + action="store_true", + default=False, + help=( + "If in a virtualenv that has global access, do not list " + "globally-installed packages." + ), + ) + self.cmd_opts.add_option( + "--user", + dest="user", + action="store_true", + default=False, + help="Only output packages installed in user-site.", + ) + self.cmd_opts.add_option(cmdoptions.list_path()) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + cmdoptions.check_list_path_option(options) + dists = get_environment(options.path).iter_installed_distributions( + local_only=options.local, + user_only=options.user, + skip=set(stdlib_pkgs), + ) + output = { + "version": "1", + "pip_version": __version__, + "installed": [self._dist_to_dict(dist) for dist in dists], + "environment": default_environment(), + # TODO tags? scheme? + } + print_json(data=output) + return SUCCESS + + def _dist_to_dict(self, dist: BaseDistribution) -> Dict[str, Any]: + res: Dict[str, Any] = { + "metadata": dist.metadata_dict, + "metadata_location": dist.info_location, + } + # direct_url. Note that we don't have download_info (as in the installation + # report) since it is not recorded in installed metadata. + direct_url = dist.direct_url + if direct_url is not None: + res["direct_url"] = direct_url.to_dict() + else: + # Emulate direct_url for legacy editable installs. + editable_project_location = dist.editable_project_location + if editable_project_location is not None: + res["direct_url"] = { + "url": path_to_url(editable_project_location), + "dir_info": { + "editable": True, + }, + } + # installer + installer = dist.installer + if dist.installer: + res["installer"] = installer + # requested + if dist.installed_with_dist_info: + res["requested"] = dist.requested + return res diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/install.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000..e944bb9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,774 @@ +import errno +import json +import operator +import os +import shutil +import site +from optparse import SUPPRESS_HELP, Values +from typing import List, Optional + +from pip._vendor.rich import print_json + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import ( + RequirementCommand, + warn_if_run_as_root, + with_cleanup, +) +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import get_scheme +from pip._internal.metadata import get_environment +from pip._internal.models.installation_report import InstallationReport +from pip._internal.operations.build.build_tracker import get_build_tracker +from pip._internal.operations.check import ConflictDetails, check_install_conflicts +from pip._internal.req import install_given_reqs +from pip._internal.req.req_install import ( + InstallRequirement, + check_legacy_setup_py_options, +) +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.filesystem import test_writable_dir +from pip._internal.utils.logging import getLogger +from pip._internal.utils.misc import ( + check_externally_managed, + ensure_dir, + get_pip_version, + protect_pip_from_modification_on_windows, + write_output, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, +) +from pip._internal.wheel_builder import build, should_build_for_install_command + +logger = getLogger(__name__) + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + def add_options(self) -> None: + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.pre()) + + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option( + "--dry-run", + action="store_true", + dest="dry_run", + default=False, + help=( + "Don't actually install anything, just print what would be. " + "Can be used in combination with --ignore-installed " + "to 'resolve' the requirements." + ), + ) + self.cmd_opts.add_option( + "-t", + "--target", + dest="target_dir", + metavar="dir", + default=None, + help=( + "Install packages into . " + "By default this will not replace existing files/folders in " + ". Use --upgrade to replace existing packages in " + "with new versions." + ), + ) + cmdoptions.add_target_python_options(self.cmd_opts) + + self.cmd_opts.add_option( + "--user", + dest="use_user_site", + action="store_true", + help=( + "Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.)" + ), + ) + self.cmd_opts.add_option( + "--no-user", + dest="use_user_site", + action="store_false", + help=SUPPRESS_HELP, + ) + self.cmd_opts.add_option( + "--root", + dest="root_path", + metavar="dir", + default=None, + help="Install everything relative to this alternate root directory.", + ) + self.cmd_opts.add_option( + "--prefix", + dest="prefix_path", + metavar="dir", + default=None, + help=( + "Installation prefix where lib, bin and other top-level " + "folders are placed. Note that the resulting installation may " + "contain scripts and other resources which reference the " + "Python interpreter of pip, and not that of ``--prefix``. " + "See also the ``--python`` option if the intention is to " + "install packages into another (possibly pip-free) " + "environment." + ), + ) + + self.cmd_opts.add_option(cmdoptions.src()) + + self.cmd_opts.add_option( + "-U", + "--upgrade", + dest="upgrade", + action="store_true", + help=( + "Upgrade all specified packages to the newest available " + "version. The handling of dependencies depends on the " + "upgrade-strategy used." + ), + ) + + self.cmd_opts.add_option( + "--upgrade-strategy", + dest="upgrade_strategy", + default="only-if-needed", + choices=["only-if-needed", "eager"], + help=( + "Determines how dependency upgrading should be handled " + "[default: %default]. " + '"eager" - dependencies are upgraded regardless of ' + "whether the currently installed version satisfies the " + "requirements of the upgraded package(s). " + '"only-if-needed" - are upgraded only when they do not ' + "satisfy the requirements of the upgraded package(s)." + ), + ) + + self.cmd_opts.add_option( + "--force-reinstall", + dest="force_reinstall", + action="store_true", + help="Reinstall all packages even if they are already up-to-date.", + ) + + self.cmd_opts.add_option( + "-I", + "--ignore-installed", + dest="ignore_installed", + action="store_true", + help=( + "Ignore the installed packages, overwriting them. " + "This can break your system if the existing package " + "is of a different version or was installed " + "with a different package manager!" + ), + ) + + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.check_build_deps()) + self.cmd_opts.add_option(cmdoptions.override_externally_managed()) + + self.cmd_opts.add_option(cmdoptions.config_settings()) + self.cmd_opts.add_option(cmdoptions.global_options()) + + self.cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile Python source files to bytecode", + ) + + self.cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile Python source files to bytecode", + ) + + self.cmd_opts.add_option( + "--no-warn-script-location", + action="store_false", + dest="warn_script_location", + default=True, + help="Do not warn when installing scripts outside PATH", + ) + self.cmd_opts.add_option( + "--no-warn-conflicts", + action="store_false", + dest="warn_about_conflicts", + default=True, + help="Do not warn about broken dependencies", + ) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.root_user_action()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + self.cmd_opts.add_option( + "--report", + dest="json_report_file", + metavar="file", + default=None, + help=( + "Generate a JSON file describing what pip did to install " + "the provided requirements. " + "Can be used in combination with --dry-run and --ignore-installed " + "to 'resolve' the requirements. " + "When - is used as file name it writes to stdout. " + "When writing to stdout, please combine with the --quiet option " + "to avoid mixing pip logging output with JSON output." + ), + ) + + @with_cleanup + def run(self, options: Values, args: List[str]) -> int: + if options.use_user_site and options.target_dir is not None: + raise CommandError("Can not combine '--user' and '--target'") + + # Check whether the environment we're installing into is externally + # managed, as specified in PEP 668. Specifying --root, --target, or + # --prefix disables the check, since there's no reliable way to locate + # the EXTERNALLY-MANAGED file for those cases. An exception is also + # made specifically for "--dry-run --report" for convenience. + installing_into_current_environment = ( + not (options.dry_run and options.json_report_file) + and options.root_path is None + and options.target_dir is None + and options.prefix_path is None + ) + if ( + installing_into_current_environment + and not options.override_externally_managed + ): + check_externally_managed() + + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + cmdoptions.check_dist_restriction(options, check_target=True) + + logger.verbose("Using %s", get_pip_version()) + options.use_user_site = decide_user_install( + options.use_user_site, + prefix_path=options.prefix_path, + target_dir=options.target_dir, + root_path=options.root_path, + isolated_mode=options.isolated_mode, + ) + + target_temp_dir: Optional[TempDirectory] = None + target_temp_dir_path: Optional[str] = None + if options.target_dir: + options.ignore_installed = True + options.target_dir = os.path.abspath(options.target_dir) + if ( + # fmt: off + os.path.exists(options.target_dir) and + not os.path.isdir(options.target_dir) + # fmt: on + ): + raise CommandError( + "Target path exists but is not a directory, will not continue." + ) + + # Create a target directory for using with the target option + target_temp_dir = TempDirectory(kind="target") + target_temp_dir_path = target_temp_dir.path + self.enter_context(target_temp_dir) + + global_options = options.global_options or [] + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=options.ignore_requires_python, + ) + build_tracker = self.enter_context(get_build_tracker()) + + directory = TempDirectory( + delete=not options.no_clean, + kind="install", + globally_managed=True, + ) + + try: + reqs = self.get_requirements(args, options, finder, session) + check_legacy_setup_py_options(options, reqs) + + wheel_cache = WheelCache(options.cache_dir) + + # Only when installing is it permitted to use PEP 660. + # In other circumstances (pip wheel, pip download) we generate + # regular (i.e. non editable) metadata and wheels. + for req in reqs: + req.permit_editable_wheels = True + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + build_tracker=build_tracker, + session=session, + finder=finder, + use_user_site=options.use_user_site, + verbosity=self.verbosity, + ) + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + ignore_installed=options.ignore_installed, + ignore_requires_python=options.ignore_requires_python, + force_reinstall=options.force_reinstall, + upgrade_strategy=upgrade_strategy, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=not options.target_dir + ) + + if options.json_report_file: + report = InstallationReport(requirement_set.requirements_to_install) + if options.json_report_file == "-": + print_json(data=report.to_dict()) + else: + with open(options.json_report_file, "w", encoding="utf-8") as f: + json.dump(report.to_dict(), f, indent=2, ensure_ascii=False) + + if options.dry_run: + # In non dry-run mode, the legacy versions and specifiers check + # will be done as part of conflict detection. + requirement_set.warn_legacy_versions_and_specifiers() + would_install_items = sorted( + (r.metadata["name"], r.metadata["version"]) + for r in requirement_set.requirements_to_install + ) + if would_install_items: + write_output( + "Would install %s", + " ".join("-".join(item) for item in would_install_items), + ) + return SUCCESS + + try: + pip_req = requirement_set.get_requirement("pip") + except KeyError: + modifying_pip = False + else: + # If we're not replacing an already installed pip, + # we're not modifying it. + modifying_pip = pip_req.satisfied_by is None + protect_pip_from_modification_on_windows(modifying_pip=modifying_pip) + + reqs_to_build = [ + r + for r in requirement_set.requirements.values() + if should_build_for_install_command(r) + ] + + _, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + verify=True, + build_options=[], + global_options=global_options, + ) + + if build_failures: + raise InstallationError( + "Could not build wheels for {}, which is required to " + "install pyproject.toml-based projects".format( + ", ".join(r.name for r in build_failures) # type: ignore + ) + ) + + to_install = resolver.get_installation_order(requirement_set) + + # Check for conflicts in the package set we're installing. + conflicts: Optional[ConflictDetails] = None + should_warn_about_conflicts = ( + not options.ignore_dependencies and options.warn_about_conflicts + ) + if should_warn_about_conflicts: + conflicts = self._determine_conflicts(to_install) + + # Don't warn about script install locations if + # --target or --prefix has been specified + warn_script_location = options.warn_script_location + if options.target_dir or options.prefix_path: + warn_script_location = False + + installed = install_given_reqs( + to_install, + global_options, + root=options.root_path, + home=target_temp_dir_path, + prefix=options.prefix_path, + warn_script_location=warn_script_location, + use_user_site=options.use_user_site, + pycompile=options.compile, + ) + + lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=target_temp_dir_path, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + env = get_environment(lib_locations) + + installed.sort(key=operator.attrgetter("name")) + items = [] + for result in installed: + item = result.name + try: + installed_dist = env.get_distribution(item) + if installed_dist is not None: + item = f"{item}-{installed_dist.version}" + except Exception: + pass + items.append(item) + + if conflicts is not None: + self._warn_about_conflicts( + conflicts, + resolver_variant=self.determine_resolver_variant(options), + ) + + installed_desc = " ".join(items) + if installed_desc: + write_output( + "Successfully installed %s", + installed_desc, + ) + except OSError as error: + show_traceback = self.verbosity >= 1 + + message = create_os_error_message( + error, + show_traceback, + options.use_user_site, + ) + logger.error(message, exc_info=show_traceback) + + return ERROR + + if options.target_dir: + assert target_temp_dir + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + if options.root_user_action == "warn": + warn_if_run_as_root() + return SUCCESS + + def _handle_target_dir( + self, target_dir: str, target_temp_dir: TempDirectory, upgrade: bool + ) -> None: + ensure_dir(target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = get_scheme("", home=target_temp_dir.path) + purelib_dir = scheme.purelib + platlib_dir = scheme.platlib + data_dir = scheme.data + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + "Target directory %s already exists. Specify " + "--upgrade to force replacement.", + target_item_dir, + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + "Target directory %s already exists and is " + "a link. pip will not automatically replace " + "links, please remove if replacement is " + "desired.", + target_item_dir, + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move(os.path.join(lib_dir, item), target_item_dir) + + def _determine_conflicts( + self, to_install: List[InstallRequirement] + ) -> Optional[ConflictDetails]: + try: + return check_install_conflicts(to_install) + except Exception: + logger.exception( + "Error while checking for conflicts. Please file an issue on " + "pip's issue tracker: https://github.com/pypa/pip/issues/new" + ) + return None + + def _warn_about_conflicts( + self, conflict_details: ConflictDetails, resolver_variant: str + ) -> None: + package_set, (missing, conflicting) = conflict_details + if not missing and not conflicting: + return + + parts: List[str] = [] + if resolver_variant == "legacy": + parts.append( + "pip's legacy dependency resolver does not consider dependency " + "conflicts when selecting packages. This behaviour is the " + "source of the following dependency conflicts." + ) + else: + assert resolver_variant == "resolvelib" + parts.append( + "pip's dependency resolver does not currently take into account " + "all the packages that are installed. This behaviour is the " + "source of the following dependency conflicts." + ) + + # NOTE: There is some duplication here, with commands/check.py + for project_name in missing: + version = package_set[project_name][0] + for dependency in missing[project_name]: + message = ( + f"{project_name} {version} requires {dependency[1]}, " + "which is not installed." + ) + parts.append(message) + + for project_name in conflicting: + version = package_set[project_name][0] + for dep_name, dep_version, req in conflicting[project_name]: + message = ( + "{name} {version} requires {requirement}, but {you} have " + "{dep_name} {dep_version} which is incompatible." + ).format( + name=project_name, + version=version, + requirement=req, + dep_name=dep_name, + dep_version=dep_version, + you=("you" if resolver_variant == "resolvelib" else "you'll"), + ) + parts.append(message) + + logger.critical("\n".join(parts)) + + +def get_lib_location_guesses( + user: bool = False, + home: Optional[str] = None, + root: Optional[str] = None, + isolated: bool = False, + prefix: Optional[str] = None, +) -> List[str]: + scheme = get_scheme( + "", + user=user, + home=home, + root=root, + isolated=isolated, + prefix=prefix, + ) + return [scheme.purelib, scheme.platlib] + + +def site_packages_writable(root: Optional[str], isolated: bool) -> bool: + return all( + test_writable_dir(d) + for d in set(get_lib_location_guesses(root=root, isolated=isolated)) + ) + + +def decide_user_install( + use_user_site: Optional[bool], + prefix_path: Optional[str] = None, + target_dir: Optional[str] = None, + root_path: Optional[str] = None, + isolated_mode: bool = False, +) -> bool: + """Determine whether to do a user install based on the input options. + + If use_user_site is False, no additional checks are done. + If use_user_site is True, it is checked for compatibility with other + options. + If use_user_site is None, the default behaviour depends on the environment, + which is provided by the other arguments. + """ + # In some cases (config from tox), use_user_site can be set to an integer + # rather than a bool, which 'use_user_site is False' wouldn't catch. + if (use_user_site is not None) and (not use_user_site): + logger.debug("Non-user install by explicit request") + return False + + if use_user_site: + if prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + logger.debug("User install by explicit request") + return True + + # If we are here, user installs have not been explicitly requested/avoided + assert use_user_site is None + + # user install incompatible with --prefix/--target + if prefix_path or target_dir: + logger.debug("Non-user install due to --prefix or --target option") + return False + + # If user installs are not enabled, choose a non-user install + if not site.ENABLE_USER_SITE: + logger.debug("Non-user install because user site-packages disabled") + return False + + # If we have permission for a non-user install, do that, + # otherwise do a user install. + if site_packages_writable(root=root_path, isolated=isolated_mode): + logger.debug("Non-user install because site-packages writeable") + return False + + logger.info( + "Defaulting to user installation because normal site-packages " + "is not writeable" + ) + return True + + +def create_os_error_message( + error: OSError, show_traceback: bool, using_user_site: bool +) -> str: + """Format an error message for an OSError + + It may occur anytime during the execution of the install command. + """ + parts = [] + + # Mention the error if we are not going to show a traceback + parts.append("Could not install packages due to an OSError") + if not show_traceback: + parts.append(": ") + parts.append(str(error)) + else: + parts.append(".") + + # Spilt the error indication from a helper message (if any) + parts[-1] += "\n" + + # Suggest useful actions to the user: + # (1) using user site-packages or (2) verifying the permissions + if error.errno == errno.EACCES: + user_option_part = "Consider using the `--user` option" + permissions_part = "Check the permissions" + + if not running_under_virtualenv() and not using_user_site: + parts.extend( + [ + user_option_part, + " or ", + permissions_part.lower(), + ] + ) + else: + parts.append(permissions_part) + parts.append(".\n") + + # Suggest the user to enable Long Paths if path length is + # more than 260 + if ( + WINDOWS + and error.errno == errno.ENOENT + and error.filename + and len(error.filename) > 260 + ): + parts.append( + "HINT: This error might have occurred since " + "this system does not have Windows Long Path " + "support enabled. You can find information on " + "how to enable this at " + "https://pip.pypa.io/warnings/enable-long-paths\n" + ) + + return "".join(parts).strip() + "\n" diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/list.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000..32fb19b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,370 @@ +import json +import logging +from optparse import Values +from typing import TYPE_CHECKING, Generator, List, Optional, Sequence, Tuple, cast + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution, get_environment +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.network.session import PipSession +from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.misc import tabulate, write_output + +if TYPE_CHECKING: + from pip._internal.metadata.base import DistributionVersion + + class _DistWithLatestInfo(BaseDistribution): + """Give the distribution object a couple of extra fields. + + These will be populated during ``get_outdated()``. This is dirty but + makes the rest of the code much cleaner. + """ + + latest_version: DistributionVersion + latest_filetype: str + + _ProcessedDists = Sequence[_DistWithLatestInfo] + + +from pip._vendor.packaging.version import parse + +logger = logging.getLogger(__name__) + + +class ListCommand(IndexGroupCommand): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + + ignore_require_venv = True + usage = """ + %prog [options]""" + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-o", + "--outdated", + action="store_true", + default=False, + help="List outdated packages", + ) + self.cmd_opts.add_option( + "-u", + "--uptodate", + action="store_true", + default=False, + help="List uptodate packages", + ) + self.cmd_opts.add_option( + "-e", + "--editable", + action="store_true", + default=False, + help="List editable projects.", + ) + self.cmd_opts.add_option( + "-l", + "--local", + action="store_true", + default=False, + help=( + "If in a virtualenv that has global access, do not list " + "globally-installed packages." + ), + ) + self.cmd_opts.add_option( + "--user", + dest="user", + action="store_true", + default=False, + help="Only output packages installed in user-site.", + ) + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + "--pre", + action="store_true", + default=False, + help=( + "Include pre-release and development versions. By default, " + "pip only finds stable versions." + ), + ) + + self.cmd_opts.add_option( + "--format", + action="store", + dest="list_format", + default="columns", + choices=("columns", "freeze", "json"), + help=( + "Select the output format among: columns (default), freeze, or json. " + "The 'freeze' format cannot be used with the --outdated option." + ), + ) + + self.cmd_opts.add_option( + "--not-required", + action="store_true", + dest="not_required", + help="List packages that are not dependencies of installed packages.", + ) + + self.cmd_opts.add_option( + "--exclude-editable", + action="store_false", + dest="include_editable", + help="Exclude editable package from output.", + ) + self.cmd_opts.add_option( + "--include-editable", + action="store_true", + dest="include_editable", + help="Include editable package from output.", + default=True, + ) + self.cmd_opts.add_option(cmdoptions.list_exclude()) + index_opts = cmdoptions.make_option_group(cmdoptions.index_group, self.parser) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + def _build_package_finder( + self, options: Values, session: PipSession + ) -> PackageFinder: + """ + Create a package finder appropriate to this list command. + """ + link_collector = LinkCollector.create(session, options=options) + + # Pass allow_yanked=False to ignore yanked versions. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=options.pre, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + + def run(self, options: Values, args: List[str]) -> int: + if options.outdated and options.uptodate: + raise CommandError("Options --outdated and --uptodate cannot be combined.") + + if options.outdated and options.list_format == "freeze": + raise CommandError( + "List format 'freeze' cannot be used with the --outdated option." + ) + + cmdoptions.check_list_path_option(options) + + skip = set(stdlib_pkgs) + if options.excludes: + skip.update(canonicalize_name(n) for n in options.excludes) + + packages: "_ProcessedDists" = [ + cast("_DistWithLatestInfo", d) + for d in get_environment(options.path).iter_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + skip=skip, + ) + ] + + # get_not_required must be called firstly in order to find and + # filter out all dependencies correctly. Otherwise a package + # can't be identified as requirement because some parent packages + # could be filtered out before. + if options.not_required: + packages = self.get_not_required(packages, options) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + self.output_package_listing(packages, options) + return SUCCESS + + def get_outdated( + self, packages: "_ProcessedDists", options: Values + ) -> "_ProcessedDists": + return [ + dist + for dist in self.iter_packages_latest_infos(packages, options) + if parse(str(dist.latest_version)) > parse(str(dist.version)) + ] + + def get_uptodate( + self, packages: "_ProcessedDists", options: Values + ) -> "_ProcessedDists": + return [ + dist + for dist in self.iter_packages_latest_infos(packages, options) + if parse(str(dist.latest_version)) == parse(str(dist.version)) + ] + + def get_not_required( + self, packages: "_ProcessedDists", options: Values + ) -> "_ProcessedDists": + dep_keys = { + canonicalize_name(dep.name) + for dist in packages + for dep in (dist.iter_dependencies() or ()) + } + + # Create a set to remove duplicate packages, and cast it to a list + # to keep the return type consistent with get_outdated and + # get_uptodate + return list({pkg for pkg in packages if pkg.canonical_name not in dep_keys}) + + def iter_packages_latest_infos( + self, packages: "_ProcessedDists", options: Values + ) -> Generator["_DistWithLatestInfo", None, None]: + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + + def latest_info( + dist: "_DistWithLatestInfo", + ) -> Optional["_DistWithLatestInfo"]: + all_candidates = finder.find_all_candidates(dist.canonical_name) + if not options.pre: + # Remove prereleases + all_candidates = [ + candidate + for candidate in all_candidates + if not candidate.version.is_prerelease + ] + + evaluator = finder.make_candidate_evaluator( + project_name=dist.canonical_name, + ) + best_candidate = evaluator.sort_best_candidate(all_candidates) + if best_candidate is None: + return None + + remote_version = best_candidate.version + if best_candidate.link.is_wheel: + typ = "wheel" + else: + typ = "sdist" + dist.latest_version = remote_version + dist.latest_filetype = typ + return dist + + for dist in map(latest_info, packages): + if dist is not None: + yield dist + + def output_package_listing( + self, packages: "_ProcessedDists", options: Values + ) -> None: + packages = sorted( + packages, + key=lambda dist: dist.canonical_name, + ) + if options.list_format == "columns" and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == "freeze": + for dist in packages: + if options.verbose >= 1: + write_output( + "%s==%s (%s)", dist.raw_name, dist.version, dist.location + ) + else: + write_output("%s==%s", dist.raw_name, dist.version) + elif options.list_format == "json": + write_output(format_for_json(packages, options)) + + def output_package_listing_columns( + self, data: List[List[str]], header: List[str] + ) -> None: + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join("-" * x for x in sizes)) + + for val in pkg_strings: + write_output(val) + + +def format_for_columns( + pkgs: "_ProcessedDists", options: Values +) -> Tuple[List[List[str]], List[str]]: + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + header = ["Package", "Version"] + + running_outdated = options.outdated + if running_outdated: + header.extend(["Latest", "Type"]) + + has_editables = any(x.editable for x in pkgs) + if has_editables: + header.append("Editable project location") + + if options.verbose >= 1: + header.append("Location") + if options.verbose >= 1: + header.append("Installer") + + data = [] + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.raw_name, str(proj.version)] + + if running_outdated: + row.append(str(proj.latest_version)) + row.append(proj.latest_filetype) + + if has_editables: + row.append(proj.editable_project_location or "") + + if options.verbose >= 1: + row.append(proj.location or "") + if options.verbose >= 1: + row.append(proj.installer) + + data.append(row) + + return data, header + + +def format_for_json(packages: "_ProcessedDists", options: Values) -> str: + data = [] + for dist in packages: + info = { + "name": dist.raw_name, + "version": str(dist.version), + } + if options.verbose >= 1: + info["location"] = dist.location or "" + info["installer"] = dist.installer + if options.outdated: + info["latest_version"] = str(dist.latest_version) + info["latest_filetype"] = dist.latest_filetype + editable_project_location = dist.editable_project_location + if editable_project_location: + info["editable_project_location"] = editable_project_location + data.append(info) + return json.dumps(data) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/search.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000..03ed925 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,174 @@ +import logging +import shutil +import sys +import textwrap +import xmlrpc.client +from collections import OrderedDict +from optparse import Values +from typing import TYPE_CHECKING, Dict, List, Optional + +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.metadata import get_default_environment +from pip._internal.models.index import PyPI +from pip._internal.network.xmlrpc import PipXmlrpcTransport +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import write_output + +if TYPE_CHECKING: + from typing import TypedDict + + class TransformedHit(TypedDict): + name: str + summary: str + versions: List[str] + + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command, SessionCommandMixin): + """Search for PyPI packages whose name or summary contains .""" + + usage = """ + %prog [options] """ + ignore_require_venv = True + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-i", + "--index", + dest="index", + metavar="URL", + default=PyPI.pypi_url, + help="Base URL of Python Package Index (default %default)", + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + if not args: + raise CommandError("Missing required argument (search query).") + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = shutil.get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query: List[str], options: Values) -> List[Dict[str, str]]: + index_url = options.index + + session = self.get_default_session(options) + + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc.client.ServerProxy(index_url, transport) + try: + hits = pypi.search({"name": query, "summary": query}, "or") + except xmlrpc.client.Fault as fault: + message = "XMLRPC request failed [code: {code}]\n{string}".format( + code=fault.faultCode, + string=fault.faultString, + ) + raise CommandError(message) + assert isinstance(hits, list) + return hits + + +def transform_hits(hits: List[Dict[str, str]]) -> List["TransformedHit"]: + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages: Dict[str, "TransformedHit"] = OrderedDict() + for hit in hits: + name = hit["name"] + summary = hit["summary"] + version = hit["version"] + + if name not in packages.keys(): + packages[name] = { + "name": name, + "summary": summary, + "versions": [version], + } + else: + packages[name]["versions"].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]["versions"]): + packages[name]["summary"] = summary + + return list(packages.values()) + + +def print_dist_installation_info(name: str, latest: str) -> None: + env = get_default_environment() + dist = env.get_distribution(name) + if dist is not None: + with indent_log(): + if dist.version == latest: + write_output("INSTALLED: %s (latest)", dist.version) + else: + write_output("INSTALLED: %s", dist.version) + if parse_version(latest).pre: + write_output( + "LATEST: %s (pre-release; install" + " with `pip install --pre`)", + latest, + ) + else: + write_output("LATEST: %s", latest) + + +def print_results( + hits: List["TransformedHit"], + name_column_width: Optional[int] = None, + terminal_width: Optional[int] = None, +) -> None: + if not hits: + return + if name_column_width is None: + name_column_width = ( + max( + [ + len(hit["name"]) + len(highest_version(hit.get("versions", ["-"]))) + for hit in hits + ] + ) + + 4 + ) + + for hit in hits: + name = hit["name"] + summary = hit["summary"] or "" + latest = highest_version(hit.get("versions", ["-"])) + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary_lines = textwrap.wrap(summary, target_width) + summary = ("\n" + " " * (name_column_width + 3)).join(summary_lines) + + name_latest = f"{name} ({latest})" + line = f"{name_latest:{name_column_width}} - {summary}" + try: + write_output(line) + print_dist_installation_info(name, latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions: List[str]) -> str: + return max(versions, key=parse_version) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/show.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000..3f10701 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,189 @@ +import logging +from optparse import Values +from typing import Generator, Iterable, Iterator, List, NamedTuple, Optional + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.metadata import BaseDistribution, get_default_environment +from pip._internal.utils.misc import write_output + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + + usage = """ + %prog [options] ...""" + ignore_require_venv = True + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-f", + "--files", + dest="files", + action="store_true", + default=False, + help="Show the full list of installed files for each package.", + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + if not args: + logger.warning("ERROR: Please provide a package name or names.") + return ERROR + query = args + + results = search_packages_info(query) + if not print_results( + results, list_files=options.files, verbose=options.verbose + ): + return ERROR + return SUCCESS + + +class _PackageInfo(NamedTuple): + name: str + version: str + location: str + editable_project_location: Optional[str] + requires: List[str] + required_by: List[str] + installer: str + metadata_version: str + classifiers: List[str] + summary: str + homepage: str + project_urls: List[str] + author: str + author_email: str + license: str + entry_points: List[str] + files: Optional[List[str]] + + +def search_packages_info(query: List[str]) -> Generator[_PackageInfo, None, None]: + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + env = get_default_environment() + + installed = {dist.canonical_name: dist for dist in env.iter_all_distributions()} + query_names = [canonicalize_name(name) for name in query] + missing = sorted( + [name for name, pkg in zip(query, query_names) if pkg not in installed] + ) + if missing: + logger.warning("Package(s) not found: %s", ", ".join(missing)) + + def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: + return ( + dist.metadata["Name"] or "UNKNOWN" + for dist in installed.values() + if current_dist.canonical_name + in {canonicalize_name(d.name) for d in dist.iter_dependencies()} + ) + + for query_name in query_names: + try: + dist = installed[query_name] + except KeyError: + continue + + requires = sorted((req.name for req in dist.iter_dependencies()), key=str.lower) + required_by = sorted(_get_requiring_packages(dist), key=str.lower) + + try: + entry_points_text = dist.read_text("entry_points.txt") + entry_points = entry_points_text.splitlines(keepends=False) + except FileNotFoundError: + entry_points = [] + + files_iter = dist.iter_declared_entries() + if files_iter is None: + files: Optional[List[str]] = None + else: + files = sorted(files_iter) + + metadata = dist.metadata + + yield _PackageInfo( + name=dist.raw_name, + version=str(dist.version), + location=dist.location or "", + editable_project_location=dist.editable_project_location, + requires=requires, + required_by=required_by, + installer=dist.installer, + metadata_version=dist.metadata_version or "", + classifiers=metadata.get_all("Classifier", []), + summary=metadata.get("Summary", ""), + homepage=metadata.get("Home-page", ""), + project_urls=metadata.get_all("Project-URL", []), + author=metadata.get("Author", ""), + author_email=metadata.get("Author-email", ""), + license=metadata.get("License", ""), + entry_points=entry_points, + files=files, + ) + + +def print_results( + distributions: Iterable[_PackageInfo], + list_files: bool, + verbose: bool, +) -> bool: + """ + Print the information from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + write_output("---") + + write_output("Name: %s", dist.name) + write_output("Version: %s", dist.version) + write_output("Summary: %s", dist.summary) + write_output("Home-page: %s", dist.homepage) + write_output("Author: %s", dist.author) + write_output("Author-email: %s", dist.author_email) + write_output("License: %s", dist.license) + write_output("Location: %s", dist.location) + if dist.editable_project_location is not None: + write_output( + "Editable project location: %s", dist.editable_project_location + ) + write_output("Requires: %s", ", ".join(dist.requires)) + write_output("Required-by: %s", ", ".join(dist.required_by)) + + if verbose: + write_output("Metadata-Version: %s", dist.metadata_version) + write_output("Installer: %s", dist.installer) + write_output("Classifiers:") + for classifier in dist.classifiers: + write_output(" %s", classifier) + write_output("Entry-points:") + for entry in dist.entry_points: + write_output(" %s", entry.strip()) + write_output("Project-URLs:") + for project_url in dist.project_urls: + write_output(" %s", project_url) + if list_files: + write_output("Files:") + if dist.files is None: + write_output("Cannot locate RECORD or installed-files.txt") + else: + for line in dist.files: + write_output(" %s", line.strip()) + return results_printed diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000..f198fc3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,113 @@ +import logging +from optparse import Values +from typing import List + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin, warn_if_run_as_root +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import ( + install_req_from_line, + install_req_from_parsed_requirement, +) +from pip._internal.utils.misc import ( + check_externally_managed, + protect_pip_from_modification_on_windows, +) + +logger = logging.getLogger(__name__) + + +class UninstallCommand(Command, SessionCommandMixin): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + + usage = """ + %prog [options] ... + %prog [options] -r ...""" + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-r", + "--requirement", + dest="requirements", + action="append", + default=[], + metavar="file", + help=( + "Uninstall all the packages listed in the given requirements " + "file. This option can be used multiple times." + ), + ) + self.cmd_opts.add_option( + "-y", + "--yes", + dest="yes", + action="store_true", + help="Don't ask for confirmation of uninstall deletions.", + ) + self.cmd_opts.add_option(cmdoptions.root_user_action()) + self.cmd_opts.add_option(cmdoptions.override_externally_managed()) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options: Values, args: List[str]) -> int: + session = self.get_default_session(options) + + reqs_to_uninstall = {} + for name in args: + req = install_req_from_line( + name, + isolated=options.isolated_mode, + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + else: + logger.warning( + "Invalid requirement: %r ignored -" + " the uninstall command expects named" + " requirements.", + name, + ) + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, options=options, session=session + ): + req = install_req_from_parsed_requirement( + parsed_req, isolated=options.isolated_mode + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + if not reqs_to_uninstall: + raise InstallationError( + f"You must give at least one requirement to {self.name} (see " + f'"pip help {self.name}")' + ) + + if not options.override_externally_managed: + check_externally_managed() + + protect_pip_from_modification_on_windows( + modifying_pip="pip" in reqs_to_uninstall + ) + + for req in reqs_to_uninstall.values(): + uninstall_pathset = req.uninstall( + auto_confirm=options.yes, + verbose=self.verbosity > 0, + ) + if uninstall_pathset: + uninstall_pathset.commit() + if options.root_user_action == "warn": + warn_if_run_as_root() + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000..ed578aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,183 @@ +import logging +import os +import shutil +from optparse import Values +from typing import List + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.operations.build.build_tracker import get_build_tracker +from pip._internal.req.req_install import ( + InstallRequirement, + check_legacy_setup_py_options, +) +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.wheel_builder import build, should_build_for_wheel_command + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ + + 'pip wheel' uses the build system interface as described here: + https://pip.pypa.io/en/stable/reference/build-system/ + + """ + + usage = """ + %prog [options] ... + %prog [options] -r ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + def add_options(self) -> None: + self.cmd_opts.add_option( + "-w", + "--wheel-dir", + dest="wheel_dir", + metavar="dir", + default=os.curdir, + help=( + "Build wheels into , where the default is the " + "current working directory." + ), + ) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.check_build_deps()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + + self.cmd_opts.add_option( + "--no-verify", + dest="no_verify", + action="store_true", + default=False, + help="Don't verify if built wheel is valid.", + ) + + self.cmd_opts.add_option(cmdoptions.config_settings()) + self.cmd_opts.add_option(cmdoptions.build_options()) + self.cmd_opts.add_option(cmdoptions.global_options()) + + self.cmd_opts.add_option( + "--pre", + action="store_true", + default=False, + help=( + "Include pre-release and development versions. By default, " + "pip only finds stable versions." + ), + ) + + self.cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options: Values, args: List[str]) -> int: + session = self.get_default_session(options) + + finder = self._build_package_finder(options, session) + + options.wheel_dir = normalize_path(options.wheel_dir) + ensure_dir(options.wheel_dir) + + build_tracker = self.enter_context(get_build_tracker()) + + directory = TempDirectory( + delete=not options.no_clean, + kind="wheel", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + check_legacy_setup_py_options(options, reqs) + + wheel_cache = WheelCache(options.cache_dir) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + build_tracker=build_tracker, + session=session, + finder=finder, + download_dir=options.wheel_dir, + use_user_site=False, + verbosity=self.verbosity, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + ignore_requires_python=options.ignore_requires_python, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve(reqs, check_supported_wheels=True) + + reqs_to_build: List[InstallRequirement] = [] + for req in requirement_set.requirements.values(): + if req.is_wheel: + preparer.save_linked_requirement(req) + elif should_build_for_wheel_command(req): + reqs_to_build.append(req) + + preparer.prepare_linked_requirements_more(requirement_set.requirements.values()) + requirement_set.warn_legacy_versions_and_specifiers() + + # build wheels + build_successes, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + verify=(not options.no_verify), + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + for req in build_successes: + assert req.link and req.link.is_wheel + assert req.local_file_path + # copy from cache to target directory + try: + shutil.copy(req.local_file_path, options.wheel_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, + e, + ) + build_failures.append(req) + if len(build_failures) != 0: + raise CommandError("Failed to build one or more wheels") + + return SUCCESS diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/configuration.py b/.venv/lib/python3.12/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000..c25273d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/configuration.py @@ -0,0 +1,383 @@ +"""Configuration management setup + +Some terminology: +- name + As written in config files. +- value + Value associated with a name +- key + Name combined with it's section (section.name) +- variant + A single word describing where the configuration key-value pair came from +""" + +import configparser +import locale +import os +import sys +from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple + +from pip._internal.exceptions import ( + ConfigurationError, + ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import getLogger +from pip._internal.utils.misc import ensure_dir, enum + +RawConfigParser = configparser.RawConfigParser # Shorthand +Kind = NewType("Kind", str) + +CONFIG_BASENAME = "pip.ini" if WINDOWS else "pip.conf" +ENV_NAMES_IGNORED = "version", "help" + +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + SITE="site", # [Virtual] Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) +OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR +VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE + +logger = getLogger(__name__) + + +# NOTE: Maybe use the optionx attribute to normalize keynames. +def _normalize_name(name: str) -> str: + """Make a name consistent regardless of source (environment or file)""" + name = name.lower().replace("_", "-") + if name.startswith("--"): + name = name[2:] # only prefer long opts + return name + + +def _disassemble_key(name: str) -> List[str]: + if "." not in name: + error_message = ( + "Key does not contain dot separated section and key. " + f"Perhaps you wanted to use 'global.{name}' instead?" + ) + raise ConfigurationError(error_message) + return name.split(".", 1) + + +def get_configuration_files() -> Dict[Kind, List[str]]: + global_config_files = [ + os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs("pip") + ] + + site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME) + legacy_config_file = os.path.join( + os.path.expanduser("~"), + "pip" if WINDOWS else ".pip", + CONFIG_BASENAME, + ) + new_config_file = os.path.join(appdirs.user_config_dir("pip"), CONFIG_BASENAME) + return { + kinds.GLOBAL: global_config_files, + kinds.SITE: [site_config_file], + kinds.USER: [legacy_config_file, new_config_file], + } + + +class Configuration: + """Handles management of configuration. + + Provides an interface to accessing and managing configuration files. + + This class converts provides an API that takes "section.key-name" style + keys and stores the value associated with it as "key-name" under the + section "section". + + This allows for a clean interface wherein the both the section and the + key-name are preserved in an easy to manage form in the configuration files + and the data stored is also nice. + """ + + def __init__(self, isolated: bool, load_only: Optional[Kind] = None) -> None: + super().__init__() + + if load_only is not None and load_only not in VALID_LOAD_ONLY: + raise ConfigurationError( + "Got invalid value for load_only - should be one of {}".format( + ", ".join(map(repr, VALID_LOAD_ONLY)) + ) + ) + self.isolated = isolated + self.load_only = load_only + + # Because we keep track of where we got the data from + self._parsers: Dict[Kind, List[Tuple[str, RawConfigParser]]] = { + variant: [] for variant in OVERRIDE_ORDER + } + self._config: Dict[Kind, Dict[str, Any]] = { + variant: {} for variant in OVERRIDE_ORDER + } + self._modified_parsers: List[Tuple[str, RawConfigParser]] = [] + + def load(self) -> None: + """Loads configuration from configuration files and environment""" + self._load_config_files() + if not self.isolated: + self._load_environment_vars() + + def get_file_to_edit(self) -> Optional[str]: + """Returns the file with highest priority in configuration""" + assert self.load_only is not None, "Need to be specified a file to be editing" + + try: + return self._get_parser_to_modify()[0] + except IndexError: + return None + + def items(self) -> Iterable[Tuple[str, Any]]: + """Returns key-value pairs like dict.items() representing the loaded + configuration + """ + return self._dictionary.items() + + def get_value(self, key: str) -> Any: + """Get a value from the configuration.""" + orig_key = key + key = _normalize_name(key) + try: + return self._dictionary[key] + except KeyError: + # disassembling triggers a more useful error message than simply + # "No such key" in the case that the key isn't in the form command.option + _disassemble_key(key) + raise ConfigurationError(f"No such key - {orig_key}") + + def set_value(self, key: str, value: Any) -> None: + """Modify a value in the configuration.""" + key = _normalize_name(key) + self._ensure_have_load_only() + + assert self.load_only + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Modify the parser and the configuration + if not parser.has_section(section): + parser.add_section(section) + parser.set(section, name, value) + + self._config[self.load_only][key] = value + self._mark_as_modified(fname, parser) + + def unset_value(self, key: str) -> None: + """Unset a value in the configuration.""" + orig_key = key + key = _normalize_name(key) + self._ensure_have_load_only() + + assert self.load_only + if key not in self._config[self.load_only]: + raise ConfigurationError(f"No such key - {orig_key}") + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + if not ( + parser.has_section(section) and parser.remove_option(section, name) + ): + # The option was not removed. + raise ConfigurationError( + "Fatal Internal error [id=1]. Please report as a bug." + ) + + # The section may be empty after the option was removed. + if not parser.items(section): + parser.remove_section(section) + self._mark_as_modified(fname, parser) + + del self._config[self.load_only][key] + + def save(self) -> None: + """Save the current in-memory state.""" + self._ensure_have_load_only() + + for fname, parser in self._modified_parsers: + logger.info("Writing to %s", fname) + + # Ensure directory exists. + ensure_dir(os.path.dirname(fname)) + + # Ensure directory's permission(need to be writeable) + try: + with open(fname, "w") as f: + parser.write(f) + except OSError as error: + raise ConfigurationError( + f"An error occurred while writing to the configuration file " + f"{fname}: {error}" + ) + + # + # Private routines + # + + def _ensure_have_load_only(self) -> None: + if self.load_only is None: + raise ConfigurationError("Needed a specific file to be modifying.") + logger.debug("Will be working with %s variant only", self.load_only) + + @property + def _dictionary(self) -> Dict[str, Any]: + """A dictionary representing the loaded configuration.""" + # NOTE: Dictionaries are not populated if not loaded. So, conditionals + # are not needed here. + retval = {} + + for variant in OVERRIDE_ORDER: + retval.update(self._config[variant]) + + return retval + + def _load_config_files(self) -> None: + """Loads configuration from configuration files""" + config_files = dict(self.iter_config_files()) + if config_files[kinds.ENV][0:1] == [os.devnull]: + logger.debug( + "Skipping loading configuration files due to " + "environment's PIP_CONFIG_FILE being os.devnull" + ) + return + + for variant, files in config_files.items(): + for fname in files: + # If there's specific variant set in `load_only`, load only + # that variant, not the others. + if self.load_only is not None and variant != self.load_only: + logger.debug("Skipping file '%s' (variant: %s)", fname, variant) + continue + + parser = self._load_file(variant, fname) + + # Keeping track of the parsers used + self._parsers[variant].append((fname, parser)) + + def _load_file(self, variant: Kind, fname: str) -> RawConfigParser: + logger.verbose("For variant '%s', will try loading '%s'", variant, fname) + parser = self._construct_parser(fname) + + for section in parser.sections(): + items = parser.items(section) + self._config[variant].update(self._normalized_keys(section, items)) + + return parser + + def _construct_parser(self, fname: str) -> RawConfigParser: + parser = configparser.RawConfigParser() + # If there is no such file, don't bother reading it but create the + # parser anyway, to hold the data. + # Doing this is useful when modifying and saving files, where we don't + # need to construct a parser. + if os.path.exists(fname): + locale_encoding = locale.getpreferredencoding(False) + try: + parser.read(fname, encoding=locale_encoding) + except UnicodeDecodeError: + # See https://github.com/pypa/pip/issues/4963 + raise ConfigurationFileCouldNotBeLoaded( + reason=f"contains invalid {locale_encoding} characters", + fname=fname, + ) + except configparser.Error as error: + # See https://github.com/pypa/pip/issues/4893 + raise ConfigurationFileCouldNotBeLoaded(error=error) + return parser + + def _load_environment_vars(self) -> None: + """Loads configuration from environment variables""" + self._config[kinds.ENV_VAR].update( + self._normalized_keys(":env:", self.get_environ_vars()) + ) + + def _normalized_keys( + self, section: str, items: Iterable[Tuple[str, Any]] + ) -> Dict[str, Any]: + """Normalizes items to construct a dictionary with normalized keys. + + This routine is where the names become keys and are made the same + regardless of source - configuration files or environment. + """ + normalized = {} + for name, val in items: + key = section + "." + _normalize_name(name) + normalized[key] = val + return normalized + + def get_environ_vars(self) -> Iterable[Tuple[str, str]]: + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + if key.startswith("PIP_"): + name = key[4:].lower() + if name not in ENV_NAMES_IGNORED: + yield name, val + + # XXX: This is patched in the tests. + def iter_config_files(self) -> Iterable[Tuple[Kind, List[str]]]: + """Yields variant and configuration files associated with it. + + This should be treated like items of a dictionary. The order + here doesn't affect what gets overridden. That is controlled + by OVERRIDE_ORDER. However this does control the order they are + displayed to the user. It's probably most ergononmic to display + things in the same order as OVERRIDE_ORDER + """ + # SMELL: Move the conditions out of this function + + env_config_file = os.environ.get("PIP_CONFIG_FILE", None) + config_files = get_configuration_files() + + yield kinds.GLOBAL, config_files[kinds.GLOBAL] + + # per-user config is not loaded when env_config_file exists + should_load_user_config = not self.isolated and not ( + env_config_file and os.path.exists(env_config_file) + ) + if should_load_user_config: + # The legacy config file is overridden by the new config file + yield kinds.USER, config_files[kinds.USER] + + # virtualenv config + yield kinds.SITE, config_files[kinds.SITE] + + if env_config_file is not None: + yield kinds.ENV, [env_config_file] + else: + yield kinds.ENV, [] + + def get_values_in_config(self, variant: Kind) -> Dict[str, Any]: + """Get values present in a config file""" + return self._config[variant] + + def _get_parser_to_modify(self) -> Tuple[str, RawConfigParser]: + # Determine which parser to modify + assert self.load_only + parsers = self._parsers[self.load_only] + if not parsers: + # This should not happen if everything works correctly. + raise ConfigurationError( + "Fatal Internal error [id=2]. Please report as a bug." + ) + + # Use the highest priority parser. + return parsers[-1] + + # XXX: This is patched in the tests. + def _mark_as_modified(self, fname: str, parser: RawConfigParser) -> None: + file_parser_tuple = (fname, parser) + if file_parser_tuple not in self._modified_parsers: + self._modified_parsers.append(file_parser_tuple) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self._dictionary!r})" diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py new file mode 100644 index 0000000..9a89a83 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/__init__.py @@ -0,0 +1,21 @@ +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.distributions.sdist import SourceDistribution +from pip._internal.distributions.wheel import WheelDistribution +from pip._internal.req.req_install import InstallRequirement + + +def make_distribution_for_install_requirement( + install_req: InstallRequirement, +) -> AbstractDistribution: + """Returns a Distribution for the given InstallRequirement""" + # Editable requirements will always be source distributions. They use the + # legacy logic until we create a modern standard for them. + if install_req.editable: + return SourceDistribution(install_req) + + # If it's a wheel, it's a WheelDistribution + if install_req.is_wheel: + return WheelDistribution(install_req) + + # Otherwise, a SourceDistribution + return SourceDistribution(install_req) 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 new file mode 100644 index 0000000000000000000000000000000000000000..7f13765d480293753e2d170b00e24abeefc6c65a GIT binary patch literal 962 zcmaJ;&ubGw6rM>oyIYeqwDuxF*&+zrk{zlC4;D3&iw8y0gOJOz$xf4L_D4E1sYG({ z;QvsMy^8;dUV=F&i-CeCZ-MmGlW*2UW0Vfeyzjkv^XB{B`&2AmL$J<2W>_PO(0Ao5 zmNo~o8h}&eBOeDS#0`uU&IFk-+sLLk8)#v!kxQ`_=%LXt0OzRT>l7bqzVTLTm;=*Z z`=LtOIN7Q;h2&mK))-jU>`N9$5Z^qA`@BV0q8l$elm;uo&8Lx&UJ%sjai4J-(nuzG z_ABOcdYtH-%07=;crHU3keKe71aOLm=txCZ7SBtSm#!iDFn@obvKK8`T!V#dW8p4m z|G$QfU*E5%yoBXh%aW|am|L<#tu{NMk@c@STT4vpGwC%0$`z5sWWs%=TElbT+L$XM zLh-dOholpSln76RUPNB-_=vWoAoaKzOV$$PSsb9HPhStKcsyny&i!xO}X3npFH=?xM)^t#ktzIQ<$B%~u# z<5|p%7lCfd`vHJAXj(w#^)E*Gvr+z5+CJMImz1wKa z*qsfBjUf>Z91T~9iMqjqe}I319)xJrw33jh2X7;bhH&z|?&Q!AsIpwCB>1sNX({84j zt!5)RB_;cs7@?P(02QKjElAI<*$6!P@qdo(h=jp5Yf-j^s`51y=tYLUP^7=GP#6 z3?Xq+)ofDLZLONJb)L3UcDkOvLh72Gxja_Q@a*}*;0Cd5r?|m$lQ6045Lo(FSEZT< z{ast?kAddMfads9G!0&`b9}!bVI957Q-E-dy0olFfY%81^iOtJ1TQXMu45 zxuk;?rlNKp(nI;GFBG~0MKMPrxgFI+&=nzHrdb?N~tiTT0WPdVVaKT1g0sm z^w{7joeuzymd!f=sF+mRjc8Zyl=Z<6suWbk#(KHpQ2?kmge~#Ul*IoG5`zxYH6dKNAI0%5 z5n0@IO|zuQBCJLrcsqiqG|&nMBr)|V3=c1pzw~0p=*;Mu-M9CaI|SZez4kZ0ItIz+ ze#%VV$xL?iOlIcB>=#FG&fO;>U!oBjZ?H`nM!rH^>ZX28K>b)3jem{w|sRgB-g{UcXBYKghnU&D=Qq`MI0t K{vjYnyZj5OE+O3j literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..578826bcd6c64dc5240452172afbd4c1c177d0c1 GIT binary patch literal 1721 zcma)6&2Jk;6rcU{+OcD!G=a9XtWa9jLfH+?kqRkQ1;P>`DxCJfYP~bIC)uygtgEaA zQiO7V6USWPM%DiWE=C+cD@BTgIB+w#TzcZot{v42DwJQ-33ej4>+7M8vNIK6-o4`;=4T|y$$ zkHch`5^NQwTxTR5vdEWa+~$KQz*GfwiZb*}+It2nS2+oKT!fl0Keq>QNNn!$(2MMS z79S8l<@V#a8>iIg_J?uQj~_kU+U1<~qY$Xt)>9HawFBC+H(6MOIDKQt?rNp*cB~sNSI6U=M=TRu1x`; zCGs*D3i1O0r|2_uAVMz5frz>!$CVCr{JclLkpBP7k5OCR8*PXhLqiXtU9x{#qAlkN zR5!14C@Sw!g57S$8nAg;2qp;S*ZU-OLy~&fOFefnZzKEay!`8gHL>KOgfT>JXy ztD~Qrn?E);$Ib1r(HY54dat;5d9zGA<#AB-V&3ux#6NUPGbRb& z^F?zaSsJ6;h1>O+qRiR!3#l>uKM@s&;$~!@ps9z*xv~zV1{&SMpidR4# zqe&fA8wJ63BR5<(@#q9*DmU{~To=ban2m(R31Ml*1iM^;w~Dm@ zFFN&c$8@GbUcBLJ7b>1$Bb{kX6lqL+Ps%S<9R9k(q0;t?tY_`{~ z=6aeXH0qb9H{o_#7q`<|>@se3;hANkDEXVqVWJ_9tl15B%l;@yZ!8DS+qcY3!FxFu vFO3IfzTahcVI%Pl@!Oy#iX=&A=;m)|_Y6HaLpv|@b*V9aefuv2V?pR|gI>=f literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..667deb7c5a237aaaffa742aea5cb9179336d3585 GIT binary patch literal 8509 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 Ql#L7JJBPm^m|;8cKZqu?xc~qF literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..5a992a09bc87caf3282e53ff6daf2a794d110086 GIT binary patch literal 2269 zcmaJ?&udKm!Kjf5Ta650)nDSK6JbGjN=Xa!_15! zwi*dlInZ7>a)n-6lz*U{xVIO#sYJB`38~`Hn`x*jAyw*ovv#lp=^kd^ym|BHy>Gtv z=I3IuK;V1!lkWeUCFBpBj6P#1Y<>a4T|x=f0upL9O+#4^^e|J)C^-{kL!)LWIUAT^ zu9j1>5m;fqmJbWHLO4~M(uhtzCe&ObG{=nfOzOY3t7}#Af8wN~CDz5Z^yA12K;GT( zqB!y!Uf}nb8+joE<;pC@G!y z117pcvhed3V?nZaiAnb+iY^`V)<^?OM{79Qyad8s!bnY{q^47?mSI_{GlQA?VWp|l z%vyGRXk5#|iUqptTDF#_1}jjLO+nUERHs(cqIu9`-i2hh3S~Ub-u26@&6!}46t-up z`0Vlcs)d3L-)Ajf*feIsj$&y?j8WT*x;AWWd)z}Jbp~!YYsjE$ccM1n z#b{NzN=(jVher) zf8kt?>#_73!nqJfOYyn0^Jj$cm!c4Ia;i62bi)b!y3_8;RvewEe(<3qe97i9B)CH1 zwEec@`jKQDnBa`(E*ynNwcSn1^^PA<7s+sqao4B4BX3|?-EK1jQ43`DE%L0iXA=Z6 zk+ca4m9z=@-6daqY|M4cw|sDRy#qW( zGNtrI$G!KjFo}c*@=H5zPSgSIaP&&!n^6Fn^!6#DKwO+b;oM*x>H+%;t(Zv$+$W_V zhPLBkQf}Lmtt5-f9Gd}0fH@*p(RmquiJ`(22$*g2-Kg4+on-{oM04pKQ)iFkP~FjZ zANUe=7;ce4fs|+NtbDWbxHR``X|8Ww?N_dDz*w65y|f1x?yP>h+P4<^hZZ)hh1;u7 z%7^b|zqjsNkL`tD?1hc;$=k-0(tG#VZ}X>rl#f;(t~^?OxVkZaVWYIzw-)&xjN5Fg z_L-E@y75IEs7{iU8!gtj=B8@O+G4S>ojeo0f=`1xUV@>LRVeWTsF=m!T^I!J#(PWg zI+%7XHUZTZfIa6;%>H-75j+9Q2yoj)@m4t^ z8-`iVb!prH!D{ok&A9Ax%q+>kbHm>OiKAnVO`l`8NV4@f4)`IQW4`Jvl@-VGQv-q_ zzJy_oywEd-1(#_~4|L6#xp(x36Zhx;A|P&NM#_O{85gzwv5z(h3WFKbC_XD692oE) znB>5rK@KI0%pM-(Q7Vwa^vfxb(!=rGMM&3_-8S$(jei!JYSo3_OJiP5J1wkL;r$GR zqH|&^i&5LHPPE->9bQw8jWaniX@-PZ89HQ8G5GwZ!0eqg9F;?g-|JFRC`}^w$~u+a z&S`!Oj>P6Bj>0g=Xqxtv%swY4pOZ6B$*F(ztfs#pFua^TpcVW3j{i;IuVDBOla^aC literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py new file mode 100644 index 0000000..6fb0d7b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/base.py @@ -0,0 +1,51 @@ +import abc +from typing import Optional + +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata.base import BaseDistribution +from pip._internal.req import InstallRequirement + + +class AbstractDistribution(metaclass=abc.ABCMeta): + """A base class for handling installable artifacts. + + The requirements for anything installable are as follows: + + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + + - we must be able to create a Distribution object exposing the + above metadata. + + - if we need to do work in the build tracker, we must be able to generate a unique + string to identify the requirement in the build tracker. + """ + + def __init__(self, req: InstallRequirement) -> None: + super().__init__() + self.req = req + + @abc.abstractproperty + def build_tracker_id(self) -> Optional[str]: + """A string that uniquely identifies this requirement to the build tracker. + + If None, then this dist has no work to do in the build tracker, and + ``.prepare_distribution_metadata()`` will not be called.""" + raise NotImplementedError() + + @abc.abstractmethod + def get_metadata_distribution(self) -> BaseDistribution: + raise NotImplementedError() + + @abc.abstractmethod + def prepare_distribution_metadata( + self, + finder: PackageFinder, + build_isolation: bool, + check_build_deps: bool, + ) -> None: + raise NotImplementedError() diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py new file mode 100644 index 0000000..ab8d53b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/installed.py @@ -0,0 +1,29 @@ +from typing import Optional + +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution + + +class InstalledDistribution(AbstractDistribution): + """Represents an installed package. + + This does not need any preparation as the required information has already + been computed. + """ + + @property + def build_tracker_id(self) -> Optional[str]: + return None + + def get_metadata_distribution(self) -> BaseDistribution: + assert self.req.satisfied_by is not None, "not actually installed" + return self.req.satisfied_by + + def prepare_distribution_metadata( + self, + finder: PackageFinder, + build_isolation: bool, + check_build_deps: bool, + ) -> None: + pass diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py new file mode 100644 index 0000000..15ff42b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/sdist.py @@ -0,0 +1,156 @@ +import logging +from typing import Iterable, Optional, Set, Tuple + +from pip._internal.build_env import BuildEnvironment +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.exceptions import InstallationError +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution +from pip._internal.utils.subprocess import runner_with_spinner_message + +logger = logging.getLogger(__name__) + + +class SourceDistribution(AbstractDistribution): + """Represents a source distribution. + + The preparation step for these needs metadata for the packages to be + generated, either using PEP 517 or using the legacy `setup.py egg_info`. + """ + + @property + def build_tracker_id(self) -> Optional[str]: + """Identify this requirement uniquely by its link.""" + assert self.req.link + return self.req.link.url_without_fragment + + def get_metadata_distribution(self) -> BaseDistribution: + return self.req.get_dist() + + def prepare_distribution_metadata( + self, + finder: PackageFinder, + build_isolation: bool, + check_build_deps: bool, + ) -> None: + # Load pyproject.toml, to determine whether PEP 517 is to be used + self.req.load_pyproject_toml() + + # Set up the build isolation, if this requirement should be isolated + should_isolate = self.req.use_pep517 and build_isolation + if should_isolate: + # Setup an isolated environment and install the build backend static + # requirements in it. + self._prepare_build_backend(finder) + # Check that if the requirement is editable, it either supports PEP 660 or + # has a setup.py or a setup.cfg. This cannot be done earlier because we need + # to setup the build backend to verify it supports build_editable, nor can + # it be done later, because we want to avoid installing build requirements + # needlessly. Doing it here also works around setuptools generating + # UNKNOWN.egg-info when running get_requires_for_build_wheel on a directory + # without setup.py nor setup.cfg. + self.req.isolated_editable_sanity_check() + # Install the dynamic build requirements. + self._install_build_reqs(finder) + # Check if the current environment provides build dependencies + should_check_deps = self.req.use_pep517 and check_build_deps + if should_check_deps: + pyproject_requires = self.req.pyproject_requires + assert pyproject_requires is not None + conflicting, missing = self.req.build_env.check_requirements( + pyproject_requires + ) + if conflicting: + self._raise_conflicts("the backend dependencies", conflicting) + if missing: + self._raise_missing_reqs(missing) + self.req.prepare_metadata() + + def _prepare_build_backend(self, finder: PackageFinder) -> None: + # Isolate in a BuildEnvironment and install the build-time + # requirements. + pyproject_requires = self.req.pyproject_requires + assert pyproject_requires is not None + + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, pyproject_requires, "overlay", kind="build dependencies" + ) + conflicting, missing = self.req.build_env.check_requirements( + self.req.requirements_to_check + ) + if conflicting: + self._raise_conflicts("PEP 517/518 supported requirements", conflicting) + if missing: + logger.warning( + "Missing build requirements in pyproject.toml for %s.", + self.req, + ) + logger.warning( + "The project does not specify a build backend, and " + "pip cannot fall back to setuptools without %s.", + " and ".join(map(repr, sorted(missing))), + ) + + def _get_build_requires_wheel(self) -> Iterable[str]: + with self.req.build_env: + runner = runner_with_spinner_message("Getting requirements to build wheel") + backend = self.req.pep517_backend + assert backend is not None + with backend.subprocess_runner(runner): + return backend.get_requires_for_build_wheel() + + def _get_build_requires_editable(self) -> Iterable[str]: + with self.req.build_env: + runner = runner_with_spinner_message( + "Getting requirements to build editable" + ) + backend = self.req.pep517_backend + assert backend is not None + with backend.subprocess_runner(runner): + return backend.get_requires_for_build_editable() + + def _install_build_reqs(self, finder: PackageFinder) -> None: + # Install any extra build dependencies that the backend requests. + # This must be done in a second pass, as the pyproject.toml + # dependencies must be installed before we can call the backend. + if ( + self.req.editable + and self.req.permit_editable_wheels + and self.req.supports_pyproject_editable() + ): + build_reqs = self._get_build_requires_editable() + else: + build_reqs = self._get_build_requires_wheel() + conflicting, missing = self.req.build_env.check_requirements(build_reqs) + if conflicting: + self._raise_conflicts("the backend dependencies", conflicting) + self.req.build_env.install_requirements( + finder, missing, "normal", kind="backend dependencies" + ) + + def _raise_conflicts( + self, conflicting_with: str, conflicting_reqs: Set[Tuple[str, str]] + ) -> None: + format_string = ( + "Some build dependencies for {requirement} " + "conflict with {conflicting_with}: {description}." + ) + error_message = format_string.format( + requirement=self.req, + conflicting_with=conflicting_with, + description=", ".join( + f"{installed} is incompatible with {wanted}" + for installed, wanted in sorted(conflicting_reqs) + ), + ) + raise InstallationError(error_message) + + def _raise_missing_reqs(self, missing: Set[str]) -> None: + format_string = ( + "Some build dependencies for {requirement} are missing: {missing}." + ) + error_message = format_string.format( + requirement=self.req, missing=", ".join(map(repr, sorted(missing))) + ) + raise InstallationError(error_message) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py new file mode 100644 index 0000000..eb16e25 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/distributions/wheel.py @@ -0,0 +1,40 @@ +from typing import Optional + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import ( + BaseDistribution, + FilesystemWheel, + get_wheel_distribution, +) + + +class WheelDistribution(AbstractDistribution): + """Represents a wheel distribution. + + This does not need any preparation as wheels can be directly unpacked. + """ + + @property + def build_tracker_id(self) -> Optional[str]: + return None + + def get_metadata_distribution(self) -> BaseDistribution: + """Loads the metadata from the wheel file into memory and returns a + Distribution that uses it, not relying on the wheel file or + requirement. + """ + assert self.req.local_file_path, "Set as part of preparation during download" + assert self.req.name, "Wheels are never unnamed" + wheel = FilesystemWheel(self.req.local_file_path) + return get_wheel_distribution(wheel, canonicalize_name(self.req.name)) + + def prepare_distribution_metadata( + self, + finder: PackageFinder, + build_isolation: bool, + check_build_deps: bool, + ) -> None: + pass diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/exceptions.py b/.venv/lib/python3.12/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000..5007a62 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,728 @@ +"""Exceptions used throughout package. + +This module MUST NOT try to import from anything within `pip._internal` to +operate. This is expected to be importable from any/all files within the +subpackage and, thus, should not depend on them. +""" + +import configparser +import contextlib +import locale +import logging +import pathlib +import re +import sys +from itertools import chain, groupby, repeat +from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union + +from pip._vendor.requests.models import Request, Response +from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult +from pip._vendor.rich.markup import escape +from pip._vendor.rich.text import Text + +if TYPE_CHECKING: + from hashlib import _Hash + from typing import Literal + + from pip._internal.metadata import BaseDistribution + from pip._internal.req.req_install import InstallRequirement + +logger = logging.getLogger(__name__) + + +# +# Scaffolding +# +def _is_kebab_case(s: str) -> bool: + return re.match(r"^[a-z]+(-[a-z]+)*$", s) is not None + + +def _prefix_with_indent( + s: Union[Text, str], + console: Console, + *, + prefix: str, + indent: str, +) -> Text: + if isinstance(s, Text): + text = s + else: + text = console.render_str(s) + + return console.render_str(prefix, overflow="ignore") + console.render_str( + f"\n{indent}", overflow="ignore" + ).join(text.split(allow_blank=True)) + + +class PipError(Exception): + """The base pip error.""" + + +class DiagnosticPipError(PipError): + """An error, that presents diagnostic information to the user. + + This contains a bunch of logic, to enable pretty presentation of our error + messages. Each error gets a unique reference. Each error can also include + additional context, a hint and/or a note -- which are presented with the + main error message in a consistent style. + + This is adapted from the error output styling in `sphinx-theme-builder`. + """ + + reference: str + + def __init__( + self, + *, + kind: 'Literal["error", "warning"]' = "error", + reference: Optional[str] = None, + message: Union[str, Text], + context: Optional[Union[str, Text]], + hint_stmt: Optional[Union[str, Text]], + note_stmt: Optional[Union[str, Text]] = None, + link: Optional[str] = None, + ) -> None: + # Ensure a proper reference is provided. + if reference is None: + assert hasattr(self, "reference"), "error reference not provided!" + reference = self.reference + assert _is_kebab_case(reference), "error reference must be kebab-case!" + + self.kind = kind + self.reference = reference + + self.message = message + self.context = context + + self.note_stmt = note_stmt + self.hint_stmt = hint_stmt + + self.link = link + + super().__init__(f"<{self.__class__.__name__}: {self.reference}>") + + def __repr__(self) -> str: + return ( + f"<{self.__class__.__name__}(" + f"reference={self.reference!r}, " + f"message={self.message!r}, " + f"context={self.context!r}, " + f"note_stmt={self.note_stmt!r}, " + f"hint_stmt={self.hint_stmt!r}" + ")>" + ) + + def __rich_console__( + self, + console: Console, + options: ConsoleOptions, + ) -> RenderResult: + colour = "red" if self.kind == "error" else "yellow" + + yield f"[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]" + yield "" + + if not options.ascii_only: + # Present the main message, with relevant context indented. + if self.context is not None: + yield _prefix_with_indent( + self.message, + console, + prefix=f"[{colour}]×[/] ", + indent=f"[{colour}]│[/] ", + ) + yield _prefix_with_indent( + self.context, + console, + prefix=f"[{colour}]╰─>[/] ", + indent=f"[{colour}] [/] ", + ) + else: + yield _prefix_with_indent( + self.message, + console, + prefix="[red]×[/] ", + indent=" ", + ) + else: + yield self.message + if self.context is not None: + yield "" + yield self.context + + if self.note_stmt is not None or self.hint_stmt is not None: + yield "" + + if self.note_stmt is not None: + yield _prefix_with_indent( + self.note_stmt, + console, + prefix="[magenta bold]note[/]: ", + indent=" ", + ) + if self.hint_stmt is not None: + yield _prefix_with_indent( + self.hint_stmt, + console, + prefix="[cyan bold]hint[/]: ", + indent=" ", + ) + + if self.link is not None: + yield "" + yield f"Link: {self.link}" + + +# +# Actual Errors +# +class ConfigurationError(PipError): + """General exception in configuration""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class MissingPyProjectBuildRequires(DiagnosticPipError): + """Raised when pyproject.toml has `build-system`, but no `build-system.requires`.""" + + reference = "missing-pyproject-build-system-requires" + + def __init__(self, *, package: str) -> None: + super().__init__( + message=f"Can not process {escape(package)}", + context=Text( + "This package has an invalid pyproject.toml file.\n" + "The [build-system] table is missing the mandatory `requires` key." + ), + note_stmt="This is an issue with the package mentioned above, not pip.", + hint_stmt=Text("See PEP 518 for the detailed specification."), + ) + + +class InvalidPyProjectBuildRequires(DiagnosticPipError): + """Raised when pyproject.toml an invalid `build-system.requires`.""" + + reference = "invalid-pyproject-build-system-requires" + + def __init__(self, *, package: str, reason: str) -> None: + super().__init__( + message=f"Can not process {escape(package)}", + context=Text( + "This package has an invalid `build-system.requires` key in " + f"pyproject.toml.\n{reason}" + ), + note_stmt="This is an issue with the package mentioned above, not pip.", + hint_stmt=Text("See PEP 518 for the detailed specification."), + ) + + +class NoneMetadataError(PipError): + """Raised when accessing a Distribution's "METADATA" or "PKG-INFO". + + This signifies an inconsistency, when the Distribution claims to have + the metadata file (if not, raise ``FileNotFoundError`` instead), but is + not actually able to produce its content. This may be due to permission + errors. + """ + + def __init__( + self, + dist: "BaseDistribution", + metadata_name: str, + ) -> None: + """ + :param dist: A Distribution object. + :param metadata_name: The name of the metadata being accessed + (can be "METADATA" or "PKG-INFO"). + """ + self.dist = dist + self.metadata_name = metadata_name + + def __str__(self) -> str: + # Use `dist` in the error message because its stringification + # includes more information, like the version and location. + return f"None {self.metadata_name} metadata found for distribution: {self.dist}" + + +class UserInstallationInvalid(InstallationError): + """A --user install is requested on an environment without user site.""" + + def __str__(self) -> str: + return "User base directory is not specified" + + +class InvalidSchemeCombination(InstallationError): + def __str__(self) -> str: + before = ", ".join(str(a) for a in self.args[:-1]) + return f"Cannot set {before} and {self.args[-1]} together" + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class NetworkConnectionError(PipError): + """HTTP connection error""" + + def __init__( + self, + error_msg: str, + response: Optional[Response] = None, + request: Optional[Request] = None, + ) -> None: + """ + Initialize NetworkConnectionError with `request` and `response` + objects. + """ + self.response = response + self.request = request + self.error_msg = error_msg + if ( + self.response is not None + and not self.request + and hasattr(response, "request") + ): + self.request = self.response.request + super().__init__(error_msg, response, request) + + def __str__(self) -> str: + return str(self.error_msg) + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class InvalidWheel(InstallationError): + """Invalid (e.g. corrupt) wheel.""" + + def __init__(self, location: str, name: str): + self.location = location + self.name = name + + def __str__(self) -> str: + return f"Wheel '{self.name}' located at {self.location} is invalid." + + +class MetadataInconsistent(InstallationError): + """Built metadata contains inconsistent information. + + This is raised when the metadata contains values (e.g. name and version) + that do not match the information previously obtained from sdist filename, + user-supplied ``#egg=`` value, or an install requirement name. + """ + + def __init__( + self, ireq: "InstallRequirement", field: str, f_val: str, m_val: str + ) -> None: + self.ireq = ireq + self.field = field + self.f_val = f_val + self.m_val = m_val + + def __str__(self) -> str: + return ( + f"Requested {self.ireq} has inconsistent {self.field}: " + f"expected {self.f_val!r}, but metadata has {self.m_val!r}" + ) + + +class InstallationSubprocessError(DiagnosticPipError, InstallationError): + """A subprocess call failed.""" + + reference = "subprocess-exited-with-error" + + def __init__( + self, + *, + command_description: str, + exit_code: int, + output_lines: Optional[List[str]], + ) -> None: + if output_lines is None: + output_prompt = Text("See above for output.") + else: + output_prompt = ( + Text.from_markup(f"[red][{len(output_lines)} lines of output][/]\n") + + Text("".join(output_lines)) + + Text.from_markup(R"[red]\[end of output][/]") + ) + + super().__init__( + message=( + f"[green]{escape(command_description)}[/] did not run successfully.\n" + f"exit code: {exit_code}" + ), + context=output_prompt, + hint_stmt=None, + note_stmt=( + "This error originates from a subprocess, and is likely not a " + "problem with pip." + ), + ) + + self.command_description = command_description + self.exit_code = exit_code + + def __str__(self) -> str: + return f"{self.command_description} exited with {self.exit_code}" + + +class MetadataGenerationFailed(InstallationSubprocessError, InstallationError): + reference = "metadata-generation-failed" + + def __init__( + self, + *, + package_details: str, + ) -> None: + super(InstallationSubprocessError, self).__init__( + message="Encountered error while generating package metadata.", + context=escape(package_details), + hint_stmt="See above for details.", + note_stmt="This is an issue with the package mentioned above, not pip.", + ) + + def __str__(self) -> str: + return "metadata generation failed" + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self) -> None: + self.errors: List["HashError"] = [] + + def append(self, error: "HashError") -> None: + self.errors.append(error) + + def __str__(self) -> str: + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return "\n".join(lines) + return "" + + def __bool__(self) -> bool: + return bool(self.errors) + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + + req: Optional["InstallRequirement"] = None + head = "" + order: int = -1 + + def body(self) -> str: + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + its link already populated by the resolver's _populate_link(). + + """ + return f" {self._requirement_name()}" + + def __str__(self) -> str: + return f"{self.head}\n{self.body()}" + + def _requirement_name(self) -> str: + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else "unknown package" + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ( + "Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:" + ) + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ( + "Can't verify hashes for these file:// requirements because they " + "point to directories:" + ) + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ( + "Hashes are required in --require-hashes mode, but they are " + "missing from some requirements. Here is a list of those " + "requirements along with the hashes their downloaded archives " + "actually had. Add lines like these to your requirements files to " + "prevent tampering. (If you did not enable --require-hashes " + "manually, note that it turns on automatically when any package " + "has a hash.)" + ) + + def __init__(self, gotten_hash: str) -> None: + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self) -> str: + # Dodge circular import. + from pip._internal.utils.hashes import FAVORITE_HASH + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = ( + self.req.original_link + if self.req.is_direct + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, "req", None) + ) + return " {} --hash={}:{}".format( + package or "unknown package", FAVORITE_HASH, self.gotten_hash + ) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ( + "In --require-hashes mode, all requirements must have their " + "versions pinned with ==. These do not:" + ) + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + + order = 4 + head = ( + "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS " + "FILE. If you have updated the package versions, please update " + "the hashes. Otherwise, examine the package contents carefully; " + "someone may have tampered with them." + ) + + def __init__(self, allowed: Dict[str, List[str]], gots: Dict[str, "_Hash"]) -> None: + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self) -> str: + return f" {self._requirement_name()}:\n{self._hash_comparison()}" + + def _hash_comparison(self) -> str: + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + + def hash_then_or(hash_name: str) -> "chain[str]": + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(" or")) + + lines: List[str] = [] + for hash_name, expecteds in self.allowed.items(): + prefix = hash_then_or(hash_name) + lines.extend((f" Expected {next(prefix)} {e}") for e in expecteds) + lines.append( + f" Got {self.gots[hash_name].hexdigest()}\n" + ) + return "\n".join(lines) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" + + +class ConfigurationFileCouldNotBeLoaded(ConfigurationError): + """When there are errors while loading a configuration file""" + + def __init__( + self, + reason: str = "could not be loaded", + fname: Optional[str] = None, + error: Optional[configparser.Error] = None, + ) -> None: + super().__init__(error) + self.reason = reason + self.fname = fname + self.error = error + + def __str__(self) -> str: + if self.fname is not None: + message_part = f" in {self.fname}." + else: + assert self.error is not None + message_part = f".\n{self.error}\n" + return f"Configuration file {self.reason}{message_part}" + + +_DEFAULT_EXTERNALLY_MANAGED_ERROR = f"""\ +The Python environment under {sys.prefix} is managed externally, and may not be +manipulated by the user. Please use specific tooling from the distributor of +the Python installation to interact with this environment instead. +""" + + +class ExternallyManagedEnvironment(DiagnosticPipError): + """The current environment is externally managed. + + This is raised when the current environment is externally managed, as + defined by `PEP 668`_. The ``EXTERNALLY-MANAGED`` configuration is checked + and displayed when the error is bubbled up to the user. + + :param error: The error message read from ``EXTERNALLY-MANAGED``. + """ + + reference = "externally-managed-environment" + + def __init__(self, error: Optional[str]) -> None: + if error is None: + context = Text(_DEFAULT_EXTERNALLY_MANAGED_ERROR) + else: + context = Text(error) + super().__init__( + message="This environment is externally managed", + context=context, + note_stmt=( + "If you believe this is a mistake, please contact your " + "Python installation or OS distribution provider. " + "You can override this, at the risk of breaking your Python " + "installation or OS, by passing --break-system-packages." + ), + hint_stmt=Text("See PEP 668 for the detailed specification."), + ) + + @staticmethod + def _iter_externally_managed_error_keys() -> Iterator[str]: + # LC_MESSAGES is in POSIX, but not the C standard. The most common + # platform that does not implement this category is Windows, where + # using other categories for console message localization is equally + # unreliable, so we fall back to the locale-less vendor message. This + # can always be re-evaluated when a vendor proposes a new alternative. + try: + category = locale.LC_MESSAGES + except AttributeError: + lang: Optional[str] = None + else: + lang, _ = locale.getlocale(category) + if lang is not None: + yield f"Error-{lang}" + for sep in ("-", "_"): + before, found, _ = lang.partition(sep) + if not found: + continue + yield f"Error-{before}" + yield "Error" + + @classmethod + def from_config( + cls, + config: Union[pathlib.Path, str], + ) -> "ExternallyManagedEnvironment": + parser = configparser.ConfigParser(interpolation=None) + try: + parser.read(config, encoding="utf-8") + section = parser["externally-managed"] + for key in cls._iter_externally_managed_error_keys(): + with contextlib.suppress(KeyError): + return cls(section[key]) + except KeyError: + pass + except (OSError, UnicodeDecodeError, configparser.ParsingError): + from pip._internal.utils._log import VERBOSE + + exc_info = logger.isEnabledFor(VERBOSE) + logger.warning("Failed to read %s", config, exc_info=exc_info) + return cls(None) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py new file mode 100644 index 0000000..7a17b7b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/index/__init__.py @@ -0,0 +1,2 @@ +"""Index interaction code +""" 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 new file mode 100644 index 0000000000000000000000000000000000000000..01162ed0a5cd715bec86bda9b799a0409fa053f6 GIT binary patch literal 253 zcmXv}u}T9$6x=<7hCpgNNt+_~NNhz=Y%CP9v{^R0kIU1$_majr;IHT(SPA}s zBwZ@^M5lQ(4~Cg}ACE^WM2n=|N6h%^^0A3Oa(;_9W#>Z$l-5>^>1MVJ> z!Ym*|4if{G9f7_G#r6u0H!)e#mZ_|_OmDdyIqNICsRScH1-M0JxfP|do1`j30bbe6 zADiFzx_b_2BzY_OBTeGH*Zq!u@AY?o-o*5x(}Yg_Fc zcb${D5GV7pEzXB{mfJ#hmJ1<)<&Kbp<<5|k<*ty6r^ncsTQO$GD>n1J$Jw)O zc(z^HpP$VMxl=iT{wFY-F7!O9oRque0G`w-o#tA4t_rUhgK(i!Y?K`sC6FKaG4`w% z&-#>#{K${HIr*R*R66B;r4@Viu(4P2PV7>Ryo<)BKej$m-hI^(I*lF=vEA9DoWTqp z!y5PE`4Ah0?S;}L2iUBf@;=P=W9-cj;LZD$#{8SlvZn{|^uhcbKR#g#9LjvmbACjT z5|LO^8XH%~Qko*AhNbjKOp_8Rc|5L2D5X=9@+_-69ZOysNX6qyG@Vj|QKJ;rQsZh= z(Ygac&)?Cu3hP8Ag zJx;In$Fwl@h+RfSEUw(-^*U9NBhhp?GM*j@538v}cwCL6sr_rc;wQOL>bK#tz<<=Wp#ry)yhRi={!1N`T-qYb zMKui2`O6$y?49JJHl91j2^>Jup1F8BmAWKh3#F$9&PiHyL`f)`1OQC~QlEU{bkO6G zaNz@DeE?1dziBj+i#qg5=s%Q70)7dc6;Ov|Vi)#I6Qpt;{!AJX!0zfV%6G}4OAH1w2FZah|7y8F0(j%$lp5U(C{aP%o^o>QL zm++Of{;}9te>j!|CIGnmV@X+gw%^1xVBOe+UL9uZ2m@Tg#)Q-zn9zRwX~&REbL$?i zw&D7YYdhZMY>t*0=c>2z#Rq2}eBq%L@0P50%R=Xy-oTCM4?1oQ|LKlw@W|`i-UuFF zt!@6|nVbXdb571zwT6MXC?(eAGz)9JD0~4Bq;O!YT*xN#ie0wh&n^qY&KYi)mmODq zAz_AlmJd13aRH}Z86HZdkDfgR#7biubYIjcgwqpaitaPfG%PDxRPDgz;7fpus5n!r{-3N8)CU zT}!L%#d?J~t*8=@hr>5{6*vo|Lh^_)y&#GzCA4$KRIYM4o81vuIG5v)-L|vrb>Tg_ zyGIF|3hPAl)1a9pge3nteu6=-b087OH(sOp4KLJ?G-Z7xkQV;KNXwLM(pHYV$aago zsQiRbOk6MzCL0#sN2O6;={?<9bND;Kn&igNfGH>ECHs(h60#_JQ@3DQ7hsLn)FO5oIGPvCpHeSLLy`!j+;!S zG34b)JSJOpeN<;quKT{?DeA5#lb4dID@o8!cUpNi-9M5}#4{b>nDJONLOiVha#9A} z7>flpHp}4UT|21J{f}xis-==c0hjJFwn5YF7r{3>v4d(CB?MTy%S2w?nNUE{NhH%;u9B;3TdD2H)^@Da zc4ce3ZdBfOFV#Leefnb-Pn^qO05gj)xHL z*_E#S*{=O7T}QHAN0vMfFA5JcAdli5`O9LJYXmR_;x@rUUJ&WSojia3>=0H*bXGk; z$%9C4at6LIYCtnrDGB3GBgT1^TNOm31w99+F{FiPo;u+vHKd(c`!SMY>Q;_3mqVIK z&O(Y$VXP_JlzmE=a!ffVL7Ol0swbW2(-!CcJfWW(uiJ1{M2_J++CJ*nGAlyjC$NxZ#GGO)h!6^DLd0c{s zkVm1*krN`XFSNEwKn?q7wRKm>FLJW*FY`jL z?2w(`vc2lEMw_ywYpu2rQOwn1hV^Bie)zn_a`OhCY_P^E-Feai6Orseb!9g18|fwjuun(6tg+$|^`at?rWTZ*AhvCA z?GhSeGBtx=Mw zi%beB>|7oYdab=8D$Z2i_b)O-jxImWQOz9^M(;m8s54I>wWoyE5)X!qdUz)owv!-vES;$shyXbHNMKS^(6QA= z=}q;=6+>e*XCXy2%td_|b?PHX0zQ>ARFzN^#1*hWk~brlx-*`-coBMoLsl+~UsPME z-2k-{MpLn5fpoJsVQE{b0i)l4fKsUdR2POk6*d?hxP&1xsu8psr?#&m0ab3{#Ky0s z=LfzzvCS6L$eR9 zir%{oE!Sh$V(;37>Y6pXv#~PQ&20|c+tE<%$vHS*;~LlOb50Mg@8ulzjBbMmI2z~D z%R-wu@~YU*-nJxeSrvUR`euDAViV~xn{PBPiF?*vg2S2fL1CPJ+z@kUhfCB2BI}Ic z&!a%8iE#N(37$BS|+aRuILCc`84bd zxK_d++$FUpzB=X2rK9>)W#A>zLtJ+d3A8vTb{2_?ci9I%rKT z*$U=kOEujyu3vj=?$kESUHR&vMPK`>*KcrejG4bIp1elNazRp-U9ufENEiVW*w+;Y zET|6L!?v!thHbJFWf3MGQLkf2Np?LlYY}cjbTQR_TmCH^M`Z>$q-Owd_;PxtW_yey zVHPTWUXqyL)ZOR;I#3fO{13-rIHNt*$cPBh>7sJertU)%HHk!{7nw?lq0nzCH^aPI z|2p*{8Eu+d_1CWWJKykkE{dIt{?669rWxVOZnkB*gVDeeL7z-0Y(-y0p;-2ry?0hz`a4UG)z3s)CICC>cWLoDy|a2)a@`1s?VaNK1K;`lVR<-L?uf8E&epp z7p`(^Hk+e4C-RPu^Yh1Z9OVn?HJ0a`yrW~jV;wgg#wgU6s80SO&6KXStw@Sc9W+AMvEdd8Sn7T ze}?wVcT2oo_gW?Gl+V~HYOC9ZOD*hq^!`iKeu4Xftr$kjL8#2Xn&(2YJ)MVFSxD#6 zx}eG!du`cQU%@-*`l79%LX|Us&E^#x*$EQ;p(D@OK&Zv5K5J|;(m^?JXLKg=oq!Vq zBDpj?o@CAu$fJ=o$@OI1g0OCRZ$3$%|I-q=HrV4LSnrh>MJ7p>4QUMp`#c1o@pxM5 zzNAdRn15kHDo0TQz0!CxPD%>d5{j{i4fhPu_GiGkVQiyfkiD&h{!51sOUEN|O?k!; zP03y5g6ycpG79Wf#4m_7fn7k%>%az=kRXCH(&yHQS2I&|p6l4SS#Y6ppV=ZhR&Eg= zn=ZtmBQZPyfxU_$)#s5Iz&~xM?yf5;1Yu0sSy29{}~cUL2Pcc8|oL37;@0GxhH;eondQl zcK`l+WA%B9M%{q~dvw?9U-52z!@Kp@wJpo7{kMdr)_qI04=nl~03pZjrN4Ueh36K< zO>EadL31EbS!%)%WANAIm>#Ap+I6X6Kw;ta9Cn9$yGQ zAD(++$T({=*SaXQd>CvHSt6uZI}f4hagGP= zE)N~$?El4NrJQgt=dCE}X9~8R(Y#Fr?=>&H(_@tjxdm0scaS$x7V2f&#k}nw4o)!0 zDSJ9^=^3pmw6sc-a4(J4Sa*folFX)rNnx7*Qs<=b4gNaMb6?s4H>SPFwo|^hi62uA zj9$ie^DM8yB`OOr;5tSLjW=B7S3gR+=!R?3Y3+%XGdZ%%il$t6SKEf}xHaZ7>6#Kz zzxn<3*vBo{M|aUaO7Cwq>7H~=imy5>tCP8JoAdkDLHqV6_A7kAnfa6lW)q2VdN7-U z0RlXN5q!y%n+!YDL`;dxQVfVOUXQ-6<l$Km>g; zcj6q$OS+RWUsb(Mb08Vb7|UsnML0rxXA1v~(EN(fz>^cFe_+n-@Gr#TS-j{lp{9Q8wG@t7yubrI##KMK8 zy1=`f;BK55yi?nF{m8W=3)^l8-xqI+OSQXZ2B9&{Os-Znt=2TW@L0}`b|5=&)Ks+2 z559czrISk)+ozAC(Th{FQ*ZdY7F^ROS7GCL{Q1Y{gzLU*zLlzuY*oj?$)&2kWg&Ri zQ~6@|Z1?oQs=xll&&_`B4gdCq4)myLx*oh1#55eWGfvEQ#V=+3(n7z_Ha>Z@C=ta-t6&y9A!(}=cxez0?}n~$QDzbrzsM&F=V1`gQ~pyP$-TYxO=fZFR+ z9E7d119wiv1!+qh_Q`I9?1-`p3b9T0Ku&f~h%hh_*UjvELK%bD!=RO-6)YQ&)D1W$ zh=jNki6e@@u)PwH!(OU>4SfyREPQvyKZ!CAMQI(@MXIeGe`_T0k`4_$*{Xx(w^(Z? zT36&W7jQ>C*)<7D70!#N1XL&-rVomtRM7FPq(+2e~$inK(R zjY9zOjNW7zW)kF;kyvzu^<>7MBBs! z(_ZGUQl5>FOS!N>idOlcFAsf(splV5&tThFj-e{<#b?db7t7Ci^M-&A%alhDgUwu1 z`;71U6wd# zGwiC2pk2Z9Vr|@o7>u+EU9fJ$?Ag<1nmjpaNC)p&82sMJ@0?un_brNjY?3$m^Ps5v zQD5-j>KIW@MGp0eB#(~@NDuElNLmdA}IML7iYs*oV-hlq|<8b!Z^N;fe!|+>%bd_O3Vs*xI2rg z4Us}1c3*}%Qmk`tj1-a~%od2=MY<~xJt!qZAI|_=`dM$Rb^NMUhcu`3_xKp zOAKB87f2xeGrVL^AHV}ck135FDwPuQc-TQu1^XOvWh;8<#x{6em&N|OO|1+8yLK>i z-2WiVj)rAIx2Gc)bq8&jRuV)-nixaLe}e*X0+}c4pV|$S#FLHJbQ_*i`wdsYhdE`R zv_CI0u90su0ZR@Ui{P~wZ{nDowH|<`9uygp@;f4C8+Ab#MEc)A5rEPP<*DG>% zu1dOFQ8hR4#n7s+>c!Kur&oOKSzr4f3ky$v*ZZcg7cDBA3?#Q- zTN{jUaZc+*3#_|ULR6u9)9m9jgLB&uGRR&!i%D*F)g)nRu^rDsr;7{+7nH-oo-jc$o_qb-YR zV?s9dD&5Ii1ms>)N^NrLPta_DcJUYru-*VKdTw;D^c>9g99;50G=1`pw{cahGQ6qz z!u;l}*u7fa4Aq3l#A<8by4UG*61X}I`hX$XMR|Ukr-91=u(FiA92GB)#FXtM0F-Qf z-Y88pUTrEEqsxVi$pZ0!VsK4+j476nzAP7+Qs63|!bdlP*AT#lf0OVcGHx=H&f90o zS!71*t#=mkqIonFo?4}{mX-I}OVrbduPI|$k@GDz2T<0TsbG8f;Va){y}OX3Z*i

JVA0d&(v?~=JG@i}qA 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 bqxB4Lezv{zl-w}krp$GM{ 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/index/__pycache__/sources.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9eb82b6d836fbde27769e09165c07208b391d845 GIT binary patch literal 12625 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 S9g$T}BySttYD6aA$o~iCZ}B7m literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/collector.py b/.venv/lib/python3.12/site-packages/pip/_internal/index/collector.py new file mode 100644 index 0000000..08c8bdd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/index/collector.py @@ -0,0 +1,507 @@ +""" +The main purpose of this module is to expose LinkCollector.collect_sources(). +""" + +import collections +import email.message +import functools +import itertools +import json +import logging +import os +import urllib.parse +import urllib.request +from html.parser import HTMLParser +from optparse import Values +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Iterable, + List, + MutableMapping, + NamedTuple, + Optional, + Sequence, + Tuple, + Union, +) + +from pip._vendor import requests +from pip._vendor.requests import Response +from pip._vendor.requests.exceptions import RetryError, SSLError + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.filetypes import is_archive_file +from pip._internal.utils.misc import redact_auth_from_url +from pip._internal.vcs import vcs + +from .sources import CandidatesFromPage, LinkSource, build_source + +if TYPE_CHECKING: + from typing import Protocol +else: + Protocol = object + +logger = logging.getLogger(__name__) + +ResponseHeaders = MutableMapping[str, str] + + +def _match_vcs_scheme(url: str) -> Optional[str]: + """Look for VCS schemes in the URL. + + Returns the matched VCS scheme, or None if there's no match. + """ + for scheme in vcs.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in "+:": + return scheme + return None + + +class _NotAPIContent(Exception): + def __init__(self, content_type: str, request_desc: str) -> None: + super().__init__(content_type, request_desc) + self.content_type = content_type + self.request_desc = request_desc + + +def _ensure_api_header(response: Response) -> None: + """ + Check the Content-Type header to ensure the response contains a Simple + API Response. + + Raises `_NotAPIContent` if the content type is not a valid content-type. + """ + content_type = response.headers.get("Content-Type", "Unknown") + + content_type_l = content_type.lower() + if content_type_l.startswith( + ( + "text/html", + "application/vnd.pypi.simple.v1+html", + "application/vnd.pypi.simple.v1+json", + ) + ): + return + + raise _NotAPIContent(content_type, response.request.method) + + +class _NotHTTP(Exception): + pass + + +def _ensure_api_response(url: str, session: PipSession) -> None: + """ + Send a HEAD request to the URL, and ensure the response contains a simple + API Response. + + Raises `_NotHTTP` if the URL is not available for a HEAD request, or + `_NotAPIContent` if the content type is not a valid content type. + """ + scheme, netloc, path, query, fragment = urllib.parse.urlsplit(url) + if scheme not in {"http", "https"}: + raise _NotHTTP() + + resp = session.head(url, allow_redirects=True) + raise_for_status(resp) + + _ensure_api_header(resp) + + +def _get_simple_response(url: str, session: PipSession) -> Response: + """Access an Simple API response with GET, and return the response. + + This consists of three parts: + + 1. If the URL looks suspiciously like an archive, send a HEAD first to + check the Content-Type is HTML or Simple API, to avoid downloading a + large file. Raise `_NotHTTP` if the content type cannot be determined, or + `_NotAPIContent` if it is not HTML or a Simple API. + 2. Actually perform the request. Raise HTTP exceptions on network failures. + 3. Check the Content-Type header to make sure we got a Simple API response, + and raise `_NotAPIContent` otherwise. + """ + if is_archive_file(Link(url).filename): + _ensure_api_response(url, session=session) + + logger.debug("Getting page %s", redact_auth_from_url(url)) + + resp = session.get( + url, + headers={ + "Accept": ", ".join( + [ + "application/vnd.pypi.simple.v1+json", + "application/vnd.pypi.simple.v1+html; q=0.1", + "text/html; q=0.01", + ] + ), + # We don't want to blindly returned cached data for + # /simple/, because authors generally expecting that + # twine upload && pip install will function, but if + # they've done a pip install in the last ~10 minutes + # it won't. Thus by setting this to zero we will not + # blindly use any cached data, however the benefit of + # using max-age=0 instead of no-cache, is that we will + # still support conditional requests, so we will still + # minimize traffic sent in cases where the page hasn't + # changed at all, we will just always incur the round + # trip for the conditional GET now instead of only + # once per 10 minutes. + # For more information, please see pypa/pip#5670. + "Cache-Control": "max-age=0", + }, + ) + raise_for_status(resp) + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is a + # Simple API response or not. However we can check after we've + # downloaded it. + _ensure_api_header(resp) + + logger.debug( + "Fetched page %s as %s", + redact_auth_from_url(url), + resp.headers.get("Content-Type", "Unknown"), + ) + + return resp + + +def _get_encoding_from_headers(headers: ResponseHeaders) -> Optional[str]: + """Determine if we have any encoding information in our headers.""" + if headers and "Content-Type" in headers: + m = email.message.Message() + m["content-type"] = headers["Content-Type"] + charset = m.get_param("charset") + if charset: + return str(charset) + return None + + +class CacheablePageContent: + def __init__(self, page: "IndexContent") -> None: + assert page.cache_link_parsing + self.page = page + + def __eq__(self, other: object) -> bool: + return isinstance(other, type(self)) and self.page.url == other.page.url + + def __hash__(self) -> int: + return hash(self.page.url) + + +class ParseLinks(Protocol): + def __call__(self, page: "IndexContent") -> Iterable[Link]: + ... + + +def with_cached_index_content(fn: ParseLinks) -> ParseLinks: + """ + Given a function that parses an Iterable[Link] from an IndexContent, cache the + function's result (keyed by CacheablePageContent), unless the IndexContent + `page` has `page.cache_link_parsing == False`. + """ + + @functools.lru_cache(maxsize=None) + def wrapper(cacheable_page: CacheablePageContent) -> List[Link]: + return list(fn(cacheable_page.page)) + + @functools.wraps(fn) + def wrapper_wrapper(page: "IndexContent") -> List[Link]: + if page.cache_link_parsing: + return wrapper(CacheablePageContent(page)) + return list(fn(page)) + + return wrapper_wrapper + + +@with_cached_index_content +def parse_links(page: "IndexContent") -> Iterable[Link]: + """ + Parse a Simple API's Index Content, and yield its anchor elements as Link objects. + """ + + content_type_l = page.content_type.lower() + if content_type_l.startswith("application/vnd.pypi.simple.v1+json"): + data = json.loads(page.content) + for file in data.get("files", []): + link = Link.from_json(file, page.url) + if link is None: + continue + yield link + return + + parser = HTMLLinkParser(page.url) + encoding = page.encoding or "utf-8" + parser.feed(page.content.decode(encoding)) + + url = page.url + base_url = parser.base_url or url + for anchor in parser.anchors: + link = Link.from_element(anchor, page_url=url, base_url=base_url) + if link is None: + continue + yield link + + +class IndexContent: + """Represents one response (or page), along with its URL""" + + def __init__( + self, + content: bytes, + content_type: str, + encoding: Optional[str], + url: str, + cache_link_parsing: bool = True, + ) -> None: + """ + :param encoding: the encoding to decode the given content. + :param url: the URL from which the HTML was downloaded. + :param cache_link_parsing: whether links parsed from this page's url + should be cached. PyPI index urls should + have this set to False, for example. + """ + self.content = content + self.content_type = content_type + self.encoding = encoding + self.url = url + self.cache_link_parsing = cache_link_parsing + + def __str__(self) -> str: + return redact_auth_from_url(self.url) + + +class HTMLLinkParser(HTMLParser): + """ + HTMLParser that keeps the first base HREF and a list of all anchor + elements' attributes. + """ + + def __init__(self, url: str) -> None: + super().__init__(convert_charrefs=True) + + self.url: str = url + self.base_url: Optional[str] = None + self.anchors: List[Dict[str, Optional[str]]] = [] + + def handle_starttag(self, tag: str, attrs: List[Tuple[str, Optional[str]]]) -> None: + if tag == "base" and self.base_url is None: + href = self.get_href(attrs) + if href is not None: + self.base_url = href + elif tag == "a": + self.anchors.append(dict(attrs)) + + def get_href(self, attrs: List[Tuple[str, Optional[str]]]) -> Optional[str]: + for name, value in attrs: + if name == "href": + return value + return None + + +def _handle_get_simple_fail( + link: Link, + reason: Union[str, Exception], + meth: Optional[Callable[..., None]] = None, +) -> None: + if meth is None: + meth = logger.debug + meth("Could not fetch URL %s: %s - skipping", link, reason) + + +def _make_index_content( + response: Response, cache_link_parsing: bool = True +) -> IndexContent: + encoding = _get_encoding_from_headers(response.headers) + return IndexContent( + response.content, + response.headers["Content-Type"], + encoding=encoding, + url=response.url, + cache_link_parsing=cache_link_parsing, + ) + + +def _get_index_content(link: Link, *, session: PipSession) -> Optional["IndexContent"]: + url = link.url.split("#", 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + vcs_scheme = _match_vcs_scheme(url) + if vcs_scheme: + logger.warning( + "Cannot look at %s URL %s because it does not support lookup as web pages.", + vcs_scheme, + link, + ) + return None + + # Tack index.html onto file:// URLs that point to directories + scheme, _, path, _, _, _ = urllib.parse.urlparse(url) + if scheme == "file" and os.path.isdir(urllib.request.url2pathname(path)): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith("/"): + url += "/" + # TODO: In the future, it would be nice if pip supported PEP 691 + # style responses in the file:// URLs, however there's no + # standard file extension for application/vnd.pypi.simple.v1+json + # so we'll need to come up with something on our own. + url = urllib.parse.urljoin(url, "index.html") + logger.debug(" file: URL is directory, getting %s", url) + + try: + resp = _get_simple_response(url, session=session) + except _NotHTTP: + logger.warning( + "Skipping page %s because it looks like an archive, and cannot " + "be checked by a HTTP HEAD request.", + link, + ) + except _NotAPIContent as exc: + logger.warning( + "Skipping page %s because the %s request got Content-Type: %s. " + "The only supported Content-Types are application/vnd.pypi.simple.v1+json, " + "application/vnd.pypi.simple.v1+html, and text/html", + link, + exc.request_desc, + exc.content_type, + ) + except NetworkConnectionError as exc: + _handle_get_simple_fail(link, exc) + except RetryError as exc: + _handle_get_simple_fail(link, exc) + except SSLError as exc: + reason = "There was a problem confirming the ssl certificate: " + reason += str(exc) + _handle_get_simple_fail(link, reason, meth=logger.info) + except requests.ConnectionError as exc: + _handle_get_simple_fail(link, f"connection error: {exc}") + except requests.Timeout: + _handle_get_simple_fail(link, "timed out") + else: + return _make_index_content(resp, cache_link_parsing=link.cache_link_parsing) + return None + + +class CollectedSources(NamedTuple): + find_links: Sequence[Optional[LinkSource]] + index_urls: Sequence[Optional[LinkSource]] + + +class LinkCollector: + + """ + Responsible for collecting Link objects from all configured locations, + making network requests as needed. + + The class's main method is its collect_sources() method. + """ + + def __init__( + self, + session: PipSession, + search_scope: SearchScope, + ) -> None: + self.search_scope = search_scope + self.session = session + + @classmethod + def create( + cls, + session: PipSession, + options: Values, + suppress_no_index: bool = False, + ) -> "LinkCollector": + """ + :param session: The Session to use to make requests. + :param suppress_no_index: Whether to ignore the --no-index option + when constructing the SearchScope object. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index and not suppress_no_index: + logger.debug( + "Ignoring indexes: %s", + ",".join(redact_auth_from_url(url) for url in index_urls), + ) + index_urls = [] + + # Make sure find_links is a list before passing to create(). + find_links = options.find_links or [] + + search_scope = SearchScope.create( + find_links=find_links, + index_urls=index_urls, + no_index=options.no_index, + ) + link_collector = LinkCollector( + session=session, + search_scope=search_scope, + ) + return link_collector + + @property + def find_links(self) -> List[str]: + return self.search_scope.find_links + + def fetch_response(self, location: Link) -> Optional[IndexContent]: + """ + Fetch an HTML page containing package links. + """ + return _get_index_content(location, session=self.session) + + def collect_sources( + self, + project_name: str, + candidates_from_page: CandidatesFromPage, + ) -> CollectedSources: + # The OrderedDict calls deduplicate sources by URL. + index_url_sources = collections.OrderedDict( + build_source( + loc, + candidates_from_page=candidates_from_page, + page_validator=self.session.is_secure_origin, + expand_dir=False, + cache_link_parsing=False, + project_name=project_name, + ) + for loc in self.search_scope.get_index_urls_locations(project_name) + ).values() + find_links_sources = collections.OrderedDict( + build_source( + loc, + candidates_from_page=candidates_from_page, + page_validator=self.session.is_secure_origin, + expand_dir=True, + cache_link_parsing=True, + project_name=project_name, + ) + for loc in self.find_links + ).values() + + if logger.isEnabledFor(logging.DEBUG): + lines = [ + f"* {s.link}" + for s in itertools.chain(find_links_sources, index_url_sources) + if s is not None and s.link is not None + ] + lines = [ + f"{len(lines)} location(s) to search " + f"for versions of {project_name}:" + ] + lines + logger.debug("\n".join(lines)) + + return CollectedSources( + find_links=list(find_links_sources), + index_urls=list(index_url_sources), + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py b/.venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py new file mode 100644 index 0000000..ec9ebc3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/index/package_finder.py @@ -0,0 +1,1027 @@ +"""Routines related to PyPI, indexes""" + +import enum +import functools +import itertools +import logging +import re +from typing import TYPE_CHECKING, FrozenSet, Iterable, List, Optional, Set, Tuple, Union + +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.tags import Tag +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import _BaseVersion +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.index.collector import LinkCollector, parse_links +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.models.wheel import Wheel +from pip._internal.req import InstallRequirement +from pip._internal.utils._log import getLogger +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import build_netloc +from pip._internal.utils.packaging import check_requires_python +from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS + +if TYPE_CHECKING: + from pip._vendor.typing_extensions import TypeGuard + +__all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"] + + +logger = getLogger(__name__) + +BuildTag = Union[Tuple[()], Tuple[int, str]] +CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag] + + +def _check_link_requires_python( + link: Link, + version_info: Tuple[int, int, int], + ignore_requires_python: bool = False, +) -> bool: + """ + Return whether the given Python version is compatible with a link's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + """ + try: + is_compatible = check_requires_python( + link.requires_python, + version_info=version_info, + ) + except specifiers.InvalidSpecifier: + logger.debug( + "Ignoring invalid Requires-Python (%r) for link: %s", + link.requires_python, + link, + ) + else: + if not is_compatible: + version = ".".join(map(str, version_info)) + if not ignore_requires_python: + logger.verbose( + "Link requires a different Python (%s not in: %r): %s", + version, + link.requires_python, + link, + ) + return False + + logger.debug( + "Ignoring failed Requires-Python check (%s not in: %r) for link: %s", + version, + link.requires_python, + link, + ) + + return True + + +class LinkType(enum.Enum): + candidate = enum.auto() + different_project = enum.auto() + yanked = enum.auto() + format_unsupported = enum.auto() + format_invalid = enum.auto() + platform_mismatch = enum.auto() + requires_python_mismatch = enum.auto() + + +class LinkEvaluator: + + """ + Responsible for evaluating links for a particular project. + """ + + _py_version_re = re.compile(r"-py([123]\.?[0-9]?)$") + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + project_name: str, + canonical_name: str, + formats: FrozenSet[str], + target_python: TargetPython, + allow_yanked: bool, + ignore_requires_python: Optional[bool] = None, + ) -> None: + """ + :param project_name: The user supplied package name. + :param canonical_name: The canonical package name. + :param formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. + :param target_python: The target Python interpreter to use when + evaluating link compatibility. This is used, for example, to + check wheel compatibility, as well as when checking the Python + version, e.g. the Python version embedded in a link filename + (or egg fragment) and against an HTML link's optional PEP 503 + "data-requires-python" attribute. + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param ignore_requires_python: Whether to ignore incompatible + PEP 503 "data-requires-python" values in HTML links. Defaults + to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self._allow_yanked = allow_yanked + self._canonical_name = canonical_name + self._ignore_requires_python = ignore_requires_python + self._formats = formats + self._target_python = target_python + + self.project_name = project_name + + def evaluate_link(self, link: Link) -> Tuple[LinkType, str]: + """ + Determine whether a link is a candidate for installation. + + :return: A tuple (result, detail), where *result* is an enum + representing whether the evaluation found a candidate, or the reason + why one is not found. If a candidate is found, *detail* will be the + candidate's version string; if one is not found, it contains the + reason the link fails to qualify. + """ + version = None + if link.is_yanked and not self._allow_yanked: + reason = link.yanked_reason or "" + return (LinkType.yanked, f"yanked for reason: {reason}") + + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + return (LinkType.format_unsupported, "not a file") + if ext not in SUPPORTED_EXTENSIONS: + return ( + LinkType.format_unsupported, + f"unsupported archive format: {ext}", + ) + if "binary" not in self._formats and ext == WHEEL_EXTENSION: + reason = f"No binaries permitted for {self.project_name}" + return (LinkType.format_unsupported, reason) + if "macosx10" in link.path and ext == ".zip": + return (LinkType.format_unsupported, "macosx10 one") + if ext == WHEEL_EXTENSION: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + return ( + LinkType.format_invalid, + "invalid wheel filename", + ) + if canonicalize_name(wheel.name) != self._canonical_name: + reason = f"wrong project name (not {self.project_name})" + return (LinkType.different_project, reason) + + supported_tags = self._target_python.get_unsorted_tags() + if not wheel.supported(supported_tags): + # Include the wheel's tags in the reason string to + # simplify troubleshooting compatibility issues. + file_tags = ", ".join(wheel.get_formatted_file_tags()) + reason = ( + f"none of the wheel's tags ({file_tags}) are compatible " + f"(run pip debug --verbose to show compatible tags)" + ) + return (LinkType.platform_mismatch, reason) + + version = wheel.version + + # This should be up by the self.ok_binary check, but see issue 2700. + if "source" not in self._formats and ext != WHEEL_EXTENSION: + reason = f"No sources permitted for {self.project_name}" + return (LinkType.format_unsupported, reason) + + if not version: + version = _extract_version_from_fragment( + egg_info, + self._canonical_name, + ) + if not version: + reason = f"Missing project version for {self.project_name}" + return (LinkType.format_invalid, reason) + + match = self._py_version_re.search(version) + if match: + version = version[: match.start()] + py_version = match.group(1) + if py_version != self._target_python.py_version: + return ( + LinkType.platform_mismatch, + "Python version is incorrect", + ) + + supports_python = _check_link_requires_python( + link, + version_info=self._target_python.py_version_info, + ignore_requires_python=self._ignore_requires_python, + ) + if not supports_python: + reason = f"{version} Requires-Python {link.requires_python}" + return (LinkType.requires_python_mismatch, reason) + + logger.debug("Found link %s, version: %s", link, version) + + return (LinkType.candidate, version) + + +def filter_unallowed_hashes( + candidates: List[InstallationCandidate], + hashes: Optional[Hashes], + project_name: str, +) -> List[InstallationCandidate]: + """ + Filter out candidates whose hashes aren't allowed, and return a new + list of candidates. + + If at least one candidate has an allowed hash, then all candidates with + either an allowed hash or no hash specified are returned. Otherwise, + the given candidates are returned. + + Including the candidates with no hash specified when there is a match + allows a warning to be logged if there is a more preferred candidate + with no hash specified. Returning all candidates in the case of no + matches lets pip report the hash of the candidate that would otherwise + have been installed (e.g. permitting the user to more easily update + their requirements file with the desired hash). + """ + if not hashes: + logger.debug( + "Given no hashes to check %s links for project %r: " + "discarding no candidates", + len(candidates), + project_name, + ) + # Make sure we're not returning back the given value. + return list(candidates) + + matches_or_no_digest = [] + # Collect the non-matches for logging purposes. + non_matches = [] + match_count = 0 + for candidate in candidates: + link = candidate.link + if not link.has_hash: + pass + elif link.is_hash_allowed(hashes=hashes): + match_count += 1 + else: + non_matches.append(candidate) + continue + + matches_or_no_digest.append(candidate) + + if match_count: + filtered = matches_or_no_digest + else: + # Make sure we're not returning back the given value. + filtered = list(candidates) + + if len(filtered) == len(candidates): + discard_message = "discarding no candidates" + else: + discard_message = "discarding {} non-matches:\n {}".format( + len(non_matches), + "\n ".join(str(candidate.link) for candidate in non_matches), + ) + + logger.debug( + "Checked %s links for project %r against %s hashes " + "(%s matches, %s no digest): %s", + len(candidates), + project_name, + hashes.digest_count, + match_count, + len(matches_or_no_digest) - match_count, + discard_message, + ) + + return filtered + + +class CandidatePreferences: + + """ + Encapsulates some of the preferences for filtering and sorting + InstallationCandidate objects. + """ + + def __init__( + self, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + ) -> None: + """ + :param allow_all_prereleases: Whether to allow all pre-releases. + """ + self.allow_all_prereleases = allow_all_prereleases + self.prefer_binary = prefer_binary + + +class BestCandidateResult: + """A collection of candidates, returned by `PackageFinder.find_best_candidate`. + + This class is only intended to be instantiated by CandidateEvaluator's + `compute_best_candidate()` method. + """ + + def __init__( + self, + candidates: List[InstallationCandidate], + applicable_candidates: List[InstallationCandidate], + best_candidate: Optional[InstallationCandidate], + ) -> None: + """ + :param candidates: A sequence of all available candidates found. + :param applicable_candidates: The applicable candidates. + :param best_candidate: The most preferred candidate found, or None + if no applicable candidates were found. + """ + assert set(applicable_candidates) <= set(candidates) + + if best_candidate is None: + assert not applicable_candidates + else: + assert best_candidate in applicable_candidates + + self._applicable_candidates = applicable_candidates + self._candidates = candidates + + self.best_candidate = best_candidate + + def iter_all(self) -> Iterable[InstallationCandidate]: + """Iterate through all candidates.""" + return iter(self._candidates) + + def iter_applicable(self) -> Iterable[InstallationCandidate]: + """Iterate through the applicable candidates.""" + return iter(self._applicable_candidates) + + +class CandidateEvaluator: + + """ + Responsible for filtering and sorting candidates for installation based + on what tags are valid. + """ + + @classmethod + def create( + cls, + project_name: str, + target_python: Optional[TargetPython] = None, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> "CandidateEvaluator": + """Create a CandidateEvaluator object. + + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + :param hashes: An optional collection of allowed hashes. + """ + if target_python is None: + target_python = TargetPython() + if specifier is None: + specifier = specifiers.SpecifierSet() + + supported_tags = target_python.get_sorted_tags() + + return cls( + project_name=project_name, + supported_tags=supported_tags, + specifier=specifier, + prefer_binary=prefer_binary, + allow_all_prereleases=allow_all_prereleases, + hashes=hashes, + ) + + def __init__( + self, + project_name: str, + supported_tags: List[Tag], + specifier: specifiers.BaseSpecifier, + prefer_binary: bool = False, + allow_all_prereleases: bool = False, + hashes: Optional[Hashes] = None, + ) -> None: + """ + :param supported_tags: The PEP 425 tags supported by the target + Python in order of preference (most preferred first). + """ + self._allow_all_prereleases = allow_all_prereleases + self._hashes = hashes + self._prefer_binary = prefer_binary + self._project_name = project_name + self._specifier = specifier + self._supported_tags = supported_tags + # Since the index of the tag in the _supported_tags list is used + # as a priority, precompute a map from tag to index/priority to be + # used in wheel.find_most_preferred_tag. + self._wheel_tag_preferences = { + tag: idx for idx, tag in enumerate(supported_tags) + } + + def get_applicable_candidates( + self, + candidates: List[InstallationCandidate], + ) -> List[InstallationCandidate]: + """ + Return the applicable candidates from a list of candidates. + """ + # Using None infers from the specifier instead. + allow_prereleases = self._allow_all_prereleases or None + specifier = self._specifier + versions = { + str(v) + for v in specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + (str(c.version) for c in candidates), + prereleases=allow_prereleases, + ) + } + + # Again, converting version to str to deal with debundling. + applicable_candidates = [c for c in candidates if str(c.version) in versions] + + filtered_applicable_candidates = filter_unallowed_hashes( + candidates=applicable_candidates, + hashes=self._hashes, + project_name=self._project_name, + ) + + return sorted(filtered_applicable_candidates, key=self._sort_key) + + def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey: + """ + Function to pass as the `key` argument to a call to sorted() to sort + InstallationCandidates by preference. + + Returns a tuple such that tuples sorting as greater using Python's + default comparison operator are more preferred. + + The preference is as follows: + + First and foremost, candidates with allowed (matching) hashes are + always preferred over candidates without matching hashes. This is + because e.g. if the only candidate with an allowed hash is yanked, + we still want to use that candidate. + + Second, excepting hash considerations, candidates that have been + yanked (in the sense of PEP 592) are always less preferred than + candidates that haven't been yanked. Then: + + If not finding wheels, they are sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self._supported_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + valid_tags = self._supported_tags + support_num = len(valid_tags) + build_tag: BuildTag = () + binary_preference = 0 + link = candidate.link + if link.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(link.filename) + try: + pri = -( + wheel.find_most_preferred_tag( + valid_tags, self._wheel_tag_preferences + ) + ) + except ValueError: + raise UnsupportedWheel( + f"{wheel.filename} is not a supported wheel for this platform. It " + "can't be sorted." + ) + if self._prefer_binary: + binary_preference = 1 + if wheel.build_tag is not None: + match = re.match(r"^(\d+)(.*)$", wheel.build_tag) + assert match is not None, "guaranteed by filename validation" + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + has_allowed_hash = int(link.is_hash_allowed(self._hashes)) + yank_value = -1 * int(link.is_yanked) # -1 for yanked. + return ( + has_allowed_hash, + yank_value, + binary_preference, + candidate.version, + pri, + build_tag, + ) + + def sort_best_candidate( + self, + candidates: List[InstallationCandidate], + ) -> Optional[InstallationCandidate]: + """ + Return the best candidate per the instance's sort order, or None if + no candidate is acceptable. + """ + if not candidates: + return None + best_candidate = max(candidates, key=self._sort_key) + return best_candidate + + def compute_best_candidate( + self, + candidates: List[InstallationCandidate], + ) -> BestCandidateResult: + """ + Compute and return a `BestCandidateResult` instance. + """ + applicable_candidates = self.get_applicable_candidates(candidates) + + best_candidate = self.sort_best_candidate(applicable_candidates) + + return BestCandidateResult( + candidates, + applicable_candidates=applicable_candidates, + best_candidate=best_candidate, + ) + + +class PackageFinder: + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__( + self, + link_collector: LinkCollector, + target_python: TargetPython, + allow_yanked: bool, + format_control: Optional[FormatControl] = None, + candidate_prefs: Optional[CandidatePreferences] = None, + ignore_requires_python: Optional[bool] = None, + ) -> None: + """ + This constructor is primarily meant to be used by the create() class + method and from tests. + + :param format_control: A FormatControl object, used to control + the selection of source packages / binary packages when consulting + the index and links. + :param candidate_prefs: Options to use when creating a + CandidateEvaluator object. + """ + if candidate_prefs is None: + candidate_prefs = CandidatePreferences() + + format_control = format_control or FormatControl(set(), set()) + + self._allow_yanked = allow_yanked + self._candidate_prefs = candidate_prefs + self._ignore_requires_python = ignore_requires_python + self._link_collector = link_collector + self._target_python = target_python + + self.format_control = format_control + + # These are boring links that have already been logged somehow. + self._logged_links: Set[Tuple[Link, LinkType, str]] = set() + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + @classmethod + def create( + cls, + link_collector: LinkCollector, + selection_prefs: SelectionPreferences, + target_python: Optional[TargetPython] = None, + ) -> "PackageFinder": + """Create a PackageFinder. + + :param selection_prefs: The candidate selection preferences, as a + SelectionPreferences object. + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + """ + if target_python is None: + target_python = TargetPython() + + candidate_prefs = CandidatePreferences( + prefer_binary=selection_prefs.prefer_binary, + allow_all_prereleases=selection_prefs.allow_all_prereleases, + ) + + return cls( + candidate_prefs=candidate_prefs, + link_collector=link_collector, + target_python=target_python, + allow_yanked=selection_prefs.allow_yanked, + format_control=selection_prefs.format_control, + ignore_requires_python=selection_prefs.ignore_requires_python, + ) + + @property + def target_python(self) -> TargetPython: + return self._target_python + + @property + def search_scope(self) -> SearchScope: + return self._link_collector.search_scope + + @search_scope.setter + def search_scope(self, search_scope: SearchScope) -> None: + self._link_collector.search_scope = search_scope + + @property + def find_links(self) -> List[str]: + return self._link_collector.find_links + + @property + def index_urls(self) -> List[str]: + return self.search_scope.index_urls + + @property + def trusted_hosts(self) -> Iterable[str]: + for host_port in self._link_collector.session.pip_trusted_origins: + yield build_netloc(*host_port) + + @property + def allow_all_prereleases(self) -> bool: + return self._candidate_prefs.allow_all_prereleases + + def set_allow_all_prereleases(self) -> None: + self._candidate_prefs.allow_all_prereleases = True + + @property + def prefer_binary(self) -> bool: + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self) -> None: + self._candidate_prefs.prefer_binary = True + + def requires_python_skipped_reasons(self) -> List[str]: + reasons = { + detail + for _, result, detail in self._logged_links + if result == LinkType.requires_python_mismatch + } + return sorted(reasons) + + def make_link_evaluator(self, project_name: str) -> LinkEvaluator: + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + + return LinkEvaluator( + project_name=project_name, + canonical_name=canonical_name, + formats=formats, + target_python=self._target_python, + allow_yanked=self._allow_yanked, + ignore_requires_python=self._ignore_requires_python, + ) + + def _sort_links(self, links: Iterable[Link]) -> List[Link]: + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen: Set[Link] = set() + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _log_skipped_link(self, link: Link, result: LinkType, detail: str) -> None: + entry = (link, result, detail) + if entry not in self._logged_links: + # Put the link at the end so the reason is more visible and because + # the link string is usually very long. + logger.debug("Skipping link: %s: %s", detail, link) + self._logged_links.add(entry) + + def get_install_candidate( + self, link_evaluator: LinkEvaluator, link: Link + ) -> Optional[InstallationCandidate]: + """ + If the link is a candidate for install, convert it to an + InstallationCandidate and return it. Otherwise, return None. + """ + result, detail = link_evaluator.evaluate_link(link) + if result != LinkType.candidate: + self._log_skipped_link(link, result, detail) + return None + + return InstallationCandidate( + name=link_evaluator.project_name, + link=link, + version=detail, + ) + + def evaluate_links( + self, link_evaluator: LinkEvaluator, links: Iterable[Link] + ) -> List[InstallationCandidate]: + """ + Convert links that are candidates to InstallationCandidate objects. + """ + candidates = [] + for link in self._sort_links(links): + candidate = self.get_install_candidate(link_evaluator, link) + if candidate is not None: + candidates.append(candidate) + + return candidates + + def process_project_url( + self, project_url: Link, link_evaluator: LinkEvaluator + ) -> List[InstallationCandidate]: + logger.debug( + "Fetching project page and analyzing links: %s", + project_url, + ) + index_response = self._link_collector.fetch_response(project_url) + if index_response is None: + return [] + + page_links = list(parse_links(index_response)) + + with indent_log(): + package_links = self.evaluate_links( + link_evaluator, + links=page_links, + ) + + return package_links + + @functools.lru_cache(maxsize=None) + def find_all_candidates(self, project_name: str) -> List[InstallationCandidate]: + """Find all available InstallationCandidate for project_name + + This checks index_urls and find_links. + All versions found are returned as an InstallationCandidate list. + + See LinkEvaluator.evaluate_link() for details on which files + are accepted. + """ + link_evaluator = self.make_link_evaluator(project_name) + + collected_sources = self._link_collector.collect_sources( + project_name=project_name, + candidates_from_page=functools.partial( + self.process_project_url, + link_evaluator=link_evaluator, + ), + ) + + page_candidates_it = itertools.chain.from_iterable( + source.page_candidates() + for sources in collected_sources + for source in sources + if source is not None + ) + page_candidates = list(page_candidates_it) + + file_links_it = itertools.chain.from_iterable( + source.file_links() + for sources in collected_sources + for source in sources + if source is not None + ) + file_candidates = self.evaluate_links( + link_evaluator, + sorted(file_links_it, reverse=True), + ) + + if logger.isEnabledFor(logging.DEBUG) and file_candidates: + paths = [] + for candidate in file_candidates: + assert candidate.link.url # we need to have a URL + try: + paths.append(candidate.link.file_path) + except Exception: + paths.append(candidate.link.url) # it's not a local file + + logger.debug("Local files found: %s", ", ".join(paths)) + + # This is an intentional priority ordering + return file_candidates + page_candidates + + def make_candidate_evaluator( + self, + project_name: str, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> CandidateEvaluator: + """Create a CandidateEvaluator object to use.""" + candidate_prefs = self._candidate_prefs + return CandidateEvaluator.create( + project_name=project_name, + target_python=self._target_python, + prefer_binary=candidate_prefs.prefer_binary, + allow_all_prereleases=candidate_prefs.allow_all_prereleases, + specifier=specifier, + hashes=hashes, + ) + + @functools.lru_cache(maxsize=None) + def find_best_candidate( + self, + project_name: str, + specifier: Optional[specifiers.BaseSpecifier] = None, + hashes: Optional[Hashes] = None, + ) -> BestCandidateResult: + """Find matches for the given project and specifier. + + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + + :return: A `BestCandidateResult` instance. + """ + candidates = self.find_all_candidates(project_name) + candidate_evaluator = self.make_candidate_evaluator( + project_name=project_name, + specifier=specifier, + hashes=hashes, + ) + return candidate_evaluator.compute_best_candidate(candidates) + + def find_requirement( + self, req: InstallRequirement, upgrade: bool + ) -> Optional[InstallationCandidate]: + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a InstallationCandidate if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + hashes = req.hashes(trust_internet=False) + best_candidate_result = self.find_best_candidate( + req.name, + specifier=req.specifier, + hashes=hashes, + ) + best_candidate = best_candidate_result.best_candidate + + installed_version: Optional[_BaseVersion] = None + if req.satisfied_by is not None: + installed_version = req.satisfied_by.version + + def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str: + # This repeated parse_version and str() conversion is needed to + # handle different vendoring sources from pip and pkg_resources. + # If we stop using the pkg_resources provided specifier and start + # using our own, we can drop the cast to str(). + return ( + ", ".join( + sorted( + {str(c.version) for c in cand_iter}, + key=parse_version, + ) + ) + or "none" + ) + + if installed_version is None and best_candidate is None: + logger.critical( + "Could not find a version that satisfies the requirement %s " + "(from versions: %s)", + req, + _format_versions(best_candidate_result.iter_all()), + ) + + raise DistributionNotFound(f"No matching distribution found for {req}") + + def _should_install_candidate( + candidate: Optional[InstallationCandidate], + ) -> "TypeGuard[InstallationCandidate]": + if installed_version is None: + return True + if best_candidate is None: + return False + return best_candidate.version > installed_version + + if not upgrade and installed_version is not None: + if _should_install_candidate(best_candidate): + logger.debug( + "Existing installed version (%s) satisfies requirement " + "(most up-to-date version is %s)", + installed_version, + best_candidate.version, + ) + else: + logger.debug( + "Existing installed version (%s) is most up-to-date and " + "satisfies requirement", + installed_version, + ) + return None + + if _should_install_candidate(best_candidate): + logger.debug( + "Using version %s (newest of versions: %s)", + best_candidate.version, + _format_versions(best_candidate_result.iter_applicable()), + ) + return best_candidate + + # We have an existing version, and its the best version + logger.debug( + "Installed version (%s) is most up-to-date (past versions: %s)", + installed_version, + _format_versions(best_candidate_result.iter_applicable()), + ) + raise BestVersionAlreadyInstalled + + +def _find_name_version_sep(fragment: str, canonical_name: str) -> int: + """Find the separator's index based on the package's canonical name. + + :param fragment: A + filename "fragment" (stem) or + egg fragment. + :param canonical_name: The package's canonical name. + + This function is needed since the canonicalized name does not necessarily + have the same length as the egg info's name part. An example:: + + >>> fragment = 'foo__bar-1.0' + >>> canonical_name = 'foo-bar' + >>> _find_name_version_sep(fragment, canonical_name) + 8 + """ + # Project name and version must be separated by one single dash. Find all + # occurrences of dashes; if the string in front of it matches the canonical + # name, this is the one separating the name and version parts. + for i, c in enumerate(fragment): + if c != "-": + continue + if canonicalize_name(fragment[:i]) == canonical_name: + return i + raise ValueError(f"{fragment} does not match {canonical_name}") + + +def _extract_version_from_fragment(fragment: str, canonical_name: str) -> Optional[str]: + """Parse the version string from a + filename + "fragment" (stem) or egg fragment. + + :param fragment: The string to parse. E.g. foo-2.1 + :param canonical_name: The canonicalized name of the package this + belongs to. + """ + try: + version_start = _find_name_version_sep(fragment, canonical_name) + 1 + except ValueError: + return None + version = fragment[version_start:] + if not version: + return None + return version diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/index/sources.py b/.venv/lib/python3.12/site-packages/pip/_internal/index/sources.py new file mode 100644 index 0000000..f4626d7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/index/sources.py @@ -0,0 +1,285 @@ +import logging +import mimetypes +import os +from collections import defaultdict +from typing import Callable, Dict, Iterable, List, Optional, Tuple + +from pip._vendor.packaging.utils import ( + InvalidSdistFilename, + InvalidVersion, + InvalidWheelFilename, + canonicalize_name, + parse_sdist_filename, + parse_wheel_filename, +) + +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.link import Link +from pip._internal.utils.urls import path_to_url, url_to_path +from pip._internal.vcs import is_url + +logger = logging.getLogger(__name__) + +FoundCandidates = Iterable[InstallationCandidate] +FoundLinks = Iterable[Link] +CandidatesFromPage = Callable[[Link], Iterable[InstallationCandidate]] +PageValidator = Callable[[Link], bool] + + +class LinkSource: + @property + def link(self) -> Optional[Link]: + """Returns the underlying link, if there's one.""" + raise NotImplementedError() + + def page_candidates(self) -> FoundCandidates: + """Candidates found by parsing an archive listing HTML file.""" + raise NotImplementedError() + + def file_links(self) -> FoundLinks: + """Links found by specifying archives directly.""" + raise NotImplementedError() + + +def _is_html_file(file_url: str) -> bool: + return mimetypes.guess_type(file_url, strict=False)[0] == "text/html" + + +class _FlatDirectoryToUrls: + """Scans directory and caches results""" + + def __init__(self, path: str) -> None: + self._path = path + self._page_candidates: List[str] = [] + self._project_name_to_urls: Dict[str, List[str]] = defaultdict(list) + self._scanned_directory = False + + def _scan_directory(self) -> None: + """Scans directory once and populates both page_candidates + and project_name_to_urls at the same time + """ + for entry in os.scandir(self._path): + url = path_to_url(entry.path) + if _is_html_file(url): + self._page_candidates.append(url) + continue + + # File must have a valid wheel or sdist name, + # otherwise not worth considering as a package + try: + project_filename = parse_wheel_filename(entry.name)[0] + except (InvalidWheelFilename, InvalidVersion): + try: + project_filename = parse_sdist_filename(entry.name)[0] + except (InvalidSdistFilename, InvalidVersion): + continue + + self._project_name_to_urls[project_filename].append(url) + self._scanned_directory = True + + @property + def page_candidates(self) -> List[str]: + if not self._scanned_directory: + self._scan_directory() + + return self._page_candidates + + @property + def project_name_to_urls(self) -> Dict[str, List[str]]: + if not self._scanned_directory: + self._scan_directory() + + return self._project_name_to_urls + + +class _FlatDirectorySource(LinkSource): + """Link source specified by ``--find-links=``. + + This looks the content of the directory, and returns: + + * ``page_candidates``: Links listed on each HTML file in the directory. + * ``file_candidates``: Archives in the directory. + """ + + _paths_to_urls: Dict[str, _FlatDirectoryToUrls] = {} + + def __init__( + self, + candidates_from_page: CandidatesFromPage, + path: str, + project_name: str, + ) -> None: + self._candidates_from_page = candidates_from_page + self._project_name = canonicalize_name(project_name) + + # Get existing instance of _FlatDirectoryToUrls if it exists + if path in self._paths_to_urls: + self._path_to_urls = self._paths_to_urls[path] + else: + self._path_to_urls = _FlatDirectoryToUrls(path=path) + self._paths_to_urls[path] = self._path_to_urls + + @property + def link(self) -> Optional[Link]: + return None + + def page_candidates(self) -> FoundCandidates: + for url in self._path_to_urls.page_candidates: + yield from self._candidates_from_page(Link(url)) + + def file_links(self) -> FoundLinks: + for url in self._path_to_urls.project_name_to_urls[self._project_name]: + yield Link(url) + + +class _LocalFileSource(LinkSource): + """``--find-links=`` or ``--[extra-]index-url=``. + + If a URL is supplied, it must be a ``file:`` URL. If a path is supplied to + the option, it is converted to a URL first. This returns: + + * ``page_candidates``: Links listed on an HTML file. + * ``file_candidates``: The non-HTML file. + """ + + def __init__( + self, + candidates_from_page: CandidatesFromPage, + link: Link, + ) -> None: + self._candidates_from_page = candidates_from_page + self._link = link + + @property + def link(self) -> Optional[Link]: + return self._link + + def page_candidates(self) -> FoundCandidates: + if not _is_html_file(self._link.url): + return + yield from self._candidates_from_page(self._link) + + def file_links(self) -> FoundLinks: + if _is_html_file(self._link.url): + return + yield self._link + + +class _RemoteFileSource(LinkSource): + """``--find-links=`` or ``--[extra-]index-url=``. + + This returns: + + * ``page_candidates``: Links listed on an HTML file. + * ``file_candidates``: The non-HTML file. + """ + + def __init__( + self, + candidates_from_page: CandidatesFromPage, + page_validator: PageValidator, + link: Link, + ) -> None: + self._candidates_from_page = candidates_from_page + self._page_validator = page_validator + self._link = link + + @property + def link(self) -> Optional[Link]: + return self._link + + def page_candidates(self) -> FoundCandidates: + if not self._page_validator(self._link): + return + yield from self._candidates_from_page(self._link) + + def file_links(self) -> FoundLinks: + yield self._link + + +class _IndexDirectorySource(LinkSource): + """``--[extra-]index-url=``. + + This is treated like a remote URL; ``candidates_from_page`` contains logic + for this by appending ``index.html`` to the link. + """ + + def __init__( + self, + candidates_from_page: CandidatesFromPage, + link: Link, + ) -> None: + self._candidates_from_page = candidates_from_page + self._link = link + + @property + def link(self) -> Optional[Link]: + return self._link + + def page_candidates(self) -> FoundCandidates: + yield from self._candidates_from_page(self._link) + + def file_links(self) -> FoundLinks: + return () + + +def build_source( + location: str, + *, + candidates_from_page: CandidatesFromPage, + page_validator: PageValidator, + expand_dir: bool, + cache_link_parsing: bool, + project_name: str, +) -> Tuple[Optional[str], Optional[LinkSource]]: + path: Optional[str] = None + url: Optional[str] = None + if os.path.exists(location): # Is a local path. + url = path_to_url(location) + path = location + elif location.startswith("file:"): # A file: URL. + url = location + path = url_to_path(location) + elif is_url(location): + url = location + + if url is None: + msg = ( + "Location '%s' is ignored: " + "it is either a non-existing path or lacks a specific scheme." + ) + logger.warning(msg, location) + return (None, None) + + if path is None: + source: LinkSource = _RemoteFileSource( + candidates_from_page=candidates_from_page, + page_validator=page_validator, + link=Link(url, cache_link_parsing=cache_link_parsing), + ) + return (url, source) + + if os.path.isdir(path): + if expand_dir: + source = _FlatDirectorySource( + candidates_from_page=candidates_from_page, + path=path, + project_name=project_name, + ) + else: + source = _IndexDirectorySource( + candidates_from_page=candidates_from_page, + link=Link(url, cache_link_parsing=cache_link_parsing), + ) + return (url, source) + elif os.path.isfile(path): + source = _LocalFileSource( + candidates_from_page=candidates_from_page, + link=Link(url, cache_link_parsing=cache_link_parsing), + ) + return (url, source) + logger.warning( + "Location '%s' is ignored: it is neither a file nor a directory.", + location, + ) + return (url, None) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py new file mode 100644 index 0000000..d54bc63 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/locations/__init__.py @@ -0,0 +1,467 @@ +import functools +import logging +import os +import pathlib +import sys +import sysconfig +from typing import Any, Dict, Generator, Optional, Tuple + +from pip._internal.models.scheme import SCHEME_KEYS, Scheme +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.virtualenv import running_under_virtualenv + +from . import _sysconfig +from .base import ( + USER_CACHE_DIR, + get_major_minor_version, + get_src_prefix, + is_osx_framework, + site_packages, + user_site, +) + +__all__ = [ + "USER_CACHE_DIR", + "get_bin_prefix", + "get_bin_user", + "get_major_minor_version", + "get_platlib", + "get_purelib", + "get_scheme", + "get_src_prefix", + "site_packages", + "user_site", +] + + +logger = logging.getLogger(__name__) + + +_PLATLIBDIR: str = getattr(sys, "platlibdir", "lib") + +_USE_SYSCONFIG_DEFAULT = sys.version_info >= (3, 10) + + +def _should_use_sysconfig() -> bool: + """This function determines the value of _USE_SYSCONFIG. + + By default, pip uses sysconfig on Python 3.10+. + But Python distributors can override this decision by setting: + sysconfig._PIP_USE_SYSCONFIG = True / False + Rationale in https://github.com/pypa/pip/issues/10647 + + This is a function for testability, but should be constant during any one + run. + """ + return bool(getattr(sysconfig, "_PIP_USE_SYSCONFIG", _USE_SYSCONFIG_DEFAULT)) + + +_USE_SYSCONFIG = _should_use_sysconfig() + +if not _USE_SYSCONFIG: + # Import distutils lazily to avoid deprecation warnings, + # but import it soon enough that it is in memory and available during + # a pip reinstall. + from . import _distutils + +# Be noisy about incompatibilities if this platforms "should" be using +# sysconfig, but is explicitly opting out and using distutils instead. +if _USE_SYSCONFIG_DEFAULT and not _USE_SYSCONFIG: + _MISMATCH_LEVEL = logging.WARNING +else: + _MISMATCH_LEVEL = logging.DEBUG + + +def _looks_like_bpo_44860() -> bool: + """The resolution to bpo-44860 will change this incorrect platlib. + + See . + """ + from distutils.command.install import INSTALL_SCHEMES + + try: + unix_user_platlib = INSTALL_SCHEMES["unix_user"]["platlib"] + except KeyError: + return False + return unix_user_platlib == "$usersite" + + +def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool: + platlib = scheme["platlib"] + if "/$platlibdir/" in platlib: + platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/") + if "/lib64/" not in platlib: + return False + unpatched = platlib.replace("/lib64/", "/lib/") + return unpatched.replace("$platbase/", "$base/") == scheme["purelib"] + + +@functools.lru_cache(maxsize=None) +def _looks_like_red_hat_lib() -> bool: + """Red Hat patches platlib in unix_prefix and unix_home, but not purelib. + + This is the only way I can see to tell a Red Hat-patched Python. + """ + from distutils.command.install import INSTALL_SCHEMES + + return all( + k in INSTALL_SCHEMES + and _looks_like_red_hat_patched_platlib_purelib(INSTALL_SCHEMES[k]) + for k in ("unix_prefix", "unix_home") + ) + + +@functools.lru_cache(maxsize=None) +def _looks_like_debian_scheme() -> bool: + """Debian adds two additional schemes.""" + from distutils.command.install import INSTALL_SCHEMES + + return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES + + +@functools.lru_cache(maxsize=None) +def _looks_like_red_hat_scheme() -> bool: + """Red Hat patches ``sys.prefix`` and ``sys.exec_prefix``. + + Red Hat's ``00251-change-user-install-location.patch`` changes the install + command's ``prefix`` and ``exec_prefix`` to append ``"/local"``. This is + (fortunately?) done quite unconditionally, so we create a default command + object without any configuration to detect this. + """ + from distutils.command.install import install + from distutils.dist import Distribution + + cmd: Any = install(Distribution()) + cmd.finalize_options() + return ( + cmd.exec_prefix == f"{os.path.normpath(sys.exec_prefix)}/local" + and cmd.prefix == f"{os.path.normpath(sys.prefix)}/local" + ) + + +@functools.lru_cache(maxsize=None) +def _looks_like_slackware_scheme() -> bool: + """Slackware patches sysconfig but fails to patch distutils and site. + + Slackware changes sysconfig's user scheme to use ``"lib64"`` for the lib + path, but does not do the same to the site module. + """ + if user_site is None: # User-site not available. + return False + try: + paths = sysconfig.get_paths(scheme="posix_user", expand=False) + except KeyError: # User-site not available. + return False + return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site + + +@functools.lru_cache(maxsize=None) +def _looks_like_msys2_mingw_scheme() -> bool: + """MSYS2 patches distutils and sysconfig to use a UNIX-like scheme. + + However, MSYS2 incorrectly patches sysconfig ``nt`` scheme. The fix is + likely going to be included in their 3.10 release, so we ignore the warning. + See msys2/MINGW-packages#9319. + + MSYS2 MINGW's patch uses lowercase ``"lib"`` instead of the usual uppercase, + and is missing the final ``"site-packages"``. + """ + paths = sysconfig.get_paths("nt", expand=False) + return all( + "Lib" not in p and "lib" in p and not p.endswith("site-packages") + for p in (paths[key] for key in ("platlib", "purelib")) + ) + + +def _fix_abiflags(parts: Tuple[str]) -> Generator[str, None, None]: + ldversion = sysconfig.get_config_var("LDVERSION") + abiflags = getattr(sys, "abiflags", None) + + # LDVERSION does not end with sys.abiflags. Just return the path unchanged. + if not ldversion or not abiflags or not ldversion.endswith(abiflags): + yield from parts + return + + # Strip sys.abiflags from LDVERSION-based path components. + for part in parts: + if part.endswith(ldversion): + part = part[: (0 - len(abiflags))] + yield part + + +@functools.lru_cache(maxsize=None) +def _warn_mismatched(old: pathlib.Path, new: pathlib.Path, *, key: str) -> None: + issue_url = "https://github.com/pypa/pip/issues/10151" + message = ( + "Value for %s does not match. Please report this to <%s>" + "\ndistutils: %s" + "\nsysconfig: %s" + ) + logger.log(_MISMATCH_LEVEL, message, key, issue_url, old, new) + + +def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool: + if old == new: + return False + _warn_mismatched(old, new, key=key) + return True + + +@functools.lru_cache(maxsize=None) +def _log_context( + *, + user: bool = False, + home: Optional[str] = None, + root: Optional[str] = None, + prefix: Optional[str] = None, +) -> None: + parts = [ + "Additional context:", + "user = %r", + "home = %r", + "root = %r", + "prefix = %r", + ] + + logger.log(_MISMATCH_LEVEL, "\n".join(parts), user, home, root, prefix) + + +def get_scheme( + dist_name: str, + user: bool = False, + home: Optional[str] = None, + root: Optional[str] = None, + isolated: bool = False, + prefix: Optional[str] = None, +) -> Scheme: + new = _sysconfig.get_scheme( + dist_name, + user=user, + home=home, + root=root, + isolated=isolated, + prefix=prefix, + ) + if _USE_SYSCONFIG: + return new + + old = _distutils.get_scheme( + dist_name, + user=user, + home=home, + root=root, + isolated=isolated, + prefix=prefix, + ) + + warning_contexts = [] + for k in SCHEME_KEYS: + old_v = pathlib.Path(getattr(old, k)) + new_v = pathlib.Path(getattr(new, k)) + + if old_v == new_v: + continue + + # distutils incorrectly put PyPy packages under ``site-packages/python`` + # in the ``posix_home`` scheme, but PyPy devs said they expect the + # directory name to be ``pypy`` instead. So we treat this as a bug fix + # and not warn about it. See bpo-43307 and python/cpython#24628. + skip_pypy_special_case = ( + sys.implementation.name == "pypy" + and home is not None + and k in ("platlib", "purelib") + and old_v.parent == new_v.parent + and old_v.name.startswith("python") + and new_v.name.startswith("pypy") + ) + if skip_pypy_special_case: + continue + + # sysconfig's ``osx_framework_user`` does not include ``pythonX.Y`` in + # the ``include`` value, but distutils's ``headers`` does. We'll let + # CPython decide whether this is a bug or feature. See bpo-43948. + skip_osx_framework_user_special_case = ( + user + and is_osx_framework() + and k == "headers" + and old_v.parent.parent == new_v.parent + and old_v.parent.name.startswith("python") + ) + if skip_osx_framework_user_special_case: + continue + + # On Red Hat and derived Linux distributions, distutils is patched to + # use "lib64" instead of "lib" for platlib. + if k == "platlib" and _looks_like_red_hat_lib(): + continue + + # On Python 3.9+, sysconfig's posix_user scheme sets platlib against + # sys.platlibdir, but distutils's unix_user incorrectly coninutes + # using the same $usersite for both platlib and purelib. This creates a + # mismatch when sys.platlibdir is not "lib". + skip_bpo_44860 = ( + user + and k == "platlib" + and not WINDOWS + and sys.version_info >= (3, 9) + and _PLATLIBDIR != "lib" + and _looks_like_bpo_44860() + ) + if skip_bpo_44860: + continue + + # Slackware incorrectly patches posix_user to use lib64 instead of lib, + # but not usersite to match the location. + skip_slackware_user_scheme = ( + user + and k in ("platlib", "purelib") + and not WINDOWS + and _looks_like_slackware_scheme() + ) + if skip_slackware_user_scheme: + continue + + # Both Debian and Red Hat patch Python to place the system site under + # /usr/local instead of /usr. Debian also places lib in dist-packages + # instead of site-packages, but the /usr/local check should cover it. + skip_linux_system_special_case = ( + not (user or home or prefix or running_under_virtualenv()) + and old_v.parts[1:3] == ("usr", "local") + and len(new_v.parts) > 1 + and new_v.parts[1] == "usr" + and (len(new_v.parts) < 3 or new_v.parts[2] != "local") + and (_looks_like_red_hat_scheme() or _looks_like_debian_scheme()) + ) + if skip_linux_system_special_case: + continue + + # On Python 3.7 and earlier, sysconfig does not include sys.abiflags in + # the "pythonX.Y" part of the path, but distutils does. + skip_sysconfig_abiflag_bug = ( + sys.version_info < (3, 8) + and not WINDOWS + and k in ("headers", "platlib", "purelib") + and tuple(_fix_abiflags(old_v.parts)) == new_v.parts + ) + if skip_sysconfig_abiflag_bug: + continue + + # MSYS2 MINGW's sysconfig patch does not include the "site-packages" + # part of the path. This is incorrect and will be fixed in MSYS. + skip_msys2_mingw_bug = ( + WINDOWS and k in ("platlib", "purelib") and _looks_like_msys2_mingw_scheme() + ) + if skip_msys2_mingw_bug: + continue + + # CPython's POSIX install script invokes pip (via ensurepip) against the + # interpreter located in the source tree, not the install site. This + # triggers special logic in sysconfig that's not present in distutils. + # https://github.com/python/cpython/blob/8c21941ddaf/Lib/sysconfig.py#L178-L194 + skip_cpython_build = ( + sysconfig.is_python_build(check_home=True) + and not WINDOWS + and k in ("headers", "include", "platinclude") + ) + if skip_cpython_build: + continue + + warning_contexts.append((old_v, new_v, f"scheme.{k}")) + + if not warning_contexts: + return old + + # Check if this path mismatch is caused by distutils config files. Those + # files will no longer work once we switch to sysconfig, so this raises a + # deprecation message for them. + default_old = _distutils.distutils_scheme( + dist_name, + user, + home, + root, + isolated, + prefix, + ignore_config_files=True, + ) + if any(default_old[k] != getattr(old, k) for k in SCHEME_KEYS): + deprecated( + reason=( + "Configuring installation scheme with distutils config files " + "is deprecated and will no longer work in the near future. If you " + "are using a Homebrew or Linuxbrew Python, please see discussion " + "at https://github.com/Homebrew/homebrew-core/issues/76621" + ), + replacement=None, + gone_in=None, + ) + return old + + # Post warnings about this mismatch so user can report them back. + for old_v, new_v, key in warning_contexts: + _warn_mismatched(old_v, new_v, key=key) + _log_context(user=user, home=home, root=root, prefix=prefix) + + return old + + +def get_bin_prefix() -> str: + new = _sysconfig.get_bin_prefix() + if _USE_SYSCONFIG: + return new + + old = _distutils.get_bin_prefix() + if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="bin_prefix"): + _log_context() + return old + + +def get_bin_user() -> str: + return _sysconfig.get_scheme("", user=True).scripts + + +def _looks_like_deb_system_dist_packages(value: str) -> bool: + """Check if the value is Debian's APT-controlled dist-packages. + + Debian's ``distutils.sysconfig.get_python_lib()`` implementation returns the + default package path controlled by APT, but does not patch ``sysconfig`` to + do the same. This is similar to the bug worked around in ``get_scheme()``, + but here the default is ``deb_system`` instead of ``unix_local``. Ultimately + we can't do anything about this Debian bug, and this detection allows us to + skip the warning when needed. + """ + if not _looks_like_debian_scheme(): + return False + if value == "/usr/lib/python3/dist-packages": + return True + return False + + +def get_purelib() -> str: + """Return the default pure-Python lib location.""" + new = _sysconfig.get_purelib() + if _USE_SYSCONFIG: + return new + + old = _distutils.get_purelib() + if _looks_like_deb_system_dist_packages(old): + return old + if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="purelib"): + _log_context() + return old + + +def get_platlib() -> str: + """Return the default platform-shared lib location.""" + new = _sysconfig.get_platlib() + if _USE_SYSCONFIG: + return new + + from . import _distutils + + old = _distutils.get_platlib() + if _looks_like_deb_system_dist_packages(old): + return old + if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"): + _log_context() + return old diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/locations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..203836ead0e1d9b15e8ec19980a717f09b2ccfa2 GIT binary patch literal 16797 zcmb_@Yfv0lmS$#E*1MpJrvM?863C(?pd=$pvRV%#fg~i1Wg+>2?JWv2fkO3QGOHv( z7h}4|(MBG{Hj3>Y;jlZ5dn48;tO=_ldSjT_`QiSv(YrgbQDyMJWXrLd9nbFezY@qj zJ&ygc-?^Dt1qj*g-btvNH*en8x%b?2zjN-%|5jAw=5Rguua=1ID98OHJ(!ozh^#6C z$KB>Ga}p=EbfYK ziBP`sv8}yZW3|1tv2DHEcy5T3+&|}g>nxnij~q8iD%@r9V_tvi-GMwi zWkK>t-jA(DTD{yL?~;lzB%CaabQQ{(F{${<>m1j+Tk^?`gBHpEme3nOJ%Nvn4842g zJ*pi&EwX2g$6Ei^8mN05SeK4HAuhxl_w zd_UrCV|<|Xw=~?{0h?xRi;rnSdt@M~xz5OOSqUc-ispP_G#N?6!%@w4DK#3E0~XEG zbLwoz^Btk{9k2Fi_MU-ZIVJ~q&GB+)cl!%3_aN$)$;nVGJd#jC zu}B=@RasHdC9RlVsLDVHZ4E}QYyOBDN~qUEgGx9iUrQ)gv?4W?Y!XK!qasE|74!2YqPh!X$zj}DTlVkU z+oG3ACCyYRq9&C{e+s;$iUZ-en85TY5lKeV=#wN5L})U_{xMONlS#125mp5?oo}Tj zbfNRY`re7h#7hdg9TZQ8qpHlxTny{1DvOc0IGjw5sz-vsp-6H#)!#CZhy_Q-M#DkS z5R9m5N>+pW_Z>WNNT-qw5c(4?jM8925tFi-4EIN(k>psD2qHvvIFX75<_XjEbdc6Sv$-zVkc1*}Fa_3mFq*)^#gwSQ5E3KFP^e{eOsfd#q|?eM zELSCfUbWz-)*zVV9#wK)|4$t6yE65M7N31#!GB@F^I}GLQQ3{xcW9*(Kcte8sJf2h ziJKY6pGD+0c2i&)F2+xAJY-}VD2DAU0)-QNnwRWX8ab|qt6#GY8aTzubFXq^wrkvV z>#N)~KHwNvFM+=mSxrRICb%q_5c@|H%?Az~KDbZ37Kuj1f#Gm`$Y91ud?2AfZY4!y z`5H^0N0!B-CO`G3hExm)4P{G08Pa))Rd`&VbH$?5>g(+8xzyIx71G6J55!MAa-B&Q z&0#cfdfe4OqK3&Nt5%&K9pYC^vD6YFE{jGL8f49RULNaEl!Own=m{nHDoqikQtP6q zV0<`CS~QWkqK2Z8D{=^ghgd(A19(ShOFf2QlKaZd*^cA{tF2TK<) zjI%mt=e*lyJz3A*jIj6jk8GT`e1+p}6@Tw5&slN*TJ@raDQ5-uQ`ZjbEWg95*;OVG z2l6rt8Av{{+5HcQ+@|^D^_i3`Y5ppw)TH^5wfT{YF3BQUlSB@?-UAo>%=)qMJi$u> z%JK?7W_vXpt}0e82u4^QJo!G?k?4}Q%Dzjn}KI(Q&> zdfdjA)wrAG=nt!MaNOR&?wDYGlfZWAP-N6FAS<<~FJM)k!Hv~T3^5u`V!R}kh6fT) zpSHbgr%jQi&~O-Ake<_==aA8WLg={{KUGFB$t@R`PrsTi-adI|*-<&OJL}l7Tv~lQ zcq=#?y!YZSzke_G>HbV>$3p4pDaW$IKjX+cwlDk2m;GCA`)>JWU2}&P{H>Fn%bxNj zPi@vyJM;S7;RVludHeiW_Hbv`)0q)E*T7Cy>IcY;8 zyQa87Ub4RBl!SK9L_{AOJb+!xHh$}(EQx2sSc;5p)m)M!+8NX9n?A&@q(75j)Ma!$ zfl~VL8w=Q!?zGz_;?XhjT6j$CWZRL7wGF9|lpzbkqR}WsBbydu6I-*cm1cB*9di>& zt@y2;M(|VqJjf&#f1^qlRW~eGx$C?F<*Trb-vHYatCf-Nv56%02&1VmvTkxJ=wNlbUD>DC! zO2Ycj>UQu11=mc4A>U{oz~%dqNtaPp5??fID$Vb6I*V_4LXVX*$Rkna1_bEfS6&FG zFFnPpMn_9q9y!npJbLXpPO#Y-H?Ctr5)rhXR(B#;znMtZG{@#cfO0EPoH1K{yWAfE zHVI1-Akno1MIt(u5OsN`wlJ~I=pcC8PkUfej%jW-0&JKel=D;<2sYMq^;&{a9?gaH zs+IHTrb<5&5TMu&iXV8k%$&%0_I&2)o=eZanF)4hgzm>lrKw<~c42Hckc#ycwZyCw zU@Z$65n@zaY!ekCA)ideP+{om{)&RiC5CN-hLl4%q zPU0*)Ix0gj_Vw*#a4xzNoe~Wx$SP|Dx=p6y(9oh|Cjz1bu6?kX}eLqrG`qmhVT9U4~E_yTB>Ww*0tPKKY8=cn+tU(GTWZ} z)SlUTChI&y0{A4q?DhTl+>LYZU;N<1(*Cap2yVhuuBb0N)1?0uwEnqDsDkU3jSxk63Nq81&yvj2q zp4US2NSx0jar^k+_5cH2xfWLB{EnX2dYEQ47=}_nyvx>sn4f31$Os>sU_4)sxlr=; zU}@0OGDJ4jg-2Qh46tDhh)kyg2tWd3T;c^CG)M`kwS*ibfC1!CVMAcRfa0_+VhJe~ zMJH$}o`!;-Pz^8-u}_drvtus?8c_)ND73={1ZE6S8tAQynGw*OsyvNn=t4GXTxFZ0 zu}9Rdyx{q=K4Kea9TPfyth zy;scGW+K_D#*C-&GtZg1SN|rGX+Dz?&O8y!-`KLB`-SV6^>6rN)^)oo;T*Qr3?o9o zu7GDo@*o|rjEhvb0t>yf2Kq_DTO5ObHo!l70sa+Y9~Xe2BYXo7&%-G3be?ZEu!dec17J>WscbL;FHeSPsH7)D>`Bs*mhHYPEm zG6tZ^QEVtdnm_mq_5f5DO-TT)0H|P~h{6maP(GtFGy_wNLMu;T2@r>2Es#}A=O|J! zV_@W`gU@$%pLsd2o7X?vy8l_D|9ZFCdvLnGOu*o(7zJU<09rA4oj9F@svMTctV4~Z zRG8ev6t)GHv58SZ*cmpD7|bDz4n`v+DOI!1LO|OGE1_B9$QaLkuw+fZ6J^ zKkyditemTO`o*c@&zzkz&&;;X96@9*O!9et`$9`+W>4pWv-6=9xpl<+!^S=Qx*?;* z#Djnvgutj)}QXlmYgu4zps?PV__PsF6OLE!YQw#ltN07@&;8x++l$Ix^EtHC4#IU zP?=6xKbD-xG#+0pIq}Gbr&Z2MYJj-| zl1x>S>bhYjV`@EqtG`2_bLO{6E|UW%_=3V(FtTEEPd4Q&}_bsY5xn$ZOe~rHfV}t0Osx78{mKmYudVL4ZPVKv|*(Ky`%?(u&Yq}I6!(0 ztHWTJJ;X4FxSOr|y6M(jUF|P*TJJdGg~ax& z#;m__ZtH^onaR%kzRI7dGw0@n1z+>z*@v`uy?b`9bE)Zgw(0o%^~I*M%Vkwd&Z>;F zYS~$gy{)8j1ruL3B`kYs=roA8~I@y-}Ws3D=Xi2Ncfd-0CCNlh)S9@E?-lIkYT{w)`*pz_|b}8 zJ&S-etqH4SnOK8@M(FK^t6-&13u#Ll$|rO%11Hhs2dv%WjizDo8x7U_pV>d&^Aa=E z6NcFZ%ee_yVq~jm5ihWP2K&t@kUeBR1UojSqr24O?)-*%1O?rBxI|AdFxaDsp&^;h zDH21PFZ6t8&+~1UPMr;Pb-dKk6|gC!nkt0$*q~@GW;_q26d15$vZ~U_3Sk49dSmYW zA==v@S7D`Qb`*u+@vrbx0|+L$2mT|IojJkcs(~1_c^|<5>3RFy56<0ubJj6eHW!&M zn;-aGIH~KqYiA)3LYhzynOV|2X^XG|@`XiBscFkyi@8WK($;{`efkd1D7b|Ei~0ir zx}LQ4Mz|84h{#}`4&_f#>TmH=X#;|(&EcLJ_~AGJ*nOdVx@V?-w)JyiH@K1Y`}7^y z`3a6{0+~cKK}jT%%1bDzOj1A#LHQvCls14LyJ!{Vd2J96x9OCf|2hAM^O|HCA)&Y7 zN(-hS(Z?rk-N1=3x|OvR>>dyWN%?y6$T&MS!*R9uGbWk#oDkV_BrZiz@KwZ}l9fTh9>iTEm1h3JJ?-8s)vyj7W~DZ1Z-ms;4VRSX zeYCw5CEc6#bkmag4Sd3r_9*<#|B|NuzX5YD2?2JQ)-t5O$mp^eeLVLh$~L3x8>!v( z#G1bI2uyg>Ud1xa-)zAQuyZuUo7}dM7M9}#qxA@*iRA=^t)RTl%w7!D6H_fS^JxN}p?)L9cxBCiqNQ1)7>n@@t0beS8yy4fC44VBmDK|!h z?APov(@6kNu$8Df!u2-;1}cW*bQ|o3Ly1@is@#yl*~o zG-4C}w4e2IrqDJx3tnz{DMRy3>IJ*<)Rm-5Sh-&&$za2-kt1fTaFDDkn=ZxCva(pYPnIdZtox$ zkPX+`y#qa~_}JKK-YHMEnOUdGrz+qXRQZ;3?QUYyZMLiUlc&rQ(iSKe+})}IYNDQ` z9Z*joV_pZkop4RKC(4o?W(lGtU3Ry+P~v8#z$MS*-vfmK^V;s#d`zeY7mxi3w)L%W zH`)jWrtq2}A!rk0qhrc2o?)P6=j@te7^V!Ik~5_f+C7P@-_p*_OaV#}#V8FYQr48J zXf}2pqx@USM(|M~yO~*zc2{7(o0LvRyiAD&DAe3Ii5<8SG7hllJ=vz2RY&vCA+Yf< z?l7!EC{v~zbO@}#dkH#ER9>%s64oG&UpUJZ4w zYMuTM09%ju09rKP3e~K647+>?Dh$K5hCVhq$@DSZ9aU_PmQ{`+}HGx4{I}~ne&kf%YXx?P=QV5Q*(^JJwHF^yV z9le3VNGFTT?L#3evO*A&A#Wu?F|ygQQ%KF0Bp*LTp@awp4G1(J9P9Lz80t?&pd#D# z^Ciun*DMY5nP$b=kK&<{q+&7b9;j5_ruZZU1@xa^@JzSV{DwKyg#D_{);6$n=&ELe zPL5lpdL=T7u8fX_)KPejg`*)dduk0VeVy+g^Rro>tC;1=yTNER?73iPCuPl~qmI3c z5+fNWz3b~T%9^%k)6jwO8PdvG*|oMsN-brn18c{TsiVqm8W$CTN*I`_(Hny2bUPYj zA{P8F%H08>`Y$k+`8iL;l6!mBy?xnPvRq{TmsBtNDjpR%Jod>`U->y_<+87I%KfFg z{9E$oO1Y|p7Dz-Ry;iLmt-pTX8e0sYbNba=#Va<%^DXK}C$p)K(jTU?{?-)-y=3j8J4^l@S^thDe-nB% z_sW9*V9vw2njaQ(-hF?MMx30tXxjD8@x{7l7V8dC3aCf3wxY&mqb<uP4^S90~ps)L>b0DCb*T;<&xm7%iS=miAbn_sh zv|{@5?bmL-wotNTwk=z->$8%Ra}}R#y|eY+i+Aeg4`l-^b zx3Is9DBVqz?m?IFmKdlu>#dyu>+FK5uc(c`&xQ~bxABZ~4rcrZS8Vi1bXWc)Hrw&h z`47)8RPI~w@4t8XuU`AbYgzwu>&AjAF8|E?$_$^`)|#nmMRf7-OACizA%p%lGCPXY zJZ2CU=v`>}UiqT;;0k9c`kv7`Vyy8a8UK--gL7?t=;pk2DD=Py`{eelbNg(?obBG0 z`I33-+(bq^_IJ+Xxsa8X(!+i$=dEQ65&bCJwp7}jEp4V{>-3ucepok`YsE)CXPJHS z{G(FN=~;B2{N)t{i;k1`ou%;9E9v6F3f0?|%d5UBak<>Na?W2i<;JYkwUC3}zO1u) z$~Kjn9=PwUnyHwz&9(lWGe|Cb`@R(X)6#7DT>E_G=fZOjJjIje9(OAt&;r26t^Q90 z-)0aoLFL36XH1wt;92v{CXjqNK1ooHUEge|05k$bq3c`nz06{TQ? zfE9TvOmg>0{?=#R^$YG@lc&CLR1~CgKJ~t%>isPr)V^CgJFrl_=kA$Ly6$w%mn}3M z`}FANjtlpl~VW1tUbt?MuvOz`B<&m4H%w3C}2 zGlsu4o!kzSe~j4)xW1!X8{81|<6Y)lq=RO(lT7IK6+9I3PCf8Zh()4+$gug-c`pzL zgU_HbtR(Q^1>xwvz5@QIj_aqu3N|`}zt_Z{%2XIhu^X z(vK5y)<5zV3deUdR|>#E97T|Od_Q>xv118+fT@d34E3`_-93n*@+i);$&yWx*9UKfPk$J}vsfYlmYH?_pV=fO(2{WWeXi4o(lqG9dfWPxJl zsGk3HMEOrB@DXt|A!&fFa?ZPJ(eo6!&N{pq|Gv*0`<8uW%O3wX2-E+IqHB41GrO?K z-~SrIv>+}ajv?j>LNiGkGrKnP-z${+mV8eVvw8nNlNwXZjyGLoYlF?E$!g@P+pH^U zE`Fm~yZrwV;uBaCTMHj~K8f5eM*NB^uK1@knAuOTwYF(@o%-Qt3K z=LS)nPrdKhJ9lc4<`&rb$#VJG&mGTylU&a4rxrB-$Fl(YEW#QS43|#+8yUz_nC`+k zF&M!DM#xWpVMiRvn{Y8W##JcyE%^$JV7)7t<{<31WoAul7y<3$q-oz~i~ti0bk$h1 zIt=wz+Q1CV$U`<(-DpD^HooFw8`2g^eUE~_qF^rtPvQ-1Vr;i&nb?+1`wXPCzJ0N( zDEQCBD1C@LiBbMZsD0oTr}iwl{}s3;U_0I2eY!hvQggAdwD5V5syXOj7P3>hh;Hbh z<1)n>5NP(~*eJcD-qWYUoI+=#3UQi3_CRebK6Glyf7R0h_cb}Hw&-RVW^_;pb!j#0 zv$0Qe=;IUYKe{1d`(_zTyA9+u)AqHx&2kmK^wTX4Y<6hI@BtLmcJ>J#`^ZlJe2!T# zh-LI|=p3OCJjX*J<~rA?|)7(vKMHDKklA%?e*o9A1aQarp2tQ;CX9Kd1L} zwxH~!AV2}*6PkT|u-JC78{Ss>A*HRo54y>>;F6wC`S3hHK+^HiFdN|+6W znMatRgdxH2(_4lP2@~q7Is1x{CB(np2XFaj4#7&P$77jv zO&lsIFbA4rHqXb1!GB z4`jH?oZZ7;x;Tffo#TIfV3pmWuUIN3tC#rlEMGozX_4QV zvv_#>EWR$nZPm(I>RAGacr2g5zrfEPfKrdUk&~j(xgX2wN0sd>9BxKN#BxtPp3lpl zs@4T+ZKW)|TzXqN3&S;CGxpIN`&m8*T=(|Vp z`2+8t8~P-AC(5Fqo?^FOx8vgo-oMF!*tyaz%QbcMUn{P7e0&%Gz>N<-C<5KlQ#Rdt zqdDhbk52Q^#U7zGS4{WZcs|EU!JuGwuN2W6qiZZvW1;?v8Z_~+C}H7!*gJDpimyXs?aB%71>%~)4C+;WvXe|UOemBTIPEapq@ hS#y}8l6hg3Zn>Qv-Z$Mt{Orrs7E$~WW<__?_WNnFcH6kBQ&xp70J=2D!IGz!V# z-WmPwC{+RjOBDtzzf>}sT{};a*ICq$Ui6^>+K1itg$z|lp30~f+X9<6HOgYMe%f

8e!1wq!HfgSzkpIGh^)*(AonJGAtPp`Dh@04m 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 CwVTxd literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..bd02bfe8780e61ed6ff64c63ade3d108c81df6aa GIT binary patch literal 8032 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py b/.venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py new file mode 100644 index 0000000..0e18c6e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/locations/_distutils.py @@ -0,0 +1,172 @@ +"""Locations where we look for configs, install stuff, etc""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +# If pip's going to use distutils, it should not be using the copy that setuptools +# might have injected into the environment. This is done by removing the injected +# shim, if it's injected. +# +# See https://github.com/pypa/pip/issues/8761 for the original discussion and +# rationale for why this is done within pip. +try: + __import__("_distutils_hack").remove_shim() +except (ImportError, AttributeError): + pass + +import logging +import os +import sys +from distutils.cmd import Command as DistutilsCommand +from distutils.command.install import SCHEME_KEYS +from distutils.command.install import install as distutils_install_command +from distutils.sysconfig import get_python_lib +from typing import Dict, List, Optional, Union, cast + +from pip._internal.models.scheme import Scheme +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.virtualenv import running_under_virtualenv + +from .base import get_major_minor_version + +logger = logging.getLogger(__name__) + + +def distutils_scheme( + dist_name: str, + user: bool = False, + home: Optional[str] = None, + root: Optional[str] = None, + isolated: bool = False, + prefix: Optional[str] = None, + *, + ignore_config_files: bool = False, +) -> Dict[str, str]: + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + dist_args: Dict[str, Union[str, List[str]]] = {"name": dist_name} + if isolated: + dist_args["script_args"] = ["--no-user-cfg"] + + d = Distribution(dist_args) + if not ignore_config_files: + try: + d.parse_config_files() + except UnicodeDecodeError: + paths = d.find_config_files() + logger.warning( + "Ignore distutils configs in %s due to encoding errors.", + ", ".join(os.path.basename(p) for p in paths), + ) + obj: Optional[DistutilsCommand] = None + obj = d.get_command_obj("install", create=True) + assert obj is not None + i = cast(distutils_install_command, obj) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), f"user={user} prefix={prefix}" + assert not (home and prefix), f"home={home} prefix={prefix}" + i.user = user or i.user + if user or home: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + + scheme = {} + for key in SCHEME_KEYS: + scheme[key] = getattr(i, "install_" + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if "install_lib" in d.get_option_dict("install"): + scheme.update({"purelib": i.install_lib, "platlib": i.install_lib}) + + if running_under_virtualenv(): + if home: + prefix = home + elif user: + prefix = i.install_userbase + else: + prefix = i.prefix + scheme["headers"] = os.path.join( + prefix, + "include", + "site", + f"python{get_major_minor_version()}", + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive(os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join(root, path_no_drive[1:]) + + return scheme + + +def get_scheme( + dist_name: str, + user: bool = False, + home: Optional[str] = None, + root: Optional[str] = None, + isolated: bool = False, + prefix: Optional[str] = None, +) -> Scheme: + """ + Get the "scheme" corresponding to the input parameters. The distutils + documentation provides the context for the available schemes: + https://docs.python.org/3/install/index.html#alternate-installation + + :param dist_name: the name of the package to retrieve the scheme for, used + in the headers scheme path + :param user: indicates to use the "user" scheme + :param home: indicates to use the "home" scheme and provides the base + directory for the same + :param root: root under which other directories are re-based + :param isolated: equivalent to --no-user-cfg, i.e. do not consider + ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for + scheme paths + :param prefix: indicates to use the "prefix" scheme and provides the + base directory for the same + """ + scheme = distutils_scheme(dist_name, user, home, root, isolated, prefix) + return Scheme( + platlib=scheme["platlib"], + purelib=scheme["purelib"], + headers=scheme["headers"], + scripts=scheme["scripts"], + data=scheme["data"], + ) + + +def get_bin_prefix() -> str: + # XXX: In old virtualenv versions, sys.prefix can contain '..' components, + # so we need to call normpath to eliminate them. + prefix = os.path.normpath(sys.prefix) + if WINDOWS: + bin_py = os.path.join(prefix, "Scripts") + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(prefix, "bin") + return bin_py + # Forcing to use /usr/local/bin for standard macOS framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == "darwin" and prefix[:16] == "/System/Library/": + return "/usr/local/bin" + return os.path.join(prefix, "bin") + + +def get_purelib() -> str: + return get_python_lib(plat_specific=False) + + +def get_platlib() -> str: + return get_python_lib(plat_specific=True) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py b/.venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py new file mode 100644 index 0000000..97aef1f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/locations/_sysconfig.py @@ -0,0 +1,213 @@ +import logging +import os +import sys +import sysconfig +import typing + +from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid +from pip._internal.models.scheme import SCHEME_KEYS, Scheme +from pip._internal.utils.virtualenv import running_under_virtualenv + +from .base import change_root, get_major_minor_version, is_osx_framework + +logger = logging.getLogger(__name__) + + +# Notes on _infer_* functions. +# Unfortunately ``get_default_scheme()`` didn't exist before 3.10, so there's no +# way to ask things like "what is the '_prefix' scheme on this platform". These +# functions try to answer that with some heuristics while accounting for ad-hoc +# platforms not covered by CPython's default sysconfig implementation. If the +# ad-hoc implementation does not fully implement sysconfig, we'll fall back to +# a POSIX scheme. + +_AVAILABLE_SCHEMES = set(sysconfig.get_scheme_names()) + +_PREFERRED_SCHEME_API = getattr(sysconfig, "get_preferred_scheme", None) + + +def _should_use_osx_framework_prefix() -> bool: + """Check for Apple's ``osx_framework_library`` scheme. + + Python distributed by Apple's Command Line Tools has this special scheme + that's used when: + + * This is a framework build. + * We are installing into the system prefix. + + This does not account for ``pip install --prefix`` (also means we're not + installing to the system prefix), which should use ``posix_prefix``, but + logic here means ``_infer_prefix()`` outputs ``osx_framework_library``. But + since ``prefix`` is not available for ``sysconfig.get_default_scheme()``, + which is the stdlib replacement for ``_infer_prefix()``, presumably Apple + wouldn't be able to magically switch between ``osx_framework_library`` and + ``posix_prefix``. ``_infer_prefix()`` returning ``osx_framework_library`` + means its behavior is consistent whether we use the stdlib implementation + or our own, and we deal with this special case in ``get_scheme()`` instead. + """ + return ( + "osx_framework_library" in _AVAILABLE_SCHEMES + and not running_under_virtualenv() + and is_osx_framework() + ) + + +def _infer_prefix() -> str: + """Try to find a prefix scheme for the current platform. + + This tries: + + * A special ``osx_framework_library`` for Python distributed by Apple's + Command Line Tools, when not running in a virtual environment. + * Implementation + OS, used by PyPy on Windows (``pypy_nt``). + * Implementation without OS, used by PyPy on POSIX (``pypy``). + * OS + "prefix", used by CPython on POSIX (``posix_prefix``). + * Just the OS name, used by CPython on Windows (``nt``). + + If none of the above works, fall back to ``posix_prefix``. + """ + if _PREFERRED_SCHEME_API: + return _PREFERRED_SCHEME_API("prefix") + if _should_use_osx_framework_prefix(): + return "osx_framework_library" + implementation_suffixed = f"{sys.implementation.name}_{os.name}" + if implementation_suffixed in _AVAILABLE_SCHEMES: + return implementation_suffixed + if sys.implementation.name in _AVAILABLE_SCHEMES: + return sys.implementation.name + suffixed = f"{os.name}_prefix" + if suffixed in _AVAILABLE_SCHEMES: + return suffixed + if os.name in _AVAILABLE_SCHEMES: # On Windows, prefx is just called "nt". + return os.name + return "posix_prefix" + + +def _infer_user() -> str: + """Try to find a user scheme for the current platform.""" + if _PREFERRED_SCHEME_API: + return _PREFERRED_SCHEME_API("user") + if is_osx_framework() and not running_under_virtualenv(): + suffixed = "osx_framework_user" + else: + suffixed = f"{os.name}_user" + if suffixed in _AVAILABLE_SCHEMES: + return suffixed + if "posix_user" not in _AVAILABLE_SCHEMES: # User scheme unavailable. + raise UserInstallationInvalid() + return "posix_user" + + +def _infer_home() -> str: + """Try to find a home for the current platform.""" + if _PREFERRED_SCHEME_API: + return _PREFERRED_SCHEME_API("home") + suffixed = f"{os.name}_home" + if suffixed in _AVAILABLE_SCHEMES: + return suffixed + return "posix_home" + + +# Update these keys if the user sets a custom home. +_HOME_KEYS = [ + "installed_base", + "base", + "installed_platbase", + "platbase", + "prefix", + "exec_prefix", +] +if sysconfig.get_config_var("userbase") is not None: + _HOME_KEYS.append("userbase") + + +def get_scheme( + dist_name: str, + user: bool = False, + home: typing.Optional[str] = None, + root: typing.Optional[str] = None, + isolated: bool = False, + prefix: typing.Optional[str] = None, +) -> Scheme: + """ + Get the "scheme" corresponding to the input parameters. + + :param dist_name: the name of the package to retrieve the scheme for, used + in the headers scheme path + :param user: indicates to use the "user" scheme + :param home: indicates to use the "home" scheme + :param root: root under which other directories are re-based + :param isolated: ignored, but kept for distutils compatibility (where + this controls whether the user-site pydistutils.cfg is honored) + :param prefix: indicates to use the "prefix" scheme and provides the + base directory for the same + """ + if user and prefix: + raise InvalidSchemeCombination("--user", "--prefix") + if home and prefix: + raise InvalidSchemeCombination("--home", "--prefix") + + if home is not None: + scheme_name = _infer_home() + elif user: + scheme_name = _infer_user() + else: + scheme_name = _infer_prefix() + + # Special case: When installing into a custom prefix, use posix_prefix + # instead of osx_framework_library. See _should_use_osx_framework_prefix() + # docstring for details. + if prefix is not None and scheme_name == "osx_framework_library": + scheme_name = "posix_prefix" + + if home is not None: + variables = {k: home for k in _HOME_KEYS} + elif prefix is not None: + variables = {k: prefix for k in _HOME_KEYS} + else: + variables = {} + + paths = sysconfig.get_paths(scheme=scheme_name, vars=variables) + + # Logic here is very arbitrary, we're doing it for compatibility, don't ask. + # 1. Pip historically uses a special header path in virtual environments. + # 2. If the distribution name is not known, distutils uses 'UNKNOWN'. We + # only do the same when not running in a virtual environment because + # pip's historical header path logic (see point 1) did not do this. + if running_under_virtualenv(): + if user: + base = variables.get("userbase", sys.prefix) + else: + base = variables.get("base", sys.prefix) + python_xy = f"python{get_major_minor_version()}" + paths["include"] = os.path.join(base, "include", "site", python_xy) + elif not dist_name: + dist_name = "UNKNOWN" + + scheme = Scheme( + platlib=paths["platlib"], + purelib=paths["purelib"], + headers=os.path.join(paths["include"], dist_name), + scripts=paths["scripts"], + data=paths["data"], + ) + if root is not None: + for key in SCHEME_KEYS: + value = change_root(root, getattr(scheme, key)) + setattr(scheme, key, value) + return scheme + + +def get_bin_prefix() -> str: + # Forcing to use /usr/local/bin for standard macOS framework installs. + if sys.platform[:6] == "darwin" and sys.prefix[:16] == "/System/Library/": + return "/usr/local/bin" + return sysconfig.get_paths()["scripts"] + + +def get_purelib() -> str: + return sysconfig.get_paths()["purelib"] + + +def get_platlib() -> str: + return sysconfig.get_paths()["platlib"] diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/locations/base.py b/.venv/lib/python3.12/site-packages/pip/_internal/locations/base.py new file mode 100644 index 0000000..3f9f896 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/locations/base.py @@ -0,0 +1,81 @@ +import functools +import os +import site +import sys +import sysconfig +import typing + +from pip._internal.exceptions import InstallationError +from pip._internal.utils import appdirs +from pip._internal.utils.virtualenv import running_under_virtualenv + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + +# FIXME doesn't account for venv linked to global site-packages +site_packages: str = sysconfig.get_path("purelib") + + +def get_major_minor_version() -> str: + """ + Return the major-minor version of the current Python as a string, e.g. + "3.7" or "3.10". + """ + return "{}.{}".format(*sys.version_info) + + +def change_root(new_root: str, pathname: str) -> str: + """Return 'pathname' with 'new_root' prepended. + + If 'pathname' is relative, this is equivalent to os.path.join(new_root, pathname). + Otherwise, it requires making 'pathname' relative and then joining the + two, which is tricky on DOS/Windows and Mac OS. + + This is borrowed from Python's standard library's distutils module. + """ + if os.name == "posix": + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) + else: + return os.path.join(new_root, pathname[1:]) + + elif os.name == "nt": + (drive, path) = os.path.splitdrive(pathname) + if path[0] == "\\": + path = path[1:] + return os.path.join(new_root, path) + + else: + raise InstallationError( + f"Unknown platform: {os.name}\n" + "Can not change root path prefix on unknown platform." + ) + + +def get_src_prefix() -> str: + if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, "src") + else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), "src") + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit("The folder you are executing pip from can no longer be found.") + + # under macOS + virtualenv sys.prefix is not properly resolved + # it is something like /path/to/python/bin/.. + return os.path.abspath(src_prefix) + + +try: + # Use getusersitepackages if this is present, as it ensures that the + # value is initialised properly. + user_site: typing.Optional[str] = site.getusersitepackages() +except AttributeError: + user_site = site.USER_SITE + + +@functools.lru_cache(maxsize=None) +def is_osx_framework() -> bool: + return bool(sysconfig.get_config_var("PYTHONFRAMEWORK")) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/main.py b/.venv/lib/python3.12/site-packages/pip/_internal/main.py new file mode 100644 index 0000000..33c6d24 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/main.py @@ -0,0 +1,12 @@ +from typing import List, Optional + + +def main(args: Optional[List[str]] = None) -> int: + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py new file mode 100644 index 0000000..aa232b6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__init__.py @@ -0,0 +1,128 @@ +import contextlib +import functools +import os +import sys +from typing import TYPE_CHECKING, List, Optional, Type, cast + +from pip._internal.utils.misc import strtobool + +from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel + +if TYPE_CHECKING: + from typing import Literal, Protocol +else: + Protocol = object + +__all__ = [ + "BaseDistribution", + "BaseEnvironment", + "FilesystemWheel", + "MemoryWheel", + "Wheel", + "get_default_environment", + "get_environment", + "get_wheel_distribution", + "select_backend", +] + + +def _should_use_importlib_metadata() -> bool: + """Whether to use the ``importlib.metadata`` or ``pkg_resources`` backend. + + By default, pip uses ``importlib.metadata`` on Python 3.11+, and + ``pkg_resourcess`` otherwise. This can be overridden by a couple of ways: + + * If environment variable ``_PIP_USE_IMPORTLIB_METADATA`` is set, it + dictates whether ``importlib.metadata`` is used, regardless of Python + version. + * On Python 3.11+, Python distributors can patch ``importlib.metadata`` + to add a global constant ``_PIP_USE_IMPORTLIB_METADATA = False``. This + makes pip use ``pkg_resources`` (unless the user set the aforementioned + environment variable to *True*). + """ + with contextlib.suppress(KeyError, ValueError): + return bool(strtobool(os.environ["_PIP_USE_IMPORTLIB_METADATA"])) + if sys.version_info < (3, 11): + return False + import importlib.metadata + + return bool(getattr(importlib.metadata, "_PIP_USE_IMPORTLIB_METADATA", True)) + + +class Backend(Protocol): + NAME: 'Literal["importlib", "pkg_resources"]' + Distribution: Type[BaseDistribution] + Environment: Type[BaseEnvironment] + + +@functools.lru_cache(maxsize=None) +def select_backend() -> Backend: + if _should_use_importlib_metadata(): + from . import importlib + + return cast(Backend, importlib) + from . import pkg_resources + + return cast(Backend, pkg_resources) + + +def get_default_environment() -> BaseEnvironment: + """Get the default representation for the current environment. + + This returns an Environment instance from the chosen backend. The default + Environment instance should be built from ``sys.path`` and may use caching + to share instance state accorss calls. + """ + return select_backend().Environment.default() + + +def get_environment(paths: Optional[List[str]]) -> BaseEnvironment: + """Get a representation of the environment specified by ``paths``. + + This returns an Environment instance from the chosen backend based on the + given import paths. The backend must build a fresh instance representing + the state of installed distributions when this function is called. + """ + return select_backend().Environment.from_paths(paths) + + +def get_directory_distribution(directory: str) -> BaseDistribution: + """Get the distribution metadata representation in the specified directory. + + This returns a Distribution instance from the chosen backend based on + the given on-disk ``.dist-info`` directory. + """ + return select_backend().Distribution.from_directory(directory) + + +def get_wheel_distribution(wheel: Wheel, canonical_name: str) -> BaseDistribution: + """Get the representation of the specified wheel's distribution metadata. + + This returns a Distribution instance from the chosen backend based on + the given wheel's ``.dist-info`` directory. + + :param canonical_name: Normalized project name of the given wheel. + """ + return select_backend().Distribution.from_wheel(wheel, canonical_name) + + +def get_metadata_distribution( + metadata_contents: bytes, + filename: str, + canonical_name: str, +) -> BaseDistribution: + """Get the dist representation of the specified METADATA file contents. + + This returns a Distribution instance from the chosen backend sourced from the data + in `metadata_contents`. + + :param metadata_contents: Contents of a METADATA file within a dist, or one served + via PEP 658. + :param filename: Filename for the dist this metadata represents. + :param canonical_name: Normalized project name of the given dist. + """ + return select_backend().Distribution.from_metadata_file_contents( + metadata_contents, + filename, + canonical_name, + ) 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 new file mode 100644 index 0000000000000000000000000000000000000000..3526aa939fab9cfdca4970ed626609be112da5b5 GIT binary patch literal 5903 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e6adcfdc17b44839289359a918290223cff7dab9 GIT binary patch literal 35733 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b68d6b1ca7505fc600ec0f1063aa248cdfbb2312 GIT binary patch literal 15811 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py new file mode 100644 index 0000000..27362fc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/_json.py @@ -0,0 +1,84 @@ +# Extracted from https://github.com/pfmoore/pkg_metadata + +from email.header import Header, decode_header, make_header +from email.message import Message +from typing import Any, Dict, List, Union + +METADATA_FIELDS = [ + # Name, Multiple-Use + ("Metadata-Version", False), + ("Name", False), + ("Version", False), + ("Dynamic", True), + ("Platform", True), + ("Supported-Platform", True), + ("Summary", False), + ("Description", False), + ("Description-Content-Type", False), + ("Keywords", False), + ("Home-page", False), + ("Download-URL", False), + ("Author", False), + ("Author-email", False), + ("Maintainer", False), + ("Maintainer-email", False), + ("License", False), + ("Classifier", True), + ("Requires-Dist", True), + ("Requires-Python", False), + ("Requires-External", True), + ("Project-URL", True), + ("Provides-Extra", True), + ("Provides-Dist", True), + ("Obsoletes-Dist", True), +] + + +def json_name(field: str) -> str: + return field.lower().replace("-", "_") + + +def msg_to_json(msg: Message) -> Dict[str, Any]: + """Convert a Message object into a JSON-compatible dictionary.""" + + def sanitise_header(h: Union[Header, str]) -> str: + if isinstance(h, Header): + chunks = [] + for bytes, encoding in decode_header(h): + if encoding == "unknown-8bit": + try: + # See if UTF-8 works + bytes.decode("utf-8") + encoding = "utf-8" + except UnicodeDecodeError: + # If not, latin1 at least won't fail + encoding = "latin1" + chunks.append((bytes, encoding)) + return str(make_header(chunks)) + return str(h) + + result = {} + for field, multi in METADATA_FIELDS: + if field not in msg: + continue + key = json_name(field) + if multi: + value: Union[str, List[str]] = [ + sanitise_header(v) for v in msg.get_all(field) # type: ignore + ] + else: + value = sanitise_header(msg.get(field)) # type: ignore + if key == "keywords": + # Accept both comma-separated and space-separated + # forms, for better compatibility with old data. + if "," in value: + value = [v.strip() for v in value.split(",")] + else: + value = value.split() + result[key] = value + + payload = msg.get_payload() + if payload: + result["description"] = payload + + return result diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py new file mode 100644 index 0000000..9249124 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/base.py @@ -0,0 +1,702 @@ +import csv +import email.message +import functools +import json +import logging +import pathlib +import re +import zipfile +from typing import ( + IO, + TYPE_CHECKING, + Any, + Collection, + Container, + Dict, + Iterable, + Iterator, + List, + NamedTuple, + Optional, + Tuple, + Union, +) + +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.version import LegacyVersion, Version + +from pip._internal.exceptions import NoneMetadataError +from pip._internal.locations import site_packages, user_site +from pip._internal.models.direct_url import ( + DIRECT_URL_METADATA_NAME, + DirectUrl, + DirectUrlValidationError, +) +from pip._internal.utils.compat import stdlib_pkgs # TODO: Move definition here. +from pip._internal.utils.egg_link import egg_link_path_from_sys_path +from pip._internal.utils.misc import is_local, normalize_path +from pip._internal.utils.urls import url_to_path + +from ._json import msg_to_json + +if TYPE_CHECKING: + from typing import Protocol +else: + Protocol = object + +DistributionVersion = Union[LegacyVersion, Version] + +InfoPath = Union[str, pathlib.PurePath] + +logger = logging.getLogger(__name__) + + +class BaseEntryPoint(Protocol): + @property + def name(self) -> str: + raise NotImplementedError() + + @property + def value(self) -> str: + raise NotImplementedError() + + @property + def group(self) -> str: + raise NotImplementedError() + + +def _convert_installed_files_path( + entry: Tuple[str, ...], + info: Tuple[str, ...], +) -> str: + """Convert a legacy installed-files.txt path into modern RECORD path. + + The legacy format stores paths relative to the info directory, while the + modern format stores paths relative to the package root, e.g. the + site-packages directory. + + :param entry: Path parts of the installed-files.txt entry. + :param info: Path parts of the egg-info directory relative to package root. + :returns: The converted entry. + + For best compatibility with symlinks, this does not use ``abspath()`` or + ``Path.resolve()``, but tries to work with path parts: + + 1. While ``entry`` starts with ``..``, remove the equal amounts of parts + from ``info``; if ``info`` is empty, start appending ``..`` instead. + 2. Join the two directly. + """ + while entry and entry[0] == "..": + if not info or info[-1] == "..": + info += ("..",) + else: + info = info[:-1] + entry = entry[1:] + return str(pathlib.Path(*info, *entry)) + + +class RequiresEntry(NamedTuple): + requirement: str + extra: str + marker: str + + +class BaseDistribution(Protocol): + @classmethod + def from_directory(cls, directory: str) -> "BaseDistribution": + """Load the distribution from a metadata directory. + + :param directory: Path to a metadata directory, e.g. ``.dist-info``. + """ + raise NotImplementedError() + + @classmethod + def from_metadata_file_contents( + cls, + metadata_contents: bytes, + filename: str, + project_name: str, + ) -> "BaseDistribution": + """Load the distribution from the contents of a METADATA file. + + This is used to implement PEP 658 by generating a "shallow" dist object that can + be used for resolution without downloading or building the actual dist yet. + + :param metadata_contents: The contents of a METADATA file. + :param filename: File name for the dist with this metadata. + :param project_name: Name of the project this dist represents. + """ + raise NotImplementedError() + + @classmethod + def from_wheel(cls, wheel: "Wheel", name: str) -> "BaseDistribution": + """Load the distribution from a given wheel. + + :param wheel: A concrete wheel definition. + :param name: File name of the wheel. + + :raises InvalidWheel: Whenever loading of the wheel causes a + :py:exc:`zipfile.BadZipFile` exception to be thrown. + :raises UnsupportedWheel: If the wheel is a valid zip, but malformed + internally. + """ + raise NotImplementedError() + + def __repr__(self) -> str: + return f"{self.raw_name} {self.version} ({self.location})" + + def __str__(self) -> str: + return f"{self.raw_name} {self.version}" + + @property + def location(self) -> Optional[str]: + """Where the distribution is loaded from. + + A string value is not necessarily a filesystem path, since distributions + can be loaded from other sources, e.g. arbitrary zip archives. ``None`` + means the distribution is created in-memory. + + Do not canonicalize this value with e.g. ``pathlib.Path.resolve()``. If + this is a symbolic link, we want to preserve the relative path between + it and files in the distribution. + """ + raise NotImplementedError() + + @property + def editable_project_location(self) -> Optional[str]: + """The project location for editable distributions. + + This is the directory where pyproject.toml or setup.py is located. + None if the distribution is not installed in editable mode. + """ + # TODO: this property is relatively costly to compute, memoize it ? + direct_url = self.direct_url + if direct_url: + if direct_url.is_local_editable(): + return url_to_path(direct_url.url) + else: + # Search for an .egg-link file by walking sys.path, as it was + # done before by dist_is_editable(). + egg_link_path = egg_link_path_from_sys_path(self.raw_name) + if egg_link_path: + # TODO: get project location from second line of egg_link file + # (https://github.com/pypa/pip/issues/10243) + return self.location + return None + + @property + def installed_location(self) -> Optional[str]: + """The distribution's "installed" location. + + This should generally be a ``site-packages`` directory. This is + usually ``dist.location``, except for legacy develop-installed packages, + where ``dist.location`` is the source code location, and this is where + the ``.egg-link`` file is. + + The returned location is normalized (in particular, with symlinks removed). + """ + raise NotImplementedError() + + @property + def info_location(self) -> Optional[str]: + """Location of the .[egg|dist]-info directory or file. + + Similarly to ``location``, a string value is not necessarily a + filesystem path. ``None`` means the distribution is created in-memory. + + For a modern .dist-info installation on disk, this should be something + like ``{location}/{raw_name}-{version}.dist-info``. + + Do not canonicalize this value with e.g. ``pathlib.Path.resolve()``. If + this is a symbolic link, we want to preserve the relative path between + it and other files in the distribution. + """ + raise NotImplementedError() + + @property + def installed_by_distutils(self) -> bool: + """Whether this distribution is installed with legacy distutils format. + + A distribution installed with "raw" distutils not patched by setuptools + uses one single file at ``info_location`` to store metadata. We need to + treat this specially on uninstallation. + """ + info_location = self.info_location + if not info_location: + return False + return pathlib.Path(info_location).is_file() + + @property + def installed_as_egg(self) -> bool: + """Whether this distribution is installed as an egg. + + This usually indicates the distribution was installed by (older versions + of) easy_install. + """ + location = self.location + if not location: + return False + return location.endswith(".egg") + + @property + def installed_with_setuptools_egg_info(self) -> bool: + """Whether this distribution is installed with the ``.egg-info`` format. + + This usually indicates the distribution was installed with setuptools + with an old pip version or with ``single-version-externally-managed``. + + Note that this ensure the metadata store is a directory. distutils can + also installs an ``.egg-info``, but as a file, not a directory. This + property is *False* for that case. Also see ``installed_by_distutils``. + """ + info_location = self.info_location + if not info_location: + return False + if not info_location.endswith(".egg-info"): + return False + return pathlib.Path(info_location).is_dir() + + @property + def installed_with_dist_info(self) -> bool: + """Whether this distribution is installed with the "modern format". + + This indicates a "modern" installation, e.g. storing metadata in the + ``.dist-info`` directory. This applies to installations made by + setuptools (but through pip, not directly), or anything using the + standardized build backend interface (PEP 517). + """ + info_location = self.info_location + if not info_location: + return False + if not info_location.endswith(".dist-info"): + return False + return pathlib.Path(info_location).is_dir() + + @property + def canonical_name(self) -> NormalizedName: + raise NotImplementedError() + + @property + def version(self) -> DistributionVersion: + raise NotImplementedError() + + @property + def setuptools_filename(self) -> str: + """Convert a project name to its setuptools-compatible filename. + + This is a copy of ``pkg_resources.to_filename()`` for compatibility. + """ + return self.raw_name.replace("-", "_") + + @property + def direct_url(self) -> Optional[DirectUrl]: + """Obtain a DirectUrl from this distribution. + + Returns None if the distribution has no `direct_url.json` metadata, + or if `direct_url.json` is invalid. + """ + try: + content = self.read_text(DIRECT_URL_METADATA_NAME) + except FileNotFoundError: + return None + try: + return DirectUrl.from_json(content) + except ( + UnicodeDecodeError, + json.JSONDecodeError, + DirectUrlValidationError, + ) as e: + logger.warning( + "Error parsing %s for %s: %s", + DIRECT_URL_METADATA_NAME, + self.canonical_name, + e, + ) + return None + + @property + def installer(self) -> str: + try: + installer_text = self.read_text("INSTALLER") + except (OSError, ValueError, NoneMetadataError): + return "" # Fail silently if the installer file cannot be read. + for line in installer_text.splitlines(): + cleaned_line = line.strip() + if cleaned_line: + return cleaned_line + return "" + + @property + def requested(self) -> bool: + return self.is_file("REQUESTED") + + @property + def editable(self) -> bool: + return bool(self.editable_project_location) + + @property + def local(self) -> bool: + """If distribution is installed in the current virtual environment. + + Always True if we're not in a virtualenv. + """ + if self.installed_location is None: + return False + return is_local(self.installed_location) + + @property + def in_usersite(self) -> bool: + if self.installed_location is None or user_site is None: + return False + return self.installed_location.startswith(normalize_path(user_site)) + + @property + def in_site_packages(self) -> bool: + if self.installed_location is None or site_packages is None: + return False + return self.installed_location.startswith(normalize_path(site_packages)) + + def is_file(self, path: InfoPath) -> bool: + """Check whether an entry in the info directory is a file.""" + raise NotImplementedError() + + def iter_distutils_script_names(self) -> Iterator[str]: + """Find distutils 'scripts' entries metadata. + + If 'scripts' is supplied in ``setup.py``, distutils records those in the + installed distribution's ``scripts`` directory, a file for each script. + """ + raise NotImplementedError() + + def read_text(self, path: InfoPath) -> str: + """Read a file in the info directory. + + :raise FileNotFoundError: If ``path`` does not exist in the directory. + :raise NoneMetadataError: If ``path`` exists in the info directory, but + cannot be read. + """ + raise NotImplementedError() + + def iter_entry_points(self) -> Iterable[BaseEntryPoint]: + raise NotImplementedError() + + def _metadata_impl(self) -> email.message.Message: + raise NotImplementedError() + + @functools.lru_cache(maxsize=1) + def _metadata_cached(self) -> email.message.Message: + # When we drop python 3.7 support, move this to the metadata property and use + # functools.cached_property instead of lru_cache. + metadata = self._metadata_impl() + self._add_egg_info_requires(metadata) + return metadata + + @property + def metadata(self) -> email.message.Message: + """Metadata of distribution parsed from e.g. METADATA or PKG-INFO. + + This should return an empty message if the metadata file is unavailable. + + :raises NoneMetadataError: If the metadata file is available, but does + not contain valid metadata. + """ + return self._metadata_cached() + + @property + def metadata_dict(self) -> Dict[str, Any]: + """PEP 566 compliant JSON-serializable representation of METADATA or PKG-INFO. + + This should return an empty dict if the metadata file is unavailable. + + :raises NoneMetadataError: If the metadata file is available, but does + not contain valid metadata. + """ + return msg_to_json(self.metadata) + + @property + def metadata_version(self) -> Optional[str]: + """Value of "Metadata-Version:" in distribution metadata, if available.""" + return self.metadata.get("Metadata-Version") + + @property + def raw_name(self) -> str: + """Value of "Name:" in distribution metadata.""" + # The metadata should NEVER be missing the Name: key, but if it somehow + # does, fall back to the known canonical name. + return self.metadata.get("Name", self.canonical_name) + + @property + def requires_python(self) -> SpecifierSet: + """Value of "Requires-Python:" in distribution metadata. + + If the key does not exist or contains an invalid value, an empty + SpecifierSet should be returned. + """ + value = self.metadata.get("Requires-Python") + if value is None: + return SpecifierSet() + try: + # Convert to str to satisfy the type checker; this can be a Header object. + spec = SpecifierSet(str(value)) + except InvalidSpecifier as e: + message = "Package %r has an invalid Requires-Python: %s" + logger.warning(message, self.raw_name, e) + return SpecifierSet() + return spec + + def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]: + """Dependencies of this distribution. + + For modern .dist-info distributions, this is the collection of + "Requires-Dist:" entries in distribution metadata. + """ + raise NotImplementedError() + + def iter_provided_extras(self) -> Iterable[str]: + """Extras provided by this distribution. + + For modern .dist-info distributions, this is the collection of + "Provides-Extra:" entries in distribution metadata. + + The return value of this function is not particularly useful other than + display purposes due to backward compatibility issues and the extra + names being poorly normalized prior to PEP 685. If you want to perform + logic operations on extras, use :func:`is_extra_provided` instead. + """ + raise NotImplementedError() + + def is_extra_provided(self, extra: str) -> bool: + """Check whether an extra is provided by this distribution. + + This is needed mostly for compatibility issues with pkg_resources not + following the extra normalization rules defined in PEP 685. + """ + raise NotImplementedError() + + def _iter_declared_entries_from_record(self) -> Optional[Iterator[str]]: + try: + text = self.read_text("RECORD") + except FileNotFoundError: + return None + # This extra Path-str cast normalizes entries. + return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines())) + + def _iter_declared_entries_from_legacy(self) -> Optional[Iterator[str]]: + try: + text = self.read_text("installed-files.txt") + except FileNotFoundError: + return None + paths = (p for p in text.splitlines(keepends=False) if p) + root = self.location + info = self.info_location + if root is None or info is None: + return paths + try: + info_rel = pathlib.Path(info).relative_to(root) + except ValueError: # info is not relative to root. + return paths + if not info_rel.parts: # info *is* root. + return paths + return ( + _convert_installed_files_path(pathlib.Path(p).parts, info_rel.parts) + for p in paths + ) + + def iter_declared_entries(self) -> Optional[Iterator[str]]: + """Iterate through file entries declared in this distribution. + + For modern .dist-info distributions, this is the files listed in the + ``RECORD`` metadata file. For legacy setuptools distributions, this + comes from ``installed-files.txt``, with entries normalized to be + compatible with the format used by ``RECORD``. + + :return: An iterator for listed entries, or None if the distribution + contains neither ``RECORD`` nor ``installed-files.txt``. + """ + return ( + self._iter_declared_entries_from_record() + or self._iter_declared_entries_from_legacy() + ) + + def _iter_requires_txt_entries(self) -> Iterator[RequiresEntry]: + """Parse a ``requires.txt`` in an egg-info directory. + + This is an INI-ish format where an egg-info stores dependencies. A + section name describes extra other environment markers, while each entry + is an arbitrary string (not a key-value pair) representing a dependency + as a requirement string (no markers). + + There is a construct in ``importlib.metadata`` called ``Sectioned`` that + does mostly the same, but the format is currently considered private. + """ + try: + content = self.read_text("requires.txt") + except FileNotFoundError: + return + extra = marker = "" # Section-less entries don't have markers. + for line in content.splitlines(): + line = line.strip() + if not line or line.startswith("#"): # Comment; ignored. + continue + if line.startswith("[") and line.endswith("]"): # A section header. + extra, _, marker = line.strip("[]").partition(":") + continue + yield RequiresEntry(requirement=line, extra=extra, marker=marker) + + def _iter_egg_info_extras(self) -> Iterable[str]: + """Get extras from the egg-info directory.""" + known_extras = {""} + for entry in self._iter_requires_txt_entries(): + extra = canonicalize_name(entry.extra) + if extra in known_extras: + continue + known_extras.add(extra) + yield extra + + def _iter_egg_info_dependencies(self) -> Iterable[str]: + """Get distribution dependencies from the egg-info directory. + + To ease parsing, this converts a legacy dependency entry into a PEP 508 + requirement string. Like ``_iter_requires_txt_entries()``, there is code + in ``importlib.metadata`` that does mostly the same, but not do exactly + what we need. + + Namely, ``importlib.metadata`` does not normalize the extra name before + putting it into the requirement string, which causes marker comparison + to fail because the dist-info format do normalize. This is consistent in + all currently available PEP 517 backends, although not standardized. + """ + for entry in self._iter_requires_txt_entries(): + extra = canonicalize_name(entry.extra) + if extra and entry.marker: + marker = f'({entry.marker}) and extra == "{extra}"' + elif extra: + marker = f'extra == "{extra}"' + elif entry.marker: + marker = entry.marker + else: + marker = "" + if marker: + yield f"{entry.requirement} ; {marker}" + else: + yield entry.requirement + + def _add_egg_info_requires(self, metadata: email.message.Message) -> None: + """Add egg-info requires.txt information to the metadata.""" + if not metadata.get_all("Requires-Dist"): + for dep in self._iter_egg_info_dependencies(): + metadata["Requires-Dist"] = dep + if not metadata.get_all("Provides-Extra"): + for extra in self._iter_egg_info_extras(): + metadata["Provides-Extra"] = extra + + +class BaseEnvironment: + """An environment containing distributions to introspect.""" + + @classmethod + def default(cls) -> "BaseEnvironment": + raise NotImplementedError() + + @classmethod + def from_paths(cls, paths: Optional[List[str]]) -> "BaseEnvironment": + raise NotImplementedError() + + def get_distribution(self, name: str) -> Optional["BaseDistribution"]: + """Given a requirement name, return the installed distributions. + + The name may not be normalized. The implementation must canonicalize + it for lookup. + """ + raise NotImplementedError() + + def _iter_distributions(self) -> Iterator["BaseDistribution"]: + """Iterate through installed distributions. + + This function should be implemented by subclass, but never called + directly. Use the public ``iter_distribution()`` instead, which + implements additional logic to make sure the distributions are valid. + """ + raise NotImplementedError() + + def iter_all_distributions(self) -> Iterator[BaseDistribution]: + """Iterate through all installed distributions without any filtering.""" + for dist in self._iter_distributions(): + # Make sure the distribution actually comes from a valid Python + # packaging distribution. Pip's AdjacentTempDirectory leaves folders + # e.g. ``~atplotlib.dist-info`` if cleanup was interrupted. The + # valid project name pattern is taken from PEP 508. + project_name_valid = re.match( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", + dist.canonical_name, + flags=re.IGNORECASE, + ) + if not project_name_valid: + logger.warning( + "Ignoring invalid distribution %s (%s)", + dist.canonical_name, + dist.location, + ) + continue + yield dist + + def iter_installed_distributions( + self, + local_only: bool = True, + skip: Container[str] = stdlib_pkgs, + include_editables: bool = True, + editables_only: bool = False, + user_only: bool = False, + ) -> Iterator[BaseDistribution]: + """Return a list of installed distributions. + + This is based on ``iter_all_distributions()`` with additional filtering + options. Note that ``iter_installed_distributions()`` without arguments + is *not* equal to ``iter_all_distributions()``, since some of the + configurations exclude packages by default. + + :param local_only: If True (default), only return installations + local to the current virtualenv, if in a virtualenv. + :param skip: An iterable of canonicalized project names to ignore; + defaults to ``stdlib_pkgs``. + :param include_editables: If False, don't report editables. + :param editables_only: If True, only report editables. + :param user_only: If True, only report installations in the user + site directory. + """ + it = self.iter_all_distributions() + if local_only: + it = (d for d in it if d.local) + if not include_editables: + it = (d for d in it if not d.editable) + if editables_only: + it = (d for d in it if d.editable) + if user_only: + it = (d for d in it if d.in_usersite) + return (d for d in it if d.canonical_name not in skip) + + +class Wheel(Protocol): + location: str + + def as_zipfile(self) -> zipfile.ZipFile: + raise NotImplementedError() + + +class FilesystemWheel(Wheel): + def __init__(self, location: str) -> None: + self.location = location + + def as_zipfile(self) -> zipfile.ZipFile: + return zipfile.ZipFile(self.location, allowZip64=True) + + +class MemoryWheel(Wheel): + def __init__(self, location: str, stream: IO[bytes]) -> None: + self.location = location + self.stream = stream + + def as_zipfile(self) -> zipfile.ZipFile: + return zipfile.ZipFile(self.stream, allowZip64=True) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py new file mode 100644 index 0000000..a779138 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__init__.py @@ -0,0 +1,6 @@ +from ._dists import Distribution +from ._envs import Environment + +__all__ = ["NAME", "Distribution", "Environment"] + +NAME = "importlib" 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 new file mode 100644 index 0000000000000000000000000000000000000000..5404e2655a32886f515b467d3d9286faff1942a3 GIT binary patch literal 379 zcmX|7Jx@Y06m8!JD9B>sY8-t7H0WwfFfu}n<6D|Sn@6=DFKr=VlgXdp?&e?7fAA6# zqLZ5m8U=A7G}+_zS%fuL5ek$i;yu^s*@`X%dAAWw)Rjup~4$9T&l73nyS zF)9#`pK+e>Bv0pQR{O+Y&v12cEq!ouG!4=kn45hwlg=6~OvvIY8TJN!7lYR&0M)+K zrFB8c(J-r3HOgVz@AN9A!p!`StxhRZic*)t(hFJxqRoO#tQN#GubCk?&fbY}@Z{Ex zY>;D52G$hz`l{XYUKYCmq%(sz3H(ONd6-yp-szqZFN0{8Y<$lO;Yle=LZu198K#I9 zfpHcX`Oirxs4`I6Dd(HL3vKySiA&dlMB5YiBY>|MW4uHsOLY9+=swg}^}|KtnxK3G DXO3)o literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..d90dd3a17e6ec6d96c342ebe6f12567b3baed762 GIT binary patch literal 3354 zcmbVOO>7)V74Dwt8P9n9mpHrF?3&cFUOD5<&LoRqBa{T?hs~l@yaBTO%w=5eE_>SP z>1wKL?2Ok)*2)r*0LceLEGdn_IOYO% zsCxCP>eZ|F-uGVpb$EEtLi@*GQhsH?vi?bvZp(CtgFk_|Z+Vty2Uf+#mdjmmv?5+$|-wbY+k@ zE|0a_y`9EvXL4{G#C=N`BUDnJEz--m&RXBFY)sn60O_Sf)=PWNV%Ez*l>sj;vWqD% z3p&?yiUUdED)(**%{{Jp;&^_nldKXvmLZM0p~5*g)sbmqB$|wTz|h^d*6nqx3aZ`K zQ<%{%)0)OEP};noR~y0#-47JYqBoNF^}s!EW;+t!qsv4J90s4 z-;K*RWw<1-Uwz|B9Q#XQU4*(^S`pz&Iq(T<~!@ff-d>&dB!d(O}D zPhNcap7Zm<0eF_%do(5#zs5utK_Gr%qGeFew~yc)taOdrw~UqauQT7Ziq72H7?fdM z&6yvvwG=z^)Gn$K9N4kdD2AABXNt5-2CCIqE4*U-=qn79oDiQzhew=v<>Tq!7JpOx z_@xI|AL!qIu=Bx#`lIu^qnCDbm()q@Ddx#0A+L*SH5sf{>(XlkL=RP~KWgxxvy$&w zGs$)JOopx_kd(jBpeRq$pw$V8R0Ojf%ZNAXDhgxMkqT94usZ?fa`-l5e<-Mu$ z{?x_2sf#TOtUr&NVfWkbAJDXwvon{s#}6$GZHXl82!O~7)DAA7>jzLJEgV2211O|7 zorh+VWmU4^W<95mGtkTcB)O|R7Pq*r@t?rdS6I}+ECWZg6|Q{Qh#3N@#^i#DT;|Gf zQL1`ra1i0mZrA*niMudL%oc>p8?j(LyOp|TV_;)lLd^U;NkfR1=-T;t#zW6Ynx9|u zqeVXuorSZs!d3+fh42{1z4f{b*#Z=1P=M)XBv=g?h}oI5V#F;XH1u8mrVb7KZK;4#@`*6irKa<*jlr~=$}1bDsNL}7N{(mhai z9sHrKhOx9`C*yNcf2$q^f>Md_zNwT{>IJOzW67ZqUmNZ^zeH222M2WXhV`$JS00bN zx|@5|MBo3ViTW}m9lJLhaRs&3Mte868B?_RHR=eTbH$V~fnt~xbLtDUFhL#ZNQuCc zF5sF&v_?YdrZVTJX~zt8U!smAwV}v}qK-04{55o&)@P|)Cf7P;Wk*^mJ2SoIeh#!v zAEdjCRuK3XowSZj>7}~08rd(+e+du1?IXT*O|;>K zpdy;;R5!iM#Lx1h=PT3=shXXzeCNuwcfa07pAvzONCE(k0}mo1<*#r}0H_l32ywc7 zg(xuuiwQXnob+Ga!88d4`_5Sw%l{&sFytbJc-a&vYHUc0M0$d&%aj;k256LD*UX1$8JPylJLQ68nOmjs$8CXJHg{_LV znuSaI0dxXOsH%&y8c3H@El?$Fp`ee)(QR0VC#|9Jt@B$;+xl@~de`Yh4}BSq0LlTw z)Km?kKWa3A-n4b=VTWF(-Osfj+ZzAOc3Zb~@B7p}6Ax=bwP_TIze{yjKD5_uipI4+ z+z@c!@#q7-DCaXu5506qkwlFCK%zh}`H!z~Ki~@iia^M2ncoY;_vIWz!VX|@Rbt;q zR48F=t+|37$%o=DcQw33*|42XZ#JW5FQc;>DWm!d^8%5?-lpT^mAH^Wn90j4BGss%VHs&KoJ7O|4>-;!|$;pHY^H)&ku|!Ue z-$Vxo4_QNFzesQ0{^>XN3#a!Ar#G&DT6kg0`_;8i3hdL7$?fUgnYTX~xv`tO@uV=i z@vg}p#nI&Cag?erLSm9dMT0Hc>XM@sWfb#-Wbk`DXoz+)H$MwTOD|DJ*~SE4`$eSX za|!Ti?qd6kb6L?pgo;P(qidyY+y0w1`FHEwW9!^uHfvwrnmx2I9G*O3Pv5(HXklob z$=k1OPwr2?zBl>${^W(d$qOwDtVciiV{Px!dj~|fCi3>!c7A_ic5h;Kf8wpZiMMFa M*rRKQG?=IV2XDAPx&QzG literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..651bbca3b920d241f5bc1243727892f07e2b9e2d GIT binary patch literal 13446 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..405a3b936e7d1184c3a1dc4eab323b3107fce227 GIT binary patch literal 11201 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 Mga5_h#DwjC0Rsk#)Bpeg literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py new file mode 100644 index 0000000..593bff2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_compat.py @@ -0,0 +1,55 @@ +import importlib.metadata +from typing import Any, Optional, Protocol, cast + + +class BadMetadata(ValueError): + def __init__(self, dist: importlib.metadata.Distribution, *, reason: str) -> None: + self.dist = dist + self.reason = reason + + def __str__(self) -> str: + return f"Bad metadata in {self.dist} ({self.reason})" + + +class BasePath(Protocol): + """A protocol that various path objects conform. + + This exists because importlib.metadata uses both ``pathlib.Path`` and + ``zipfile.Path``, and we need a common base for type hints (Union does not + work well since ``zipfile.Path`` is too new for our linter setup). + + This does not mean to be exhaustive, but only contains things that present + in both classes *that we need*. + """ + + @property + def name(self) -> str: + raise NotImplementedError() + + @property + def parent(self) -> "BasePath": + raise NotImplementedError() + + +def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]: + """Find the path to the distribution's metadata directory. + + HACK: This relies on importlib.metadata's private ``_path`` attribute. Not + all distributions exist on disk, so importlib.metadata is correct to not + expose the attribute as public. But pip's code base is old and not as clean, + so we do this to avoid having to rewrite too many things. Hopefully we can + eliminate this some day. + """ + return getattr(d, "_path", None) + + +def get_dist_name(dist: importlib.metadata.Distribution) -> str: + """Get the distribution's project name. + + The ``name`` attribute is only available in Python 3.10 or later. We are + targeting exactly that, but Mypy does not know this. + """ + name = cast(Any, dist).name + if not isinstance(name, str): + raise BadMetadata(dist, reason="invalid metadata entry 'name'") + return name diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py new file mode 100644 index 0000000..26370fa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_dists.py @@ -0,0 +1,227 @@ +import email.message +import importlib.metadata +import os +import pathlib +import zipfile +from typing import ( + Collection, + Dict, + Iterable, + Iterator, + Mapping, + Optional, + Sequence, + cast, +) + +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.exceptions import InvalidWheel, UnsupportedWheel +from pip._internal.metadata.base import ( + BaseDistribution, + BaseEntryPoint, + DistributionVersion, + InfoPath, + Wheel, +) +from pip._internal.utils.misc import normalize_path +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file + +from ._compat import BasePath, get_dist_name + + +class WheelDistribution(importlib.metadata.Distribution): + """An ``importlib.metadata.Distribution`` read from a wheel. + + Although ``importlib.metadata.PathDistribution`` accepts ``zipfile.Path``, + its implementation is too "lazy" for pip's needs (we can't keep the ZipFile + handle open for the entire lifetime of the distribution object). + + This implementation eagerly reads the entire metadata directory into the + memory instead, and operates from that. + """ + + def __init__( + self, + files: Mapping[pathlib.PurePosixPath, bytes], + info_location: pathlib.PurePosixPath, + ) -> None: + self._files = files + self.info_location = info_location + + @classmethod + def from_zipfile( + cls, + zf: zipfile.ZipFile, + name: str, + location: str, + ) -> "WheelDistribution": + info_dir, _ = parse_wheel(zf, name) + paths = ( + (name, pathlib.PurePosixPath(name.split("/", 1)[-1])) + for name in zf.namelist() + if name.startswith(f"{info_dir}/") + ) + files = { + relpath: read_wheel_metadata_file(zf, fullpath) + for fullpath, relpath in paths + } + info_location = pathlib.PurePosixPath(location, info_dir) + return cls(files, info_location) + + def iterdir(self, path: InfoPath) -> Iterator[pathlib.PurePosixPath]: + # Only allow iterating through the metadata directory. + if pathlib.PurePosixPath(str(path)) in self._files: + return iter(self._files) + raise FileNotFoundError(path) + + def read_text(self, filename: str) -> Optional[str]: + try: + data = self._files[pathlib.PurePosixPath(filename)] + except KeyError: + return None + try: + text = data.decode("utf-8") + except UnicodeDecodeError as e: + wheel = self.info_location.parent + error = f"Error decoding metadata for {wheel}: {e} in {filename} file" + raise UnsupportedWheel(error) + return text + + +class Distribution(BaseDistribution): + def __init__( + self, + dist: importlib.metadata.Distribution, + info_location: Optional[BasePath], + installed_location: Optional[BasePath], + ) -> None: + self._dist = dist + self._info_location = info_location + self._installed_location = installed_location + + @classmethod + def from_directory(cls, directory: str) -> BaseDistribution: + info_location = pathlib.Path(directory) + dist = importlib.metadata.Distribution.at(info_location) + return cls(dist, info_location, info_location.parent) + + @classmethod + def from_metadata_file_contents( + cls, + metadata_contents: bytes, + filename: str, + project_name: str, + ) -> BaseDistribution: + # Generate temp dir to contain the metadata file, and write the file contents. + temp_dir = pathlib.Path( + TempDirectory(kind="metadata", globally_managed=True).path + ) + metadata_path = temp_dir / "METADATA" + metadata_path.write_bytes(metadata_contents) + # Construct dist pointing to the newly created directory. + dist = importlib.metadata.Distribution.at(metadata_path.parent) + return cls(dist, metadata_path.parent, None) + + @classmethod + def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution: + try: + with wheel.as_zipfile() as zf: + dist = WheelDistribution.from_zipfile(zf, name, wheel.location) + except zipfile.BadZipFile as e: + raise InvalidWheel(wheel.location, name) from e + except UnsupportedWheel as e: + raise UnsupportedWheel(f"{name} has an invalid wheel, {e}") + return cls(dist, dist.info_location, pathlib.PurePosixPath(wheel.location)) + + @property + def location(self) -> Optional[str]: + if self._info_location is None: + return None + return str(self._info_location.parent) + + @property + def info_location(self) -> Optional[str]: + if self._info_location is None: + return None + return str(self._info_location) + + @property + def installed_location(self) -> Optional[str]: + if self._installed_location is None: + return None + return normalize_path(str(self._installed_location)) + + def _get_dist_name_from_location(self) -> Optional[str]: + """Try to get the name from the metadata directory name. + + This is much faster than reading metadata. + """ + if self._info_location is None: + return None + stem, suffix = os.path.splitext(self._info_location.name) + if suffix not in (".dist-info", ".egg-info"): + return None + return stem.split("-", 1)[0] + + @property + def canonical_name(self) -> NormalizedName: + name = self._get_dist_name_from_location() or get_dist_name(self._dist) + return canonicalize_name(name) + + @property + def version(self) -> DistributionVersion: + return parse_version(self._dist.version) + + def is_file(self, path: InfoPath) -> bool: + return self._dist.read_text(str(path)) is not None + + def iter_distutils_script_names(self) -> Iterator[str]: + # A distutils installation is always "flat" (not in e.g. egg form), so + # if this distribution's info location is NOT a pathlib.Path (but e.g. + # zipfile.Path), it can never contain any distutils scripts. + if not isinstance(self._info_location, pathlib.Path): + return + for child in self._info_location.joinpath("scripts").iterdir(): + yield child.name + + def read_text(self, path: InfoPath) -> str: + content = self._dist.read_text(str(path)) + if content is None: + raise FileNotFoundError(path) + return content + + def iter_entry_points(self) -> Iterable[BaseEntryPoint]: + # importlib.metadata's EntryPoint structure sasitfies BaseEntryPoint. + return self._dist.entry_points + + def _metadata_impl(self) -> email.message.Message: + # From Python 3.10+, importlib.metadata declares PackageMetadata as the + # return type. This protocol is unfortunately a disaster now and misses + # a ton of fields that we need, including get() and get_payload(). We + # rely on the implementation that the object is actually a Message now, + # until upstream can improve the protocol. (python/cpython#94952) + return cast(email.message.Message, self._dist.metadata) + + def iter_provided_extras(self) -> Iterable[str]: + return self.metadata.get_all("Provides-Extra", []) + + def is_extra_provided(self, extra: str) -> bool: + return any( + canonicalize_name(provided_extra) == canonicalize_name(extra) + for provided_extra in self.metadata.get_all("Provides-Extra", []) + ) + + def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]: + contexts: Sequence[Dict[str, str]] = [{"extra": e} for e in extras] + for req_string in self.metadata.get_all("Requires-Dist", []): + req = Requirement(req_string) + if not req.marker: + yield req + elif not extras and req.marker.evaluate({"extra": ""}): + yield req + elif any(req.marker.evaluate(context) for context in contexts): + yield req diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py new file mode 100644 index 0000000..048dc55 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/importlib/_envs.py @@ -0,0 +1,189 @@ +import functools +import importlib.metadata +import logging +import os +import pathlib +import sys +import zipfile +import zipimport +from typing import Iterator, List, Optional, Sequence, Set, Tuple + +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name + +from pip._internal.metadata.base import BaseDistribution, BaseEnvironment +from pip._internal.models.wheel import Wheel +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filetypes import WHEEL_EXTENSION + +from ._compat import BadMetadata, BasePath, get_dist_name, get_info_location +from ._dists import Distribution + +logger = logging.getLogger(__name__) + + +def _looks_like_wheel(location: str) -> bool: + if not location.endswith(WHEEL_EXTENSION): + return False + if not os.path.isfile(location): + return False + if not Wheel.wheel_file_re.match(os.path.basename(location)): + return False + return zipfile.is_zipfile(location) + + +class _DistributionFinder: + """Finder to locate distributions. + + The main purpose of this class is to memoize found distributions' names, so + only one distribution is returned for each package name. At lot of pip code + assumes this (because it is setuptools's behavior), and not doing the same + can potentially cause a distribution in lower precedence path to override a + higher precedence one if the caller is not careful. + + Eventually we probably want to make it possible to see lower precedence + installations as well. It's useful feature, after all. + """ + + FoundResult = Tuple[importlib.metadata.Distribution, Optional[BasePath]] + + def __init__(self) -> None: + self._found_names: Set[NormalizedName] = set() + + def _find_impl(self, location: str) -> Iterator[FoundResult]: + """Find distributions in a location.""" + # Skip looking inside a wheel. Since a package inside a wheel is not + # always valid (due to .data directories etc.), its .dist-info entry + # should not be considered an installed distribution. + if _looks_like_wheel(location): + return + # To know exactly where we find a distribution, we have to feed in the + # paths one by one, instead of dumping the list to importlib.metadata. + for dist in importlib.metadata.distributions(path=[location]): + info_location = get_info_location(dist) + try: + raw_name = get_dist_name(dist) + except BadMetadata as e: + logger.warning("Skipping %s due to %s", info_location, e.reason) + continue + normalized_name = canonicalize_name(raw_name) + if normalized_name in self._found_names: + continue + self._found_names.add(normalized_name) + yield dist, info_location + + def find(self, location: str) -> Iterator[BaseDistribution]: + """Find distributions in a location. + + The path can be either a directory, or a ZIP archive. + """ + for dist, info_location in self._find_impl(location): + if info_location is None: + installed_location: Optional[BasePath] = None + else: + installed_location = info_location.parent + yield Distribution(dist, info_location, installed_location) + + def find_linked(self, location: str) -> Iterator[BaseDistribution]: + """Read location in egg-link files and return distributions in there. + + The path should be a directory; otherwise this returns nothing. This + follows how setuptools does this for compatibility. The first non-empty + line in the egg-link is read as a path (resolved against the egg-link's + containing directory if relative). Distributions found at that linked + location are returned. + """ + path = pathlib.Path(location) + if not path.is_dir(): + return + for child in path.iterdir(): + if child.suffix != ".egg-link": + continue + with child.open() as f: + lines = (line.strip() for line in f) + target_rel = next((line for line in lines if line), "") + if not target_rel: + continue + target_location = str(path.joinpath(target_rel)) + for dist, info_location in self._find_impl(target_location): + yield Distribution(dist, info_location, path) + + def _find_eggs_in_dir(self, location: str) -> Iterator[BaseDistribution]: + from pip._vendor.pkg_resources import find_distributions + + from pip._internal.metadata import pkg_resources as legacy + + with os.scandir(location) as it: + for entry in it: + if not entry.name.endswith(".egg"): + continue + for dist in find_distributions(entry.path): + yield legacy.Distribution(dist) + + def _find_eggs_in_zip(self, location: str) -> Iterator[BaseDistribution]: + from pip._vendor.pkg_resources import find_eggs_in_zip + + from pip._internal.metadata import pkg_resources as legacy + + try: + importer = zipimport.zipimporter(location) + except zipimport.ZipImportError: + return + for dist in find_eggs_in_zip(importer, location): + yield legacy.Distribution(dist) + + def find_eggs(self, location: str) -> Iterator[BaseDistribution]: + """Find eggs in a location. + + This actually uses the old *pkg_resources* backend. We likely want to + deprecate this so we can eventually remove the *pkg_resources* + dependency entirely. Before that, this should first emit a deprecation + warning for some versions when using the fallback since importing + *pkg_resources* is slow for those who don't need it. + """ + if os.path.isdir(location): + yield from self._find_eggs_in_dir(location) + if zipfile.is_zipfile(location): + yield from self._find_eggs_in_zip(location) + + +@functools.lru_cache(maxsize=None) # Warn a distribution exactly once. +def _emit_egg_deprecation(location: Optional[str]) -> None: + deprecated( + reason=f"Loading egg at {location} is deprecated.", + replacement="to use pip for package installation.", + gone_in="24.3", + issue=12330, + ) + + +class Environment(BaseEnvironment): + def __init__(self, paths: Sequence[str]) -> None: + self._paths = paths + + @classmethod + def default(cls) -> BaseEnvironment: + return cls(sys.path) + + @classmethod + def from_paths(cls, paths: Optional[List[str]]) -> BaseEnvironment: + if paths is None: + return cls(sys.path) + return cls(paths) + + def _iter_distributions(self) -> Iterator[BaseDistribution]: + finder = _DistributionFinder() + for location in self._paths: + yield from finder.find(location) + for dist in finder.find_eggs(location): + _emit_egg_deprecation(dist.location) + yield dist + # This must go last because that's how pkg_resources tie-breaks. + yield from finder.find_linked(location) + + def get_distribution(self, name: str) -> Optional[BaseDistribution]: + matches = ( + distribution + for distribution in self.iter_all_distributions() + if distribution.canonical_name == canonicalize_name(name) + ) + return next(matches, None) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py new file mode 100644 index 0000000..bb11e5b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/metadata/pkg_resources.py @@ -0,0 +1,278 @@ +import email.message +import email.parser +import logging +import os +import zipfile +from typing import Collection, Iterable, Iterator, List, Mapping, NamedTuple, Optional + +from pip._vendor import pkg_resources +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.exceptions import InvalidWheel, NoneMetadataError, UnsupportedWheel +from pip._internal.utils.egg_link import egg_link_path_from_location +from pip._internal.utils.misc import display_path, normalize_path +from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file + +from .base import ( + BaseDistribution, + BaseEntryPoint, + BaseEnvironment, + DistributionVersion, + InfoPath, + Wheel, +) + +__all__ = ["NAME", "Distribution", "Environment"] + +logger = logging.getLogger(__name__) + +NAME = "pkg_resources" + + +class EntryPoint(NamedTuple): + name: str + value: str + group: str + + +class InMemoryMetadata: + """IMetadataProvider that reads metadata files from a dictionary. + + This also maps metadata decoding exceptions to our internal exception type. + """ + + def __init__(self, metadata: Mapping[str, bytes], wheel_name: str) -> None: + self._metadata = metadata + self._wheel_name = wheel_name + + def has_metadata(self, name: str) -> bool: + return name in self._metadata + + def get_metadata(self, name: str) -> str: + try: + return self._metadata[name].decode() + except UnicodeDecodeError as e: + # Augment the default error with the origin of the file. + raise UnsupportedWheel( + f"Error decoding metadata for {self._wheel_name}: {e} in {name} file" + ) + + def get_metadata_lines(self, name: str) -> Iterable[str]: + return pkg_resources.yield_lines(self.get_metadata(name)) + + def metadata_isdir(self, name: str) -> bool: + return False + + def metadata_listdir(self, name: str) -> List[str]: + return [] + + def run_script(self, script_name: str, namespace: str) -> None: + pass + + +class Distribution(BaseDistribution): + def __init__(self, dist: pkg_resources.Distribution) -> None: + self._dist = dist + + @classmethod + def from_directory(cls, directory: str) -> BaseDistribution: + dist_dir = directory.rstrip(os.sep) + + # Build a PathMetadata object, from path to metadata. :wink: + base_dir, dist_dir_name = os.path.split(dist_dir) + metadata = pkg_resources.PathMetadata(base_dir, dist_dir) + + # Determine the correct Distribution object type. + if dist_dir.endswith(".egg-info"): + dist_cls = pkg_resources.Distribution + dist_name = os.path.splitext(dist_dir_name)[0] + else: + assert dist_dir.endswith(".dist-info") + dist_cls = pkg_resources.DistInfoDistribution + dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0] + + dist = dist_cls(base_dir, project_name=dist_name, metadata=metadata) + return cls(dist) + + @classmethod + def from_metadata_file_contents( + cls, + metadata_contents: bytes, + filename: str, + project_name: str, + ) -> BaseDistribution: + metadata_dict = { + "METADATA": metadata_contents, + } + dist = pkg_resources.DistInfoDistribution( + location=filename, + metadata=InMemoryMetadata(metadata_dict, filename), + project_name=project_name, + ) + return cls(dist) + + @classmethod + def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution: + try: + with wheel.as_zipfile() as zf: + info_dir, _ = parse_wheel(zf, name) + metadata_dict = { + path.split("/", 1)[-1]: read_wheel_metadata_file(zf, path) + for path in zf.namelist() + if path.startswith(f"{info_dir}/") + } + except zipfile.BadZipFile as e: + raise InvalidWheel(wheel.location, name) from e + except UnsupportedWheel as e: + raise UnsupportedWheel(f"{name} has an invalid wheel, {e}") + dist = pkg_resources.DistInfoDistribution( + location=wheel.location, + metadata=InMemoryMetadata(metadata_dict, wheel.location), + project_name=name, + ) + return cls(dist) + + @property + def location(self) -> Optional[str]: + return self._dist.location + + @property + def installed_location(self) -> Optional[str]: + egg_link = egg_link_path_from_location(self.raw_name) + if egg_link: + location = egg_link + elif self.location: + location = self.location + else: + return None + return normalize_path(location) + + @property + def info_location(self) -> Optional[str]: + return self._dist.egg_info + + @property + def installed_by_distutils(self) -> bool: + # A distutils-installed distribution is provided by FileMetadata. This + # provider has a "path" attribute not present anywhere else. Not the + # best introspection logic, but pip has been doing this for a long time. + try: + return bool(self._dist._provider.path) + except AttributeError: + return False + + @property + def canonical_name(self) -> NormalizedName: + return canonicalize_name(self._dist.project_name) + + @property + def version(self) -> DistributionVersion: + return parse_version(self._dist.version) + + def is_file(self, path: InfoPath) -> bool: + return self._dist.has_metadata(str(path)) + + def iter_distutils_script_names(self) -> Iterator[str]: + yield from self._dist.metadata_listdir("scripts") + + def read_text(self, path: InfoPath) -> str: + name = str(path) + if not self._dist.has_metadata(name): + raise FileNotFoundError(name) + content = self._dist.get_metadata(name) + if content is None: + raise NoneMetadataError(self, name) + return content + + def iter_entry_points(self) -> Iterable[BaseEntryPoint]: + for group, entries in self._dist.get_entry_map().items(): + for name, entry_point in entries.items(): + name, _, value = str(entry_point).partition("=") + yield EntryPoint(name=name.strip(), value=value.strip(), group=group) + + def _metadata_impl(self) -> email.message.Message: + """ + :raises NoneMetadataError: if the distribution reports `has_metadata()` + True but `get_metadata()` returns None. + """ + if isinstance(self._dist, pkg_resources.DistInfoDistribution): + metadata_name = "METADATA" + else: + metadata_name = "PKG-INFO" + try: + metadata = self.read_text(metadata_name) + except FileNotFoundError: + if self.location: + displaying_path = display_path(self.location) + else: + displaying_path = repr(self.location) + logger.warning("No metadata found in %s", displaying_path) + metadata = "" + feed_parser = email.parser.FeedParser() + feed_parser.feed(metadata) + return feed_parser.close() + + def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]: + if extras: # pkg_resources raises on invalid extras, so we sanitize. + extras = frozenset(pkg_resources.safe_extra(e) for e in extras) + extras = extras.intersection(self._dist.extras) + return self._dist.requires(extras) + + def iter_provided_extras(self) -> Iterable[str]: + return self._dist.extras + + def is_extra_provided(self, extra: str) -> bool: + return pkg_resources.safe_extra(extra) in self._dist.extras + + +class Environment(BaseEnvironment): + def __init__(self, ws: pkg_resources.WorkingSet) -> None: + self._ws = ws + + @classmethod + def default(cls) -> BaseEnvironment: + return cls(pkg_resources.working_set) + + @classmethod + def from_paths(cls, paths: Optional[List[str]]) -> BaseEnvironment: + return cls(pkg_resources.WorkingSet(paths)) + + def _iter_distributions(self) -> Iterator[BaseDistribution]: + for dist in self._ws: + yield Distribution(dist) + + def _search_distribution(self, name: str) -> Optional[BaseDistribution]: + """Find a distribution matching the ``name`` in the environment. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + canonical_name = canonicalize_name(name) + for dist in self.iter_all_distributions(): + if dist.canonical_name == canonical_name: + return dist + return None + + def get_distribution(self, name: str) -> Optional[BaseDistribution]: + # Search the distribution by looking through the working set. + dist = self._search_distribution(name) + if dist: + return dist + + # If distribution could not be found, call working_set.require to + # update the working set, and try to find the distribution again. + # This might happen for e.g. when you install a package twice, once + # using setup.py develop and again using setup.py install. Now when + # running pip uninstall twice, the package gets removed from the + # working set in the first uninstall, so we have to populate the + # working set again so that pip knows about it and the packages gets + # picked up and is successfully uninstalled the second time too. + try: + # We didn't pass in any version specifiers, so this can never + # raise pkg_resources.VersionConflict. + self._ws.require(name) + except pkg_resources.DistributionNotFound: + return None + return self._search_distribution(name) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000..7855226 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" 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 new file mode 100644 index 0000000000000000000000000000000000000000..84641359629a24f89960cbbc1bde638616f9b856 GIT binary patch literal 287 zcmXv~K}rKb5ZqaVhCu$X*Br8qdhsAi#H%76y$qAtX18^BW|-~-){poGzoI|zB=`X_ zx12IdVnNeYbttO)b2^=53Ev+%U8nssh<~U9*^Co;%c|@t>(wlOy;u~%*cVe{A&rs3 zdMAb)7oD%L<-Hj(L}YYQq#+^Xa&kXYUng2u-fI1xZHD;m3L4)bu;I=)c!>TPtuQ?L zr5Cacciz?h_GZ3drrLEW49YdSHMF#Z&`aapd3kyUOoH=6Ervh=G&#ZOObh#7pp!$Q hbs75Y_%fNR7O&!QYB5~O!y|r=lv01WQlmIZatC^gR!;x` literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/candidate.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65b3541c31e149cdb91193788fc776268b50b4e2 GIT binary patch literal 1926 zcmb7EO>Epm6rSp-q~mkjgDR#D%O&n-fG)(^f##G@@xGR9`H2J)1i5+S?f? z>V_cY5Q#(rK@TWF6#~JfQBVmdIEGUZTvBl$SgnNkIdEHPiKueo&8!`^)pB4Yzj-t7 zjpxnx-uHfJZ_gtbzkDxwpUVjS#uw>O6Qw>3$_jFki+xnamV~)3`EpsY6rsz$TFzJ* zj3o3La+O8oswA@x|Kr2TRx?KSPp%p`Rx1u=1Qcb=3#MW2x=5<$97f#pVHs@Xl6T7s zirhOLTB6jig7OYTLXd@BWJxZzmtf2Irl?^;{<+bAuD4LJ1>5#B@imgb`?- zBJrEz_6*Q0Mk)2vmlwgX`ytIxR+hCVchj|%Rdp=Q$99mZ9(R&eX+Rgoc$8&WJ!lWM%*B_%KLf=RpRJB%4w@%Fe~oGWZN_gR~TxnFyfb<#w}+z{%YHdqou=G*qt?Rgk@qfhn1bce1nEl zq!=-CBD@|(UXhuX!eBDIaBg6PF>f*`LmJJ(ED2^!-@9&Bs?m)wI8%7(w8^}P3{;%r zv@=PVS@9|+gcK1PIKEj9UE(uyFZG2=l^%cy=8m>3oju(3!#N!3D-F0|Lc{lg4 z=ftOP{@61BdRwyfyVky(eV`rQex`e+_jd2KuId?5UcHa=kl9 z&gJXyFH}s@&vmp{HZClHs(7l|DqKYJ_X;K1g6aPxKczHKMjD?phFZS6|E**6?HBcb zr7>(MaRz8ocA}?}xrXUMh=K8FbqWQFco9$_oo)X&tPT4ie()p+K(*uLpE^b!bc}4b zb-q8kJj$oB<*{T{&nlhy8d7q3%FjFj9ItS}H{M%-Z7un62RaQ{T7Y*4x=(8W!6JX0 z0icxexh{RLX7v7~`t>O3h`|kuunm-Bei$)OWw@5|ZXrsa9^o1fB5qG;StP!1+Xkjxuq$44RwPz% zS_n>V_9}?Si|A)%_}9)C7Dj7|rsh|#)DTQ}&xmQm71KA-4&SVGso?N@9l=!FkJSmh zdhl<=XYnHO<8eca`+&0rXfc5s(n5pC(1(EQq7ze|YuX7S?iG$HxeE<)7eu4x&x?;d zX`hKapCw+D(@osCh8nl?%H7q58aNJ91D3p`8AqljV~ii6vwxtIkI*ZBWqR-g-g@rT J4&sV9{txJF%dh|d literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..0c7b59936bdde7d8b4581ba1814feb3bfaef414d GIT binary patch literal 11220 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 IdoZE;e^a3Zd;kCd literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/format_control.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fe6b53099d867629c67332d2c4051545606357e GIT binary patch literal 4248 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..644863b37af779de7459c95ba55545d4da39f9f6 GIT binary patch literal 2293 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 W8;0>an)?%N{$sBhn=cWVH0@uA=@a1q literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/models/__pycache__/target_python.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05e42de697038a50aa5b4bb7ea2f7decf8cb35e0 GIT binary patch literal 4975 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

DGS zA#x==$CX36-3w{j1)?B`Wb8zWLEvz@!`W_Fp)-O_F0kmP7@@OWZWNUjhJ*sHmdNVH z10s3|=36o}pSmF(8Gb{(p)71(+PtK`YrJE;H}K5Qxk6<4GYbp9?`?gU`)U2dTvUXV zuT9%dKr<$?a7D5;r}4CH=Gc7+oz;=5hQG-(O(`N8gQAzqFtGV^8qP*GP<;=6+mCue z{%aQkJ8ri`ijm=8>Wktp%=aV1#g^ga;P9uno44H|9B{X6`(gKTaC=FBmwT@6+hnp3 z>KB}52#bVmCUS5uq`EzmtOP!YM{6>uI0%E+_}XM}iFbkGKS>Bm9uB=G_`Nl~c)!ny zK{E&fxRq|;r4^sUiM7xGciyiK3G##md51+CLylUhwT6+iN-hU8yAhTEB@nqNgd8)Q zju&kpW=#skjTeSB0eB{jWu*m+L3BbGvr3MoJr`=KZi!qADlIvwVAbk{JYL|HNmExV z`Y8tBS57l5z&n6DLsO`1C4mabRFN|Z9YeC4T<^oa{}zF&VP2I`YokF}xNIglMNNir zsQCJWh7$#S%66=52cgWhDVPcEScS71J9@=@HJ<*zK!#CU17vUmIS;ql%RGZC|7CuT z!+#RWptWfoXx$`Hf#DVHfhqJG&{!B;>(Q!Qg`@zc5*QBA-dy`eLgp1pDSuR-}^0jOov3PiZ8601uDKtUHsg(@c1D)usiQ75v2 zHeP|U0s9bYwhJ}8K<@4&n_AYGZryv{K5_E|6r(K?5H?{^8;~g69^n!RjrX8w@K-KF z0cSi;P5;GzL<9+xq1^2E%a8+c3z>`7Bv$Y=z>ORf6!5Q>n@DT0#w{fN=b;njnav$Z zx8Q%7aT9Jo^hwxLZC&aFD*1J`d0i6F)aP;THfR9*B@$|#A9{UgrKP*j(w#q3Z0TKY z?p^lxGJ8ftc!R>*Xd>ZA3GgR%0`8azCzwclcS6aOIrWKzY9@iq!|k&k4FUy@Q~%u2 zQ-_{CeU#!|i#~=;CpP%E4#foIUMB@qe?PwBVNpDDifO|UM@=(BkK+45Z18~Htc-v5 zP;Bu31?x>{E|L$#tsjQAUq3W|{Pp7{o)7GRD_Ef6nsixO3;4oy7lunsA)mC+QXmxGoN!>|~>$^j+WrF? None: + self.name = name + self.version = parse_version(version) + self.link = link + + super().__init__( + key=(self.name, self.version, self.link), + defining_class=InstallationCandidate, + ) + + def __repr__(self) -> str: + return "".format( + self.name, + self.version, + self.link, + ) + + def __str__(self) -> str: + return f"{self.name!r} candidate (version {self.version} at {self.link})" diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py new file mode 100644 index 0000000..0af884b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/direct_url.py @@ -0,0 +1,235 @@ +""" PEP 610 """ +import json +import re +import urllib.parse +from typing import Any, Dict, Iterable, Optional, Type, TypeVar, Union + +__all__ = [ + "DirectUrl", + "DirectUrlValidationError", + "DirInfo", + "ArchiveInfo", + "VcsInfo", +] + +T = TypeVar("T") + +DIRECT_URL_METADATA_NAME = "direct_url.json" +ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") + + +class DirectUrlValidationError(Exception): + pass + + +def _get( + d: Dict[str, Any], expected_type: Type[T], key: str, default: Optional[T] = None +) -> Optional[T]: + """Get value from dictionary and verify expected type.""" + if key not in d: + return default + value = d[key] + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + f"{value!r} has unexpected type for {key} (expected {expected_type})" + ) + return value + + +def _get_required( + d: Dict[str, Any], expected_type: Type[T], key: str, default: Optional[T] = None +) -> T: + value = _get(d, expected_type, key, default) + if value is None: + raise DirectUrlValidationError(f"{key} must have a value") + return value + + +def _exactly_one_of(infos: Iterable[Optional["InfoType"]]) -> "InfoType": + infos = [info for info in infos if info is not None] + if not infos: + raise DirectUrlValidationError( + "missing one of archive_info, dir_info, vcs_info" + ) + if len(infos) > 1: + raise DirectUrlValidationError( + "more than one of archive_info, dir_info, vcs_info" + ) + assert infos[0] is not None + return infos[0] + + +def _filter_none(**kwargs: Any) -> Dict[str, Any]: + """Make dict excluding None values.""" + return {k: v for k, v in kwargs.items() if v is not None} + + +class VcsInfo: + name = "vcs_info" + + def __init__( + self, + vcs: str, + commit_id: str, + requested_revision: Optional[str] = None, + ) -> None: + self.vcs = vcs + self.requested_revision = requested_revision + self.commit_id = commit_id + + @classmethod + def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["VcsInfo"]: + if d is None: + return None + return cls( + vcs=_get_required(d, str, "vcs"), + commit_id=_get_required(d, str, "commit_id"), + requested_revision=_get(d, str, "requested_revision"), + ) + + def _to_dict(self) -> Dict[str, Any]: + return _filter_none( + vcs=self.vcs, + requested_revision=self.requested_revision, + commit_id=self.commit_id, + ) + + +class ArchiveInfo: + name = "archive_info" + + def __init__( + self, + hash: Optional[str] = None, + hashes: Optional[Dict[str, str]] = None, + ) -> None: + # set hashes before hash, since the hash setter will further populate hashes + self.hashes = hashes + self.hash = hash + + @property + def hash(self) -> Optional[str]: + return self._hash + + @hash.setter + def hash(self, value: Optional[str]) -> None: + if value is not None: + # Auto-populate the hashes key to upgrade to the new format automatically. + # We don't back-populate the legacy hash key from hashes. + try: + hash_name, hash_value = value.split("=", 1) + except ValueError: + raise DirectUrlValidationError( + f"invalid archive_info.hash format: {value!r}" + ) + if self.hashes is None: + self.hashes = {hash_name: hash_value} + elif hash_name not in self.hashes: + self.hashes = self.hashes.copy() + self.hashes[hash_name] = hash_value + self._hash = value + + @classmethod + def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["ArchiveInfo"]: + if d is None: + return None + return cls(hash=_get(d, str, "hash"), hashes=_get(d, dict, "hashes")) + + def _to_dict(self) -> Dict[str, Any]: + return _filter_none(hash=self.hash, hashes=self.hashes) + + +class DirInfo: + name = "dir_info" + + def __init__( + self, + editable: bool = False, + ) -> None: + self.editable = editable + + @classmethod + def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["DirInfo"]: + if d is None: + return None + return cls(editable=_get_required(d, bool, "editable", default=False)) + + def _to_dict(self) -> Dict[str, Any]: + return _filter_none(editable=self.editable or None) + + +InfoType = Union[ArchiveInfo, DirInfo, VcsInfo] + + +class DirectUrl: + def __init__( + self, + url: str, + info: InfoType, + subdirectory: Optional[str] = None, + ) -> None: + self.url = url + self.info = info + self.subdirectory = subdirectory + + def _remove_auth_from_netloc(self, netloc: str) -> str: + if "@" not in netloc: + return netloc + user_pass, netloc_no_user_pass = netloc.split("@", 1) + if ( + isinstance(self.info, VcsInfo) + and self.info.vcs == "git" + and user_pass == "git" + ): + return netloc + if ENV_VAR_RE.match(user_pass): + return netloc + return netloc_no_user_pass + + @property + def redacted_url(self) -> str: + """url with user:password part removed unless it is formed with + environment variables as specified in PEP 610, or it is ``git`` + in the case of a git URL. + """ + purl = urllib.parse.urlsplit(self.url) + netloc = self._remove_auth_from_netloc(purl.netloc) + surl = urllib.parse.urlunsplit( + (purl.scheme, netloc, purl.path, purl.query, purl.fragment) + ) + return surl + + def validate(self) -> None: + self.from_dict(self.to_dict()) + + @classmethod + def from_dict(cls, d: Dict[str, Any]) -> "DirectUrl": + return DirectUrl( + url=_get_required(d, str, "url"), + subdirectory=_get(d, str, "subdirectory"), + info=_exactly_one_of( + [ + ArchiveInfo._from_dict(_get(d, dict, "archive_info")), + DirInfo._from_dict(_get(d, dict, "dir_info")), + VcsInfo._from_dict(_get(d, dict, "vcs_info")), + ] + ), + ) + + def to_dict(self) -> Dict[str, Any]: + res = _filter_none( + url=self.redacted_url, + subdirectory=self.subdirectory, + ) + res[self.info.name] = self.info._to_dict() + return res + + @classmethod + def from_json(cls, s: str) -> "DirectUrl": + return cls.from_dict(json.loads(s)) + + def to_json(self) -> str: + return json.dumps(self.to_dict(), sort_keys=True) + + def is_local_editable(self) -> bool: + return isinstance(self.info, DirInfo) and self.info.editable diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000..ccd1127 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,78 @@ +from typing import FrozenSet, Optional, Set + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import CommandError + + +class FormatControl: + """Helper for managing formats from which a package can be installed.""" + + __slots__ = ["no_binary", "only_binary"] + + def __init__( + self, + no_binary: Optional[Set[str]] = None, + only_binary: Optional[Set[str]] = None, + ) -> None: + if no_binary is None: + no_binary = set() + if only_binary is None: + only_binary = set() + + self.no_binary = no_binary + self.only_binary = only_binary + + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + + if self.__slots__ != other.__slots__: + return False + + return all(getattr(self, k) == getattr(other, k) for k in self.__slots__) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.no_binary}, {self.only_binary})" + + @staticmethod + def handle_mutual_excludes(value: str, target: Set[str], other: Set[str]) -> None: + if value.startswith("-"): + raise CommandError( + "--no-binary / --only-binary option requires 1 argument." + ) + new = value.split(",") + while ":all:" in new: + other.clear() + target.clear() + target.add(":all:") + del new[: new.index(":all:") + 1] + # Without a none, we want to discard everything as :all: covers it + if ":none:" not in new: + return + for name in new: + if name == ":none:": + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + def get_allowed_formats(self, canonical_name: str) -> FrozenSet[str]: + result = {"binary", "source"} + if canonical_name in self.only_binary: + result.discard("source") + elif canonical_name in self.no_binary: + result.discard("binary") + elif ":all:" in self.only_binary: + result.discard("source") + elif ":all:" in self.no_binary: + result.discard("binary") + return frozenset(result) + + def disallow_binaries(self) -> None: + self.handle_mutual_excludes( + ":all:", + self.no_binary, + self.only_binary, + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/index.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000..b94c325 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/index.py @@ -0,0 +1,28 @@ +import urllib.parse + + +class PackageIndex: + """Represents a Package Index and provides easier access to endpoints""" + + __slots__ = ["url", "netloc", "simple_url", "pypi_url", "file_storage_domain"] + + def __init__(self, url: str, file_storage_domain: str) -> None: + super().__init__() + self.url = url + self.netloc = urllib.parse.urlsplit(url).netloc + self.simple_url = self._url_for_path("simple") + self.pypi_url = self._url_for_path("pypi") + + # This is part of a temporary hack used to block installs of PyPI + # packages which depend on external urls only necessary until PyPI can + # block such packages themselves + self.file_storage_domain = file_storage_domain + + def _url_for_path(self, path: str) -> str: + return urllib.parse.urljoin(self.url, path) + + +PyPI = PackageIndex("https://pypi.org/", file_storage_domain="files.pythonhosted.org") +TestPyPI = PackageIndex( + "https://test.pypi.org/", file_storage_domain="test-files.pythonhosted.org" +) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py new file mode 100644 index 0000000..b9c6330 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/installation_report.py @@ -0,0 +1,56 @@ +from typing import Any, Dict, Sequence + +from pip._vendor.packaging.markers import default_environment + +from pip import __version__ +from pip._internal.req.req_install import InstallRequirement + + +class InstallationReport: + def __init__(self, install_requirements: Sequence[InstallRequirement]): + self._install_requirements = install_requirements + + @classmethod + def _install_req_to_dict(cls, ireq: InstallRequirement) -> Dict[str, Any]: + assert ireq.download_info, f"No download_info for {ireq}" + res = { + # PEP 610 json for the download URL. download_info.archive_info.hashes may + # be absent when the requirement was installed from the wheel cache + # and the cache entry was populated by an older pip version that did not + # record origin.json. + "download_info": ireq.download_info.to_dict(), + # is_direct is true if the requirement was a direct URL reference (which + # includes editable requirements), and false if the requirement was + # downloaded from a PEP 503 index or --find-links. + "is_direct": ireq.is_direct, + # is_yanked is true if the requirement was yanked from the index, but + # was still selected by pip to conform to PEP 592. + "is_yanked": ireq.link.is_yanked if ireq.link else False, + # requested is true if the requirement was specified by the user (aka + # top level requirement), and false if it was installed as a dependency of a + # requirement. https://peps.python.org/pep-0376/#requested + "requested": ireq.user_supplied, + # PEP 566 json encoding for metadata + # https://www.python.org/dev/peps/pep-0566/#json-compatible-metadata + "metadata": ireq.get_dist().metadata_dict, + } + if ireq.user_supplied and ireq.extras: + # For top level requirements, the list of requested extras, if any. + res["requested_extras"] = sorted(ireq.extras) + return res + + def to_dict(self) -> Dict[str, Any]: + return { + "version": "1", + "pip_version": __version__, + "install": [ + self._install_req_to_dict(ireq) for ireq in self._install_requirements + ], + # https://peps.python.org/pep-0508/#environment-markers + # TODO: currently, the resolver uses the default environment to evaluate + # environment markers, so that is what we report here. In the future, it + # should also take into account options such as --python-version or + # --platform, perhaps under the form of an environment_override field? + # https://github.com/pypa/pip/issues/11198 + "environment": default_environment(), + } diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/link.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000..73041b8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/link.py @@ -0,0 +1,579 @@ +import functools +import itertools +import logging +import os +import posixpath +import re +import urllib.parse +from dataclasses import dataclass +from typing import ( + TYPE_CHECKING, + Any, + Dict, + List, + Mapping, + NamedTuple, + Optional, + Tuple, + Union, +) + +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.misc import ( + pairwise, + redact_auth_from_url, + split_auth_from_netloc, + splitext, +) +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.urls import path_to_url, url_to_path + +if TYPE_CHECKING: + from pip._internal.index.collector import IndexContent + +logger = logging.getLogger(__name__) + + +# Order matters, earlier hashes have a precedence over later hashes for what +# we will pick to use. +_SUPPORTED_HASHES = ("sha512", "sha384", "sha256", "sha224", "sha1", "md5") + + +@dataclass(frozen=True) +class LinkHash: + """Links to content may have embedded hash values. This class parses those. + + `name` must be any member of `_SUPPORTED_HASHES`. + + This class can be converted to and from `ArchiveInfo`. While ArchiveInfo intends to + be JSON-serializable to conform to PEP 610, this class contains the logic for + parsing a hash name and value for correctness, and then checking whether that hash + conforms to a schema with `.is_hash_allowed()`.""" + + name: str + value: str + + _hash_url_fragment_re = re.compile( + # NB: we do not validate that the second group (.*) is a valid hex + # digest. Instead, we simply keep that string in this class, and then check it + # against Hashes when hash-checking is needed. This is easier to debug than + # proactively discarding an invalid hex digest, as we handle incorrect hashes + # and malformed hashes in the same place. + r"[#&]({choices})=([^&]*)".format( + choices="|".join(re.escape(hash_name) for hash_name in _SUPPORTED_HASHES) + ), + ) + + def __post_init__(self) -> None: + assert self.name in _SUPPORTED_HASHES + + @classmethod + @functools.lru_cache(maxsize=None) + def find_hash_url_fragment(cls, url: str) -> Optional["LinkHash"]: + """Search a string for a checksum algorithm name and encoded output value.""" + match = cls._hash_url_fragment_re.search(url) + if match is None: + return None + name, value = match.groups() + return cls(name=name, value=value) + + def as_dict(self) -> Dict[str, str]: + return {self.name: self.value} + + def as_hashes(self) -> Hashes: + """Return a Hashes instance which checks only for the current hash.""" + return Hashes({self.name: [self.value]}) + + def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: + """ + Return True if the current hash is allowed by `hashes`. + """ + if hashes is None: + return False + return hashes.is_hash_allowed(self.name, hex_digest=self.value) + + +@dataclass(frozen=True) +class MetadataFile: + """Information about a core metadata file associated with a distribution.""" + + hashes: Optional[Dict[str, str]] + + def __post_init__(self) -> None: + if self.hashes is not None: + assert all(name in _SUPPORTED_HASHES for name in self.hashes) + + +def supported_hashes(hashes: Optional[Dict[str, str]]) -> Optional[Dict[str, str]]: + # Remove any unsupported hash types from the mapping. If this leaves no + # supported hashes, return None + if hashes is None: + return None + hashes = {n: v for n, v in hashes.items() if n in _SUPPORTED_HASHES} + if not hashes: + return None + return hashes + + +def _clean_url_path_part(part: str) -> str: + """ + Clean a "part" of a URL path (i.e. after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + return urllib.parse.quote(urllib.parse.unquote(part)) + + +def _clean_file_url_path(part: str) -> str: + """ + Clean the first part of a URL path that corresponds to a local + filesystem path (i.e. the first part after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + # Also, on Windows the path part might contain a drive letter which + # should not be quoted. On Linux where drive letters do not + # exist, the colon should be quoted. We rely on urllib.request + # to do the right thing here. + return urllib.request.pathname2url(urllib.request.url2pathname(part)) + + +# percent-encoded: / +_reserved_chars_re = re.compile("(@|%2F)", re.IGNORECASE) + + +def _clean_url_path(path: str, is_local_path: bool) -> str: + """ + Clean the path portion of a URL. + """ + if is_local_path: + clean_func = _clean_file_url_path + else: + clean_func = _clean_url_path_part + + # Split on the reserved characters prior to cleaning so that + # revision strings in VCS URLs are properly preserved. + parts = _reserved_chars_re.split(path) + + cleaned_parts = [] + for to_clean, reserved in pairwise(itertools.chain(parts, [""])): + cleaned_parts.append(clean_func(to_clean)) + # Normalize %xx escapes (e.g. %2f -> %2F) + cleaned_parts.append(reserved.upper()) + + return "".join(cleaned_parts) + + +def _ensure_quoted_url(url: str) -> str: + """ + Make sure a link is fully quoted. + For example, if ' ' occurs in the URL, it will be replaced with "%20", + and without double-quoting other characters. + """ + # Split the URL into parts according to the general structure + # `scheme://netloc/path;parameters?query#fragment`. + result = urllib.parse.urlparse(url) + # If the netloc is empty, then the URL refers to a local filesystem path. + is_local_path = not result.netloc + path = _clean_url_path(result.path, is_local_path=is_local_path) + return urllib.parse.urlunparse(result._replace(path=path)) + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL""" + + __slots__ = [ + "_parsed_url", + "_url", + "_hashes", + "comes_from", + "requires_python", + "yanked_reason", + "metadata_file_data", + "cache_link_parsing", + "egg_fragment", + ] + + def __init__( + self, + url: str, + comes_from: Optional[Union[str, "IndexContent"]] = None, + requires_python: Optional[str] = None, + yanked_reason: Optional[str] = None, + metadata_file_data: Optional[MetadataFile] = None, + cache_link_parsing: bool = True, + hashes: Optional[Mapping[str, str]] = None, + ) -> None: + """ + :param url: url of the resource pointed to (href of the link) + :param comes_from: instance of IndexContent where the link was found, + or string. + :param requires_python: String containing the `Requires-Python` + metadata field, specified in PEP 345. This may be specified by + a data-requires-python attribute in the HTML link tag, as + described in PEP 503. + :param yanked_reason: the reason the file has been yanked, if the + file has been yanked, or None if the file hasn't been yanked. + This is the value of the "data-yanked" attribute, if present, in + a simple repository HTML link. If the file has been yanked but + no reason was provided, this should be the empty string. See + PEP 592 for more information and the specification. + :param metadata_file_data: the metadata attached to the file, or None if + no such metadata is provided. This argument, if not None, indicates + that a separate metadata file exists, and also optionally supplies + hashes for that file. + :param cache_link_parsing: A flag that is used elsewhere to determine + whether resources retrieved from this link should be cached. PyPI + URLs should generally have this set to False, for example. + :param hashes: A mapping of hash names to digests to allow us to + determine the validity of a download. + """ + + # The comes_from, requires_python, and metadata_file_data arguments are + # only used by classmethods of this class, and are not used in client + # code directly. + + # url can be a UNC windows share + if url.startswith("\\\\"): + url = path_to_url(url) + + self._parsed_url = urllib.parse.urlsplit(url) + # Store the url as a private attribute to prevent accidentally + # trying to set a new value. + self._url = url + + link_hash = LinkHash.find_hash_url_fragment(url) + hashes_from_link = {} if link_hash is None else link_hash.as_dict() + if hashes is None: + self._hashes = hashes_from_link + else: + self._hashes = {**hashes, **hashes_from_link} + + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + self.yanked_reason = yanked_reason + self.metadata_file_data = metadata_file_data + + super().__init__(key=url, defining_class=Link) + + self.cache_link_parsing = cache_link_parsing + self.egg_fragment = self._egg_fragment() + + @classmethod + def from_json( + cls, + file_data: Dict[str, Any], + page_url: str, + ) -> Optional["Link"]: + """ + Convert an pypi json document from a simple repository page into a Link. + """ + file_url = file_data.get("url") + if file_url is None: + return None + + url = _ensure_quoted_url(urllib.parse.urljoin(page_url, file_url)) + pyrequire = file_data.get("requires-python") + yanked_reason = file_data.get("yanked") + hashes = file_data.get("hashes", {}) + + # PEP 714: Indexes must use the name core-metadata, but + # clients should support the old name as a fallback for compatibility. + metadata_info = file_data.get("core-metadata") + if metadata_info is None: + metadata_info = file_data.get("dist-info-metadata") + + # The metadata info value may be a boolean, or a dict of hashes. + if isinstance(metadata_info, dict): + # The file exists, and hashes have been supplied + metadata_file_data = MetadataFile(supported_hashes(metadata_info)) + elif metadata_info: + # The file exists, but there are no hashes + metadata_file_data = MetadataFile(None) + else: + # False or not present: the file does not exist + metadata_file_data = None + + # The Link.yanked_reason expects an empty string instead of a boolean. + if yanked_reason and not isinstance(yanked_reason, str): + yanked_reason = "" + # The Link.yanked_reason expects None instead of False. + elif not yanked_reason: + yanked_reason = None + + return cls( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + hashes=hashes, + metadata_file_data=metadata_file_data, + ) + + @classmethod + def from_element( + cls, + anchor_attribs: Dict[str, Optional[str]], + page_url: str, + base_url: str, + ) -> Optional["Link"]: + """ + Convert an anchor element's attributes in a simple repository page to a Link. + """ + href = anchor_attribs.get("href") + if not href: + return None + + url = _ensure_quoted_url(urllib.parse.urljoin(base_url, href)) + pyrequire = anchor_attribs.get("data-requires-python") + yanked_reason = anchor_attribs.get("data-yanked") + + # PEP 714: Indexes must use the name data-core-metadata, but + # clients should support the old name as a fallback for compatibility. + metadata_info = anchor_attribs.get("data-core-metadata") + if metadata_info is None: + metadata_info = anchor_attribs.get("data-dist-info-metadata") + # The metadata info value may be the string "true", or a string of + # the form "hashname=hashval" + if metadata_info == "true": + # The file exists, but there are no hashes + metadata_file_data = MetadataFile(None) + elif metadata_info is None: + # The file does not exist + metadata_file_data = None + else: + # The file exists, and hashes have been supplied + hashname, sep, hashval = metadata_info.partition("=") + if sep == "=": + metadata_file_data = MetadataFile(supported_hashes({hashname: hashval})) + else: + # Error - data is wrong. Treat as no hashes supplied. + logger.debug( + "Index returned invalid data-dist-info-metadata value: %s", + metadata_info, + ) + metadata_file_data = MetadataFile(None) + + return cls( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + metadata_file_data=metadata_file_data, + ) + + def __str__(self) -> str: + if self.requires_python: + rp = f" (requires-python:{self.requires_python})" + else: + rp = "" + if self.comes_from: + return f"{redact_auth_from_url(self._url)} (from {self.comes_from}){rp}" + else: + return redact_auth_from_url(str(self._url)) + + def __repr__(self) -> str: + return f"" + + @property + def url(self) -> str: + return self._url + + @property + def filename(self) -> str: + path = self.path.rstrip("/") + name = posixpath.basename(path) + if not name: + # Make sure we don't leak auth information if the netloc + # includes a username and password. + netloc, user_pass = split_auth_from_netloc(self.netloc) + return netloc + + name = urllib.parse.unquote(name) + assert name, f"URL {self._url!r} produced no filename" + return name + + @property + def file_path(self) -> str: + return url_to_path(self.url) + + @property + def scheme(self) -> str: + return self._parsed_url.scheme + + @property + def netloc(self) -> str: + """ + This can contain auth information. + """ + return self._parsed_url.netloc + + @property + def path(self) -> str: + return urllib.parse.unquote(self._parsed_url.path) + + def splitext(self) -> Tuple[str, str]: + return splitext(posixpath.basename(self.path.rstrip("/"))) + + @property + def ext(self) -> str: + return self.splitext()[1] + + @property + def url_without_fragment(self) -> str: + scheme, netloc, path, query, fragment = self._parsed_url + return urllib.parse.urlunsplit((scheme, netloc, path, query, "")) + + _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") + + # Per PEP 508. + _project_name_re = re.compile( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE + ) + + def _egg_fragment(self) -> Optional[str]: + match = self._egg_fragment_re.search(self._url) + if not match: + return None + + # An egg fragment looks like a PEP 508 project name, along with + # an optional extras specifier. Anything else is invalid. + project_name = match.group(1) + if not self._project_name_re.match(project_name): + deprecated( + reason=f"{self} contains an egg fragment with a non-PEP 508 name", + replacement="to use the req @ url syntax, and remove the egg fragment", + gone_in="25.0", + issue=11617, + ) + + return project_name + + _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") + + @property + def subdirectory_fragment(self) -> Optional[str]: + match = self._subdirectory_fragment_re.search(self._url) + if not match: + return None + return match.group(1) + + def metadata_link(self) -> Optional["Link"]: + """Return a link to the associated core metadata file (if any).""" + if self.metadata_file_data is None: + return None + metadata_url = f"{self.url_without_fragment}.metadata" + if self.metadata_file_data.hashes is None: + return Link(metadata_url) + return Link(metadata_url, hashes=self.metadata_file_data.hashes) + + def as_hashes(self) -> Hashes: + return Hashes({k: [v] for k, v in self._hashes.items()}) + + @property + def hash(self) -> Optional[str]: + return next(iter(self._hashes.values()), None) + + @property + def hash_name(self) -> Optional[str]: + return next(iter(self._hashes), None) + + @property + def show_url(self) -> str: + return posixpath.basename(self._url.split("#", 1)[0].split("?", 1)[0]) + + @property + def is_file(self) -> bool: + return self.scheme == "file" + + def is_existing_dir(self) -> bool: + return self.is_file and os.path.isdir(self.file_path) + + @property + def is_wheel(self) -> bool: + return self.ext == WHEEL_EXTENSION + + @property + def is_vcs(self) -> bool: + from pip._internal.vcs import vcs + + return self.scheme in vcs.all_schemes + + @property + def is_yanked(self) -> bool: + return self.yanked_reason is not None + + @property + def has_hash(self) -> bool: + return bool(self._hashes) + + def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: + """ + Return True if the link has a hash and it is allowed by `hashes`. + """ + if hashes is None: + return False + return any(hashes.is_hash_allowed(k, v) for k, v in self._hashes.items()) + + +class _CleanResult(NamedTuple): + """Convert link for equivalency check. + + This is used in the resolver to check whether two URL-specified requirements + likely point to the same distribution and can be considered equivalent. This + equivalency logic avoids comparing URLs literally, which can be too strict + (e.g. "a=1&b=2" vs "b=2&a=1") and produce conflicts unexpecting to users. + + Currently this does three things: + + 1. Drop the basic auth part. This is technically wrong since a server can + serve different content based on auth, but if it does that, it is even + impossible to guarantee two URLs without auth are equivalent, since + the user can input different auth information when prompted. So the + practical solution is to assume the auth doesn't affect the response. + 2. Parse the query to avoid the ordering issue. Note that ordering under the + same key in the query are NOT cleaned; i.e. "a=1&a=2" and "a=2&a=1" are + still considered different. + 3. Explicitly drop most of the fragment part, except ``subdirectory=`` and + hash values, since it should have no impact the downloaded content. Note + that this drops the "egg=" part historically used to denote the requested + project (and extras), which is wrong in the strictest sense, but too many + people are supplying it inconsistently to cause superfluous resolution + conflicts, so we choose to also ignore them. + """ + + parsed: urllib.parse.SplitResult + query: Dict[str, List[str]] + subdirectory: str + hashes: Dict[str, str] + + +def _clean_link(link: Link) -> _CleanResult: + parsed = link._parsed_url + netloc = parsed.netloc.rsplit("@", 1)[-1] + # According to RFC 8089, an empty host in file: means localhost. + if parsed.scheme == "file" and not netloc: + netloc = "localhost" + fragment = urllib.parse.parse_qs(parsed.fragment) + if "egg" in fragment: + logger.debug("Ignoring egg= fragment in %s", link) + try: + # If there are multiple subdirectory values, use the first one. + # This matches the behavior of Link.subdirectory_fragment. + subdirectory = fragment["subdirectory"][0] + except (IndexError, KeyError): + subdirectory = "" + # If there are multiple hash values under the same algorithm, use the + # first one. This matches the behavior of Link.hash_value. + hashes = {k: fragment[k][0] for k in _SUPPORTED_HASHES if k in fragment} + return _CleanResult( + parsed=parsed._replace(netloc=netloc, query="", fragment=""), + query=urllib.parse.parse_qs(parsed.query), + subdirectory=subdirectory, + hashes=hashes, + ) + + +@functools.lru_cache(maxsize=None) +def links_equivalent(link1: Link, link2: Link) -> bool: + return _clean_link(link1) == _clean_link(link2) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py new file mode 100644 index 0000000..f51190a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/scheme.py @@ -0,0 +1,31 @@ +""" +For types associated with installation schemes. + +For a general overview of available schemes and their context, see +https://docs.python.org/3/install/index.html#alternate-installation. +""" + + +SCHEME_KEYS = ["platlib", "purelib", "headers", "scripts", "data"] + + +class Scheme: + """A Scheme holds paths which are used as the base directories for + artifacts associated with a Python package. + """ + + __slots__ = SCHEME_KEYS + + def __init__( + self, + platlib: str, + purelib: str, + headers: str, + scripts: str, + data: str, + ) -> None: + self.platlib = platlib + self.purelib = purelib + self.headers = headers + self.scripts = scripts + self.data = data diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py new file mode 100644 index 0000000..fe61e81 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/search_scope.py @@ -0,0 +1,132 @@ +import itertools +import logging +import os +import posixpath +import urllib.parse +from typing import List + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import has_tls +from pip._internal.utils.misc import normalize_path, redact_auth_from_url + +logger = logging.getLogger(__name__) + + +class SearchScope: + + """ + Encapsulates the locations that pip is configured to search. + """ + + __slots__ = ["find_links", "index_urls", "no_index"] + + @classmethod + def create( + cls, + find_links: List[str], + index_urls: List[str], + no_index: bool, + ) -> "SearchScope": + """ + Create a SearchScope object after normalizing the `find_links`. + """ + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + built_find_links: List[str] = [] + for link in find_links: + if link.startswith("~"): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + built_find_links.append(link) + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not has_tls(): + for link in itertools.chain(index_urls, built_find_links): + parsed = urllib.parse.urlparse(link) + if parsed.scheme == "https": + logger.warning( + "pip is configured with locations that require " + "TLS/SSL, however the ssl module in Python is not " + "available." + ) + break + + return cls( + find_links=built_find_links, + index_urls=index_urls, + no_index=no_index, + ) + + def __init__( + self, + find_links: List[str], + index_urls: List[str], + no_index: bool, + ) -> None: + self.find_links = find_links + self.index_urls = index_urls + self.no_index = no_index + + def get_formatted_locations(self) -> str: + lines = [] + redacted_index_urls = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + for url in self.index_urls: + redacted_index_url = redact_auth_from_url(url) + + # Parse the URL + purl = urllib.parse.urlsplit(redacted_index_url) + + # URL is generally invalid if scheme and netloc is missing + # there are issues with Python and URL parsing, so this test + # is a bit crude. See bpo-20271, bpo-23505. Python doesn't + # always parse invalid URLs correctly - it should raise + # exceptions for malformed URLs + if not purl.scheme and not purl.netloc: + logger.warning( + 'The index url "%s" seems invalid, please provide a scheme.', + redacted_index_url, + ) + + redacted_index_urls.append(redacted_index_url) + + lines.append( + "Looking in indexes: {}".format(", ".join(redacted_index_urls)) + ) + + if self.find_links: + lines.append( + "Looking in links: {}".format( + ", ".join(redact_auth_from_url(url) for url in self.find_links) + ) + ) + return "\n".join(lines) + + def get_index_urls_locations(self, project_name: str) -> List[str]: + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url: str) -> str: + loc = posixpath.join( + url, urllib.parse.quote(canonicalize_name(project_name)) + ) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith("/"): + loc = loc + "/" + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py new file mode 100644 index 0000000..977bc4c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/selection_prefs.py @@ -0,0 +1,51 @@ +from typing import Optional + +from pip._internal.models.format_control import FormatControl + + +class SelectionPreferences: + """ + Encapsulates the candidate selection preferences for downloading + and installing files. + """ + + __slots__ = [ + "allow_yanked", + "allow_all_prereleases", + "format_control", + "prefer_binary", + "ignore_requires_python", + ] + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + allow_yanked: bool, + allow_all_prereleases: bool = False, + format_control: Optional[FormatControl] = None, + prefer_binary: bool = False, + ignore_requires_python: Optional[bool] = None, + ) -> None: + """Create a SelectionPreferences object. + + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + :param prefer_binary: Whether to prefer an old, but valid, binary + dist over a new source dist. + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self.allow_yanked = allow_yanked + self.allow_all_prereleases = allow_all_prereleases + self.format_control = format_control + self.prefer_binary = prefer_binary + self.ignore_requires_python = ignore_requires_python diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py new file mode 100644 index 0000000..67ea5da --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/target_python.py @@ -0,0 +1,122 @@ +import sys +from typing import List, Optional, Set, Tuple + +from pip._vendor.packaging.tags import Tag + +from pip._internal.utils.compatibility_tags import get_supported, version_info_to_nodot +from pip._internal.utils.misc import normalize_version_info + + +class TargetPython: + + """ + Encapsulates the properties of a Python interpreter one is targeting + for a package install, download, etc. + """ + + __slots__ = [ + "_given_py_version_info", + "abis", + "implementation", + "platforms", + "py_version", + "py_version_info", + "_valid_tags", + "_valid_tags_set", + ] + + def __init__( + self, + platforms: Optional[List[str]] = None, + py_version_info: Optional[Tuple[int, ...]] = None, + abis: Optional[List[str]] = None, + implementation: Optional[str] = None, + ) -> None: + """ + :param platforms: A list of strings or None. If None, searches for + packages that are supported by the current system. Otherwise, will + find packages that can be built on the platforms passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param py_version_info: An optional tuple of ints representing the + Python version information to use (e.g. `sys.version_info[:3]`). + This can have length 1, 2, or 3 when provided. + :param abis: A list of strings or None. This is passed to + compatibility_tags.py's get_supported() function as is. + :param implementation: A string or None. This is passed to + compatibility_tags.py's get_supported() function as is. + """ + # Store the given py_version_info for when we call get_supported(). + self._given_py_version_info = py_version_info + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + py_version = ".".join(map(str, py_version_info[:2])) + + self.abis = abis + self.implementation = implementation + self.platforms = platforms + self.py_version = py_version + self.py_version_info = py_version_info + + # This is used to cache the return value of get_(un)sorted_tags. + self._valid_tags: Optional[List[Tag]] = None + self._valid_tags_set: Optional[Set[Tag]] = None + + def format_given(self) -> str: + """ + Format the given, non-None attributes for display. + """ + display_version = None + if self._given_py_version_info is not None: + display_version = ".".join( + str(part) for part in self._given_py_version_info + ) + + key_values = [ + ("platforms", self.platforms), + ("version_info", display_version), + ("abis", self.abis), + ("implementation", self.implementation), + ] + return " ".join( + f"{key}={value!r}" for key, value in key_values if value is not None + ) + + def get_sorted_tags(self) -> List[Tag]: + """ + Return the supported PEP 425 tags to check wheel candidates against. + + The tags are returned in order of preference (most preferred first). + """ + if self._valid_tags is None: + # Pass versions=None if no py_version_info was given since + # versions=None uses special default logic. + py_version_info = self._given_py_version_info + if py_version_info is None: + version = None + else: + version = version_info_to_nodot(py_version_info) + + tags = get_supported( + version=version, + platforms=self.platforms, + abis=self.abis, + impl=self.implementation, + ) + self._valid_tags = tags + + return self._valid_tags + + def get_unsorted_tags(self) -> Set[Tag]: + """Exactly the same as get_sorted_tags, but returns a set. + + This is important for performance. + """ + if self._valid_tags_set is None: + self._valid_tags_set = set(self.get_sorted_tags()) + + return self._valid_tags_set diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py new file mode 100644 index 0000000..a5dc12b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/models/wheel.py @@ -0,0 +1,92 @@ +"""Represents a wheel file and provides access to the various parts of the +name that have meaning. +""" +import re +from typing import Dict, Iterable, List + +from pip._vendor.packaging.tags import Tag + +from pip._internal.exceptions import InvalidWheelFilename + + +class Wheel: + """A wheel file""" + + wheel_file_re = re.compile( + r"""^(?P(?P[^\s-]+?)-(?P[^\s-]*?)) + ((-(?P\d[^-]*?))?-(?P[^\s-]+?)-(?P[^\s-]+?)-(?P[^\s-]+?) + \.whl|\.dist-info)$""", + re.VERBOSE, + ) + + def __init__(self, filename: str) -> None: + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename(f"{filename} is not a valid wheel filename.") + self.filename = filename + self.name = wheel_info.group("name").replace("_", "-") + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group("ver").replace("_", "-") + self.build_tag = wheel_info.group("build") + self.pyversions = wheel_info.group("pyver").split(".") + self.abis = wheel_info.group("abi").split(".") + self.plats = wheel_info.group("plat").split(".") + + # All the tag combinations from this file + self.file_tags = { + Tag(x, y, z) for x in self.pyversions for y in self.abis for z in self.plats + } + + def get_formatted_file_tags(self) -> List[str]: + """Return the wheel's tags as a sorted list of strings.""" + return sorted(str(tag) for tag in self.file_tags) + + def support_index_min(self, tags: List[Tag]) -> int: + """Return the lowest index that one of the wheel's file_tag combinations + achieves in the given list of supported tags. + + For example, if there are 8 supported tags and one of the file tags + is first in the list, then return 0. + + :param tags: the PEP 425 tags to check the wheel against, in order + with most preferred first. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + try: + return next(i for i, t in enumerate(tags) if t in self.file_tags) + except StopIteration: + raise ValueError() + + def find_most_preferred_tag( + self, tags: List[Tag], tag_to_priority: Dict[Tag, int] + ) -> int: + """Return the priority of the most preferred tag that one of the wheel's file + tag combinations achieves in the given list of supported tags using the given + tag_to_priority mapping, where lower priorities are more-preferred. + + This is used in place of support_index_min in some cases in order to avoid + an expensive linear scan of a large list of tags. + + :param tags: the PEP 425 tags to check the wheel against. + :param tag_to_priority: a mapping from tag to priority of that tag, where + lower is more preferred. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + return min( + tag_to_priority[tag] for tag in self.file_tags if tag in tag_to_priority + ) + + def supported(self, tags: Iterable[Tag]) -> bool: + """Return whether the wheel is compatible with one of the given tags. + + :param tags: the PEP 425 tags to check the wheel against. + """ + return not self.file_tags.isdisjoint(tags) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py new file mode 100644 index 0000000..b51bde9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/__init__.py @@ -0,0 +1,2 @@ +"""Contains purely network-related utilities. +""" 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 new file mode 100644 index 0000000000000000000000000000000000000000..c3eb9540a946dc5547e8f94d8ae2f93b5250df47 GIT binary patch literal 275 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/cache.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37bfa83ed2d5321120ddd4f28d13cecd22d2be95 GIT binary patch literal 6539 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6e7a6a904b6bead789bc4754dffb56906c75de8c GIT binary patch literal 8574 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d273b17c3a9223da41f5b6781ae56d82af744e53 GIT binary patch literal 11684 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ÏqHTw;uEO=v*)|}E z*BE8%4Ga-8vJk!Kv+ms~qy&r`ZY1dSz1zXeo0EO!-3Z39=7@Pz7K||`#!yV?lDySD z!&m6422KTx`bOdLo=OE{mR4{sbUkElwpN16Pvgf5kODC_lJ*=%v-N zm#?~i!}a`{+y86sz-Qd)b?)?M+=2h(Uigd~`VIHa8u!kxxo1D)F0OGGzi{!qC+q)$ pL$)bY@n`u=c$1@?h@SG5X(L}9etU~mJnopb@jZ{=`|$=5{2!_lt+fCE literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..752b671c403c83e64f320d562c5910093b86d0da GIT binary patch literal 18795 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 bbklAbIp#z|`#?*I$-t~)yh zY#9kfIXGz!a4C_}R!F_jCY5sRsY;YXrS<}X2(pS)q$+B2Gl4@C_0YEq4pBrEb)=p5 z=Kai@ee=FIzn7Og5R8rASox_Np+Aj9X_y*VY+ZqA9$i2Zl9(h)F;OPPMp?#Kvq>&x ziCR*8lsDI0(weeGZNOV3US^^KX0RRGhuJ#V13M(^380b<^O7Lhhb@xhswG;6EyIjd z1~MmhN&;qaxnu_|XZQ?TrShwM)RndST~A?+{H$&X$qB04PhkabBG2{!sYrIo(2FAKgkY%@8G$V2o4j4jw26-7TUpU)TuES~*5aC~==@NYVokS77^)NNl@}D*U_$2745XKV3(aUT(}06; z#HblRM~utYU89tyW7sJ5SV|iq6(H1%f)z#ag%%uDQ#h!_wNzXQeoE9aK!z4Ps}8AD zPH4f8Rb@mCec08nY4V7Yf}aZpCa^LQOv*#Sbe4{)N>8BsouDRD+?9?e#^WPc3#R3C zFeWP$6D6JuDm$uQ+IRse7|dLMBZK3C(z3mN-MZ;f4>;ez5$=JXftk2Ksyh;+8D%`C$vK>OwR;#{<1l<%zrZwz80a=; z*MQaQW6ZeV*G8U^m^*9fYu$C01QTIiSeB1{LXH> z7$7x<%w2+EY)46&LD~nOMq&CJJ)c2E;V@sj;W;#adG7L>r(@aEvF`L11L$CLu^V~) zTd2p-wKUymUzQXh$Vy?Dzq8|^OrdbKA0X$_qh43_L zW)C5D!-j**>h4%9t|%&W8bce4nV8i%@FYeloB8?yZ2^nSjJ^;~K(I^_e?X3a%y_Rf z1tutehWP`vETNXaQ0NJIuXqF<>{=XKs_&bz6{-t~*`C7awZ;|HSG3kKA?9Xp5gByR u0tp6MJdQfw-{jhv+I5$=Xi{IhNjG>`Au}u83@y3EWxjcv2OHvtkpBYQnkDK0 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..57123156c567eb4922646292ae0b72b16a5b2b5e GIT binary patch literal 2970 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 Optional[AuthInfo]: + ... + + @abstractmethod + def save_auth_info(self, url: str, username: str, password: str) -> None: + ... + + +class KeyRingNullProvider(KeyRingBaseProvider): + """Keyring null provider""" + + has_keyring = False + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + return None + + +class KeyRingPythonProvider(KeyRingBaseProvider): + """Keyring interface which uses locally imported `keyring`""" + + has_keyring = True + + def __init__(self) -> None: + import keyring + + self.keyring = keyring + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + # Support keyring's get_credential interface which supports getting + # credentials without a username. This is only available for + # keyring>=15.2.0. + if hasattr(self.keyring, "get_credential"): + logger.debug("Getting credentials from keyring for %s", url) + cred = self.keyring.get_credential(url, username) + if cred is not None: + return cred.username, cred.password + return None + + if username is not None: + logger.debug("Getting password from keyring for %s", url) + password = self.keyring.get_password(url, username) + if password: + return username, password + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + self.keyring.set_password(url, username, password) + + +class KeyRingCliProvider(KeyRingBaseProvider): + """Provider which uses `keyring` cli + + Instead of calling the keyring package installed alongside pip + we call keyring on the command line which will enable pip to + use which ever installation of keyring is available first in + PATH. + """ + + has_keyring = True + + def __init__(self, cmd: str) -> None: + self.keyring = cmd + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + # This is the default implementation of keyring.get_credential + # https://github.com/jaraco/keyring/blob/97689324abcf01bd1793d49063e7ca01e03d7d07/keyring/backend.py#L134-L139 + if username is not None: + password = self._get_password(url, username) + if password is not None: + return username, password + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + return self._set_password(url, username, password) + + def _get_password(self, service_name: str, username: str) -> Optional[str]: + """Mirror the implementation of keyring.get_password using cli""" + if self.keyring is None: + return None + + cmd = [self.keyring, "get", service_name, username] + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + res = subprocess.run( + cmd, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + env=env, + ) + if res.returncode: + return None + return res.stdout.decode("utf-8").strip(os.linesep) + + def _set_password(self, service_name: str, username: str, password: str) -> None: + """Mirror the implementation of keyring.set_password using cli""" + if self.keyring is None: + return None + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + subprocess.run( + [self.keyring, "set", service_name, username], + input=f"{password}{os.linesep}".encode("utf-8"), + env=env, + check=True, + ) + return None + + +@lru_cache(maxsize=None) +def get_keyring_provider(provider: str) -> KeyRingBaseProvider: + logger.verbose("Keyring provider requested: %s", provider) + + # keyring has previously failed and been disabled + if KEYRING_DISABLED: + provider = "disabled" + if provider in ["import", "auto"]: + try: + impl = KeyRingPythonProvider() + logger.verbose("Keyring provider set: import") + return impl + except ImportError: + pass + except Exception as exc: + # In the event of an unexpected exception + # we should warn the user + msg = "Installed copy of keyring fails with exception %s" + if provider == "auto": + msg = msg + ", trying to find a keyring executable as a fallback" + logger.warning(msg, exc, exc_info=logger.isEnabledFor(logging.DEBUG)) + if provider in ["subprocess", "auto"]: + cli = shutil.which("keyring") + if cli and cli.startswith(sysconfig.get_path("scripts")): + # all code within this function is stolen from shutil.which implementation + @typing.no_type_check + def PATH_as_shutil_which_determines_it() -> str: + path = os.environ.get("PATH", None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + # os.confstr() or CS_PATH is not available + path = os.defpath + # bpo-35755: Don't use os.defpath if the PATH environment variable is + # set to an empty string + + return path + + scripts = Path(sysconfig.get_path("scripts")) + + paths = [] + for path in PATH_as_shutil_which_determines_it().split(os.pathsep): + p = Path(path) + try: + if not p.samefile(scripts): + paths.append(path) + except FileNotFoundError: + pass + + path = os.pathsep.join(paths) + + cli = shutil.which("keyring", path=path) + + if cli: + logger.verbose("Keyring provider set: subprocess with executable %s", cli) + return KeyRingCliProvider(cli) + + logger.verbose("Keyring provider set: disabled") + return KeyRingNullProvider() + + +class MultiDomainBasicAuth(AuthBase): + def __init__( + self, + prompting: bool = True, + index_urls: Optional[List[str]] = None, + keyring_provider: str = "auto", + ) -> None: + self.prompting = prompting + self.index_urls = index_urls + self.keyring_provider = keyring_provider # type: ignore[assignment] + self.passwords: Dict[str, AuthInfo] = {} + # When the user is prompted to enter credentials and keyring is + # available, we will offer to save them. If the user accepts, + # this value is set to the credentials they entered. After the + # request authenticates, the caller should call + # ``save_credentials`` to save these. + self._credentials_to_save: Optional[Credentials] = None + + @property + def keyring_provider(self) -> KeyRingBaseProvider: + return get_keyring_provider(self._keyring_provider) + + @keyring_provider.setter + def keyring_provider(self, provider: str) -> None: + # The free function get_keyring_provider has been decorated with + # functools.cache. If an exception occurs in get_keyring_auth that + # cache will be cleared and keyring disabled, take that into account + # if you want to remove this indirection. + self._keyring_provider = provider + + @property + def use_keyring(self) -> bool: + # We won't use keyring when --no-input is passed unless + # a specific provider is requested because it might require + # user interaction + return self.prompting or self._keyring_provider not in ["auto", "disabled"] + + def _get_keyring_auth( + self, + url: Optional[str], + username: Optional[str], + ) -> Optional[AuthInfo]: + """Return the tuple auth for a given url from keyring.""" + # Do nothing if no url was provided + if not url: + return None + + try: + return self.keyring_provider.get_auth_info(url, username) + except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", + str(exc), + ) + global KEYRING_DISABLED + KEYRING_DISABLED = True + get_keyring_provider.cache_clear() + return None + + def _get_index_url(self, url: str) -> Optional[str]: + """Return the original index URL matching the requested URL. + + Cached or dynamically generated credentials may work against + the original index URL rather than just the netloc. + + The provided url should have had its username and password + removed already. If the original index url had credentials then + they will be included in the return value. + + Returns None if no matching index was found, or if --no-index + was specified by the user. + """ + if not url or not self.index_urls: + return None + + url = remove_auth_from_url(url).rstrip("/") + "/" + parsed_url = urllib.parse.urlsplit(url) + + candidates = [] + + for index in self.index_urls: + index = index.rstrip("/") + "/" + parsed_index = urllib.parse.urlsplit(remove_auth_from_url(index)) + if parsed_url == parsed_index: + return index + + if parsed_url.netloc != parsed_index.netloc: + continue + + candidate = urllib.parse.urlsplit(index) + candidates.append(candidate) + + if not candidates: + return None + + candidates.sort( + reverse=True, + key=lambda candidate: commonprefix( + [ + parsed_url.path, + candidate.path, + ] + ).rfind("/"), + ) + + return urllib.parse.urlunsplit(candidates[0]) + + def _get_new_credentials( + self, + original_url: str, + *, + allow_netrc: bool = True, + allow_keyring: bool = False, + ) -> AuthInfo: + """Find and return credentials for the specified URL.""" + # Split the credentials and netloc from the url. + url, netloc, url_user_password = split_auth_netloc_from_url( + original_url, + ) + + # Start with the credentials embedded in the url + username, password = url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in url for %s", netloc) + return url_user_password + + # Find a matching index url for this request + index_url = self._get_index_url(url) + if index_url: + # Split the credentials from the url. + index_info = split_auth_netloc_from_url(index_url) + if index_info: + index_url, _, index_url_user_password = index_info + logger.debug("Found index url %s", index_url) + + # If an index URL was found, try its embedded credentials + if index_url and index_url_user_password[0] is not None: + username, password = index_url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in index url for %s", netloc) + return index_url_user_password + + # Get creds from netrc if we still don't have them + if allow_netrc: + netrc_auth = get_netrc_auth(original_url) + if netrc_auth: + logger.debug("Found credentials in netrc for %s", netloc) + return netrc_auth + + # If we don't have a password and keyring is available, use it. + if allow_keyring: + # The index url is more specific than the netloc, so try it first + # fmt: off + kr_auth = ( + self._get_keyring_auth(index_url, username) or + self._get_keyring_auth(netloc, username) + ) + # fmt: on + if kr_auth: + logger.debug("Found credentials in keyring for %s", netloc) + return kr_auth + + return username, password + + def _get_url_and_credentials( + self, original_url: str + ) -> Tuple[str, Optional[str], Optional[str]]: + """Return the credentials to use for the provided URL. + + If allowed, netrc and keyring may be used to obtain the + correct credentials. + + Returns (url_without_credentials, username, password). Note + that even if the original URL contains credentials, this + function may return a different username and password. + """ + url, netloc, _ = split_auth_netloc_from_url(original_url) + + # Try to get credentials from original url + username, password = self._get_new_credentials(original_url) + + # If credentials not found, use any stored credentials for this netloc. + # Do this if either the username or the password is missing. + # This accounts for the situation in which the user has specified + # the username in the index url, but the password comes from keyring. + if (username is None or password is None) and netloc in self.passwords: + un, pw = self.passwords[netloc] + # It is possible that the cached credentials are for a different username, + # in which case the cache should be ignored. + if username is None or username == un: + username, password = un, pw + + if username is not None or password is not None: + # Convert the username and password if they're None, so that + # this netloc will show up as "cached" in the conditional above. + # Further, HTTPBasicAuth doesn't accept None, so it makes sense to + # cache the value that is going to be used. + username = username or "" + password = password or "" + + # Store any acquired credentials. + self.passwords[netloc] = (username, password) + + assert ( + # Credentials were found + (username is not None and password is not None) + # Credentials were not found + or (username is None and password is None) + ), f"Could not load credentials from url: {original_url}" + + return url, username, password + + def __call__(self, req: Request) -> Request: + # Get credentials for this request + url, username, password = self._get_url_and_credentials(req.url) + + # Set the url of the request to the url without any credentials + req.url = url + + if username is not None and password is not None: + # Send the basic auth with this request + req = HTTPBasicAuth(username, password)(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + # Factored out to allow for easy patching in tests + def _prompt_for_password( + self, netloc: str + ) -> Tuple[Optional[str], Optional[str], bool]: + username = ask_input(f"User for {netloc}: ") if self.prompting else None + if not username: + return None, None, False + if self.use_keyring: + auth = self._get_keyring_auth(netloc, username) + if auth and auth[0] is not None and auth[1] is not None: + return auth[0], auth[1], False + password = ask_password("Password: ") + return username, password, True + + # Factored out to allow for easy patching in tests + def _should_save_password_to_keyring(self) -> bool: + if ( + not self.prompting + or not self.use_keyring + or not self.keyring_provider.has_keyring + ): + return False + return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" + + def handle_401(self, resp: Response, **kwargs: Any) -> Response: + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + username, password = None, None + + # Query the keyring for credentials: + if self.use_keyring: + username, password = self._get_new_credentials( + resp.url, + allow_netrc=False, + allow_keyring=True, + ) + + # We are not able to prompt the user so simply return the response + if not self.prompting and not username and not password: + return resp + + parsed = urllib.parse.urlparse(resp.url) + + # Prompt the user for a new username and password + save = False + if not username and not password: + username, password, save = self._prompt_for_password(parsed.netloc) + + # Store the new username and password to use for future requests + self._credentials_to_save = None + if username is not None and password is not None: + self.passwords[parsed.netloc] = (username, password) + + # Prompt to save the password to keyring + if save and self._should_save_password_to_keyring(): + self._credentials_to_save = Credentials( + url=parsed.netloc, + username=username, + password=password, + ) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + # The result of the assignment isn't used, it's just needed to consume + # the content. + _ = resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + req.register_hook("response", self.warn_on_401) + + # On successful request, save the credentials that were used to + # keyring. (Note that if the user responded "no" above, this member + # is not set and nothing will be saved.) + if self._credentials_to_save: + req.register_hook("response", self.save_credentials) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def warn_on_401(self, resp: Response, **kwargs: Any) -> None: + """Response callback to warn about incorrect credentials.""" + if resp.status_code == 401: + logger.warning( + "401 Error, Credentials not correct for %s", + resp.request.url, + ) + + def save_credentials(self, resp: Response, **kwargs: Any) -> None: + """Response callback to save credentials on success.""" + assert ( + self.keyring_provider.has_keyring + ), "should never reach here without keyring" + + creds = self._credentials_to_save + self._credentials_to_save = None + if creds and resp.status_code < 400: + try: + logger.info("Saving credentials to keyring") + self.keyring_provider.save_auth_info( + creds.url, creds.username, creds.password + ) + except Exception: + logger.exception("Failed to save credentials") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/cache.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/cache.py new file mode 100644 index 0000000..4d0fb54 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/cache.py @@ -0,0 +1,106 @@ +"""HTTP cache implementation. +""" + +import os +from contextlib import contextmanager +from datetime import datetime +from typing import BinaryIO, Generator, Optional, Union + +from pip._vendor.cachecontrol.cache import SeparateBodyBaseCache +from pip._vendor.cachecontrol.caches import SeparateBodyFileCache +from pip._vendor.requests.models import Response + +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import ensure_dir + + +def is_from_cache(response: Response) -> bool: + return getattr(response, "from_cache", False) + + +@contextmanager +def suppressed_cache_errors() -> Generator[None, None, None]: + """If we can't access the cache then we can just skip caching and process + requests as if caching wasn't enabled. + """ + try: + yield + except OSError: + pass + + +class SafeFileCache(SeparateBodyBaseCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + + There is a race condition when two processes try to write and/or read the + same entry at the same time, since each entry consists of two separate + files (https://github.com/psf/cachecontrol/issues/324). We therefore have + additional logic that makes sure that both files to be present before + returning an entry; this fixes the read side of the race condition. + + For the write side, we assume that the server will only ever return the + same data for the same URL, which ought to be the case for files pip is + downloading. PyPI does not have a mechanism to swap out a wheel for + another wheel, for example. If this assumption is not true, the + CacheControl issue will need to be fixed. + """ + + def __init__(self, directory: str) -> None: + assert directory is not None, "Cache directory must not be None." + super().__init__() + self.directory = directory + + def _get_cache_path(self, name: str) -> str: + # From cachecontrol.caches.file_cache.FileCache._fn, brought into our + # class for backwards-compatibility and to avoid using a non-public + # method. + hashed = SeparateBodyFileCache.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key: str) -> Optional[bytes]: + # The cache entry is only valid if both metadata and body exist. + metadata_path = self._get_cache_path(key) + body_path = metadata_path + ".body" + if not (os.path.exists(metadata_path) and os.path.exists(body_path)): + return None + with suppressed_cache_errors(): + with open(metadata_path, "rb") as f: + return f.read() + + def _write(self, path: str, data: bytes) -> None: + with suppressed_cache_errors(): + ensure_dir(os.path.dirname(path)) + + with adjacent_tmp_file(path) as f: + f.write(data) + + replace(f.name, path) + + def set( + self, key: str, value: bytes, expires: Union[int, datetime, None] = None + ) -> None: + path = self._get_cache_path(key) + self._write(path, value) + + def delete(self, key: str) -> None: + path = self._get_cache_path(key) + with suppressed_cache_errors(): + os.remove(path) + with suppressed_cache_errors(): + os.remove(path + ".body") + + def get_body(self, key: str) -> Optional[BinaryIO]: + # The cache entry is only valid if both metadata and body exist. + metadata_path = self._get_cache_path(key) + body_path = metadata_path + ".body" + if not (os.path.exists(metadata_path) and os.path.exists(body_path)): + return None + with suppressed_cache_errors(): + return open(body_path, "rb") + + def set_body(self, key: str, body: bytes) -> None: + path = self._get_cache_path(key) + ".body" + self._write(path, body) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/download.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/download.py new file mode 100644 index 0000000..d1d4354 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/download.py @@ -0,0 +1,186 @@ +"""Download files with progress indicators. +""" +import email.message +import logging +import mimetypes +import os +from typing import Iterable, Optional, Tuple + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response + +from pip._internal.cli.progress_bars import get_download_progress_renderer +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.models.index import PyPI +from pip._internal.models.link import Link +from pip._internal.network.cache import is_from_cache +from pip._internal.network.session import PipSession +from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks +from pip._internal.utils.misc import format_size, redact_auth_from_url, splitext + +logger = logging.getLogger(__name__) + + +def _get_http_response_size(resp: Response) -> Optional[int]: + try: + return int(resp.headers["content-length"]) + except (ValueError, KeyError, TypeError): + return None + + +def _prepare_download( + resp: Response, + link: Link, + progress_bar: str, +) -> Iterable[bytes]: + total_length = _get_http_response_size(resp) + + if link.netloc == PyPI.file_storage_domain: + url = link.show_url + else: + url = link.url_without_fragment + + logged_url = redact_auth_from_url(url) + + if total_length: + logged_url = f"{logged_url} ({format_size(total_length)})" + + if is_from_cache(resp): + logger.info("Using cached %s", logged_url) + else: + logger.info("Downloading %s", logged_url) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif is_from_cache(resp): + show_progress = False + elif not total_length: + show_progress = True + elif total_length > (40 * 1000): + show_progress = True + else: + show_progress = False + + chunks = response_chunks(resp, CONTENT_CHUNK_SIZE) + + if not show_progress: + return chunks + + renderer = get_download_progress_renderer(bar_type=progress_bar, size=total_length) + return renderer(chunks) + + +def sanitize_content_filename(filename: str) -> str: + """ + Sanitize the "filename" value from a Content-Disposition header. + """ + return os.path.basename(filename) + + +def parse_content_disposition(content_disposition: str, default_filename: str) -> str: + """ + Parse the "filename" value from a Content-Disposition header, and + return the default filename if the result is empty. + """ + m = email.message.Message() + m["content-type"] = content_disposition + filename = m.get_param("filename") + if filename: + # We need to sanitize the filename to prevent directory traversal + # in case the filename contains ".." path parts. + filename = sanitize_content_filename(str(filename)) + return filename or default_filename + + +def _get_http_response_filename(resp: Response, link: Link) -> str: + """Get an ideal filename from the given HTTP response, falling back to + the link filename if not provided. + """ + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get("content-disposition") + if content_disposition: + filename = parse_content_disposition(content_disposition, filename) + ext: Optional[str] = splitext(filename)[1] + if not ext: + ext = mimetypes.guess_extension(resp.headers.get("content-type", "")) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + return filename + + +def _http_get_download(session: PipSession, link: Link) -> Response: + target_url = link.url.split("#", 1)[0] + resp = session.get(target_url, headers=HEADERS, stream=True) + raise_for_status(resp) + return resp + + +class Downloader: + def __init__( + self, + session: PipSession, + progress_bar: str, + ) -> None: + self._session = session + self._progress_bar = progress_bar + + def __call__(self, link: Link, location: str) -> Tuple[str, str]: + """Download the file given by link into location.""" + try: + resp = _http_get_download(self._session, link) + except NetworkConnectionError as e: + assert e.response is not None + logger.critical( + "HTTP error %s while getting %s", e.response.status_code, link + ) + raise + + filename = _get_http_response_filename(resp, link) + filepath = os.path.join(location, filename) + + chunks = _prepare_download(resp, link, self._progress_bar) + with open(filepath, "wb") as content_file: + for chunk in chunks: + content_file.write(chunk) + content_type = resp.headers.get("Content-Type", "") + return filepath, content_type + + +class BatchDownloader: + def __init__( + self, + session: PipSession, + progress_bar: str, + ) -> None: + self._session = session + self._progress_bar = progress_bar + + def __call__( + self, links: Iterable[Link], location: str + ) -> Iterable[Tuple[Link, Tuple[str, str]]]: + """Download the files given by links into location.""" + for link in links: + try: + resp = _http_get_download(self._session, link) + except NetworkConnectionError as e: + assert e.response is not None + logger.critical( + "HTTP error %s while getting %s", + e.response.status_code, + link, + ) + raise + + filename = _get_http_response_filename(resp, link) + filepath = os.path.join(location, filename) + + chunks = _prepare_download(resp, link, self._progress_bar) + with open(filepath, "wb") as content_file: + for chunk in chunks: + content_file.write(chunk) + content_type = resp.headers.get("Content-Type", "") + yield link, (filepath, content_type) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py new file mode 100644 index 0000000..82ec50d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/lazy_wheel.py @@ -0,0 +1,210 @@ +"""Lazy ZIP over HTTP""" + +__all__ = ["HTTPRangeRequestUnsupported", "dist_from_wheel_url"] + +from bisect import bisect_left, bisect_right +from contextlib import contextmanager +from tempfile import NamedTemporaryFile +from typing import Any, Dict, Generator, List, Optional, Tuple +from zipfile import BadZipFile, ZipFile + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response + +from pip._internal.metadata import BaseDistribution, MemoryWheel, get_wheel_distribution +from pip._internal.network.session import PipSession +from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks + + +class HTTPRangeRequestUnsupported(Exception): + pass + + +def dist_from_wheel_url(name: str, url: str, session: PipSession) -> BaseDistribution: + """Return a distribution object from the given wheel URL. + + This uses HTTP range requests to only fetch the portion of the wheel + containing metadata, just enough for the object to be constructed. + If such requests are not supported, HTTPRangeRequestUnsupported + is raised. + """ + with LazyZipOverHTTP(url, session) as zf: + # For read-only ZIP files, ZipFile only needs methods read, + # seek, seekable and tell, not the whole IO protocol. + wheel = MemoryWheel(zf.name, zf) # type: ignore + # After context manager exit, wheel.name + # is an invalid file by intention. + return get_wheel_distribution(wheel, canonicalize_name(name)) + + +class LazyZipOverHTTP: + """File-like object mapped to a ZIP file over HTTP. + + This uses HTTP range requests to lazily fetch the file's content, + which is supposed to be fed to ZipFile. If such requests are not + supported by the server, raise HTTPRangeRequestUnsupported + during initialization. + """ + + def __init__( + self, url: str, session: PipSession, chunk_size: int = CONTENT_CHUNK_SIZE + ) -> None: + head = session.head(url, headers=HEADERS) + raise_for_status(head) + assert head.status_code == 200 + self._session, self._url, self._chunk_size = session, url, chunk_size + self._length = int(head.headers["Content-Length"]) + self._file = NamedTemporaryFile() + self.truncate(self._length) + self._left: List[int] = [] + self._right: List[int] = [] + if "bytes" not in head.headers.get("Accept-Ranges", "none"): + raise HTTPRangeRequestUnsupported("range request is not supported") + self._check_zip() + + @property + def mode(self) -> str: + """Opening mode, which is always rb.""" + return "rb" + + @property + def name(self) -> str: + """Path to the underlying file.""" + return self._file.name + + def seekable(self) -> bool: + """Return whether random access is supported, which is True.""" + return True + + def close(self) -> None: + """Close the file.""" + self._file.close() + + @property + def closed(self) -> bool: + """Whether the file is closed.""" + return self._file.closed + + def read(self, size: int = -1) -> bytes: + """Read up to size bytes from the object and return them. + + As a convenience, if size is unspecified or -1, + all bytes until EOF are returned. Fewer than + size bytes may be returned if EOF is reached. + """ + download_size = max(size, self._chunk_size) + start, length = self.tell(), self._length + stop = length if size < 0 else min(start + download_size, length) + start = max(0, stop - download_size) + self._download(start, stop - 1) + return self._file.read(size) + + def readable(self) -> bool: + """Return whether the file is readable, which is True.""" + return True + + def seek(self, offset: int, whence: int = 0) -> int: + """Change stream position and return the new absolute position. + + Seek to offset relative position indicated by whence: + * 0: Start of stream (the default). pos should be >= 0; + * 1: Current position - pos may be negative; + * 2: End of stream - pos usually negative. + """ + return self._file.seek(offset, whence) + + def tell(self) -> int: + """Return the current position.""" + return self._file.tell() + + def truncate(self, size: Optional[int] = None) -> int: + """Resize the stream to the given size in bytes. + + If size is unspecified resize to the current position. + The current stream position isn't changed. + + Return the new file size. + """ + return self._file.truncate(size) + + def writable(self) -> bool: + """Return False.""" + return False + + def __enter__(self) -> "LazyZipOverHTTP": + self._file.__enter__() + return self + + def __exit__(self, *exc: Any) -> None: + self._file.__exit__(*exc) + + @contextmanager + def _stay(self) -> Generator[None, None, None]: + """Return a context manager keeping the position. + + At the end of the block, seek back to original position. + """ + pos = self.tell() + try: + yield + finally: + self.seek(pos) + + def _check_zip(self) -> None: + """Check and download until the file is a valid ZIP.""" + end = self._length - 1 + for start in reversed(range(0, end, self._chunk_size)): + self._download(start, end) + with self._stay(): + try: + # For read-only ZIP files, ZipFile only needs + # methods read, seek, seekable and tell. + ZipFile(self) # type: ignore + except BadZipFile: + pass + else: + break + + def _stream_response( + self, start: int, end: int, base_headers: Dict[str, str] = HEADERS + ) -> Response: + """Return HTTP response to a range request from start to end.""" + headers = base_headers.copy() + headers["Range"] = f"bytes={start}-{end}" + # TODO: Get range requests to be correctly cached + headers["Cache-Control"] = "no-cache" + return self._session.get(self._url, headers=headers, stream=True) + + def _merge( + self, start: int, end: int, left: int, right: int + ) -> Generator[Tuple[int, int], None, None]: + """Return a generator of intervals to be fetched. + + Args: + start (int): Start of needed interval + end (int): End of needed interval + left (int): Index of first overlapping downloaded data + right (int): Index after last overlapping downloaded data + """ + lslice, rslice = self._left[left:right], self._right[left:right] + i = start = min([start] + lslice[:1]) + end = max([end] + rslice[-1:]) + for j, k in zip(lslice, rslice): + if j > i: + yield i, j - 1 + i = k + 1 + if i <= end: + yield i, end + self._left[left:right], self._right[left:right] = [start], [end] + + def _download(self, start: int, end: int) -> None: + """Download bytes from start to end inclusively.""" + with self._stay(): + left = bisect_left(self._right, start) + right = bisect_right(self._left, end) + for start, end in self._merge(start, end, left, right): + response = self._stream_response(start, end) + response.raise_for_status() + self.seek(start) + for chunk in response_chunks(response, self._chunk_size): + self._file.write(chunk) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/session.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/session.py new file mode 100644 index 0000000..f17efc5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/session.py @@ -0,0 +1,520 @@ +"""PipSession and supporting code, containing all pip-specific +network request configuration and behavior. +""" + +import email.utils +import io +import ipaddress +import json +import logging +import mimetypes +import os +import platform +import shutil +import subprocess +import sys +import urllib.parse +import warnings +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Generator, + List, + Mapping, + Optional, + Sequence, + Tuple, + Union, +) + +from pip._vendor import requests, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter as _BaseCacheControlAdapter +from pip._vendor.requests.adapters import DEFAULT_POOLBLOCK, BaseAdapter +from pip._vendor.requests.adapters import HTTPAdapter as _BaseHTTPAdapter +from pip._vendor.requests.models import PreparedRequest, Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.urllib3.connectionpool import ConnectionPool +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +from pip import __version__ +from pip._internal.metadata import get_default_environment +from pip._internal.models.link import Link +from pip._internal.network.auth import MultiDomainBasicAuth +from pip._internal.network.cache import SafeFileCache + +# Import ssl from compat so the initial import occurs in only one place. +from pip._internal.utils.compat import has_tls +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.misc import build_url_from_netloc, parse_netloc +from pip._internal.utils.urls import url_to_path + +if TYPE_CHECKING: + from ssl import SSLContext + + from pip._vendor.urllib3.poolmanager import PoolManager + + +logger = logging.getLogger(__name__) + +SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] + + +# Ignore warning raised when using --trusted-host. +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +SECURE_ORIGINS: List[SecureOrigin] = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] + + +# These are environment variables present when running under various +# CI systems. For each variable, some CI systems that use the variable +# are indicated. The collection was chosen so that for each of a number +# of popular systems, at least one of the environment variables is used. +# This list is used to provide some indication of and lower bound for +# CI traffic to PyPI. Thus, it is okay if the list is not comprehensive. +# For more background, see: https://github.com/pypa/pip/issues/5499 +CI_ENVIRONMENT_VARIABLES = ( + # Azure Pipelines + "BUILD_BUILDID", + # Jenkins + "BUILD_ID", + # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI + "CI", + # Explicit environment variable. + "PIP_IS_CI", +) + + +def looks_like_ci() -> bool: + """ + Return whether it looks like pip is running under CI. + """ + # We don't use the method of checking for a tty (e.g. using isatty()) + # because some CI systems mimic a tty (e.g. Travis CI). Thus that + # method doesn't provide definitive information in either direction. + return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES) + + +def user_agent() -> str: + """ + Return a string representing the user agent. + """ + data: Dict[str, Any] = { + "installer": {"name": "pip", "version": __version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == "CPython": + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == "PyPy": + pypy_version_info = sys.pypy_version_info # type: ignore + if pypy_version_info.releaselevel == "final": + pypy_version_info = pypy_version_info[:3] + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == "Jython": + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == "IronPython": + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + from pip._vendor import distro + + linux_distribution = distro.name(), distro.version(), distro.codename() + distro_infos: Dict[str, Any] = dict( + filter( + lambda x: x[1], + zip(["name", "version", "id"], linux_distribution), + ) + ) + libc = dict( + filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + ) + ) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + if has_tls(): + import _ssl as ssl + + data["openssl_version"] = ssl.OPENSSL_VERSION + + setuptools_dist = get_default_environment().get_distribution("setuptools") + if setuptools_dist is not None: + data["setuptools_version"] = str(setuptools_dist.version) + + if shutil.which("rustc") is not None: + # If for any reason `rustc --version` fails, silently ignore it + try: + rustc_output = subprocess.check_output( + ["rustc", "--version"], stderr=subprocess.STDOUT, timeout=0.5 + ) + except Exception: + pass + else: + if rustc_output.startswith(b"rustc "): + # The format of `rustc --version` is: + # `b'rustc 1.52.1 (9bc8c42bb 2021-05-09)\n'` + # We extract just the middle (1.52.1) part + data["rustc_version"] = rustc_output.split(b" ")[1].decode() + + # Use None rather than False so as not to give the impression that + # pip knows it is not being run under CI. Rather, it is a null or + # inconclusive result. Also, we include some value rather than no + # value to make it easier to know that the check has been run. + data["ci"] = True if looks_like_ci() else None + + user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") + if user_data is not None: + data["user_data"] = user_data + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class LocalFSAdapter(BaseAdapter): + def send( + self, + request: PreparedRequest, + stream: bool = False, + timeout: Optional[Union[float, Tuple[float, float]]] = None, + verify: Union[bool, str] = True, + cert: Optional[Union[str, Tuple[str, str]]] = None, + proxies: Optional[Mapping[str, str]] = None, + ) -> Response: + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + # format the exception raised as a io.BytesIO object, + # to return a better error message: + resp.status_code = 404 + resp.reason = type(exc).__name__ + resp.raw = io.BytesIO(f"{resp.reason}: {exc}".encode("utf8")) + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict( + { + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + } + ) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self) -> None: + pass + + +class _SSLContextAdapterMixin: + """Mixin to add the ``ssl_context`` constructor argument to HTTP adapters. + + The additional argument is forwarded directly to the pool manager. This allows us + to dynamically decide what SSL store to use at runtime, which is used to implement + the optional ``truststore`` backend. + """ + + def __init__( + self, + *, + ssl_context: Optional["SSLContext"] = None, + **kwargs: Any, + ) -> None: + self._ssl_context = ssl_context + super().__init__(**kwargs) + + def init_poolmanager( + self, + connections: int, + maxsize: int, + block: bool = DEFAULT_POOLBLOCK, + **pool_kwargs: Any, + ) -> "PoolManager": + if self._ssl_context is not None: + pool_kwargs.setdefault("ssl_context", self._ssl_context) + return super().init_poolmanager( # type: ignore[misc] + connections=connections, + maxsize=maxsize, + block=block, + **pool_kwargs, + ) + + +class HTTPAdapter(_SSLContextAdapterMixin, _BaseHTTPAdapter): + pass + + +class CacheControlAdapter(_SSLContextAdapterMixin, _BaseCacheControlAdapter): + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + def cert_verify( + self, + conn: ConnectionPool, + url: str, + verify: Union[bool, str], + cert: Optional[Union[str, Tuple[str, str]]], + ) -> None: + super().cert_verify(conn=conn, url=url, verify=False, cert=cert) + + +class InsecureCacheControlAdapter(CacheControlAdapter): + def cert_verify( + self, + conn: ConnectionPool, + url: str, + verify: Union[bool, str], + cert: Optional[Union[str, Tuple[str, str]]], + ) -> None: + super().cert_verify(conn=conn, url=url, verify=False, cert=cert) + + +class PipSession(requests.Session): + timeout: Optional[int] = None + + def __init__( + self, + *args: Any, + retries: int = 0, + cache: Optional[str] = None, + trusted_hosts: Sequence[str] = (), + index_urls: Optional[List[str]] = None, + ssl_context: Optional["SSLContext"] = None, + **kwargs: Any, + ) -> None: + """ + :param trusted_hosts: Domains not to emit warnings for when not using + HTTPS. + """ + super().__init__(*args, **kwargs) + + # Namespace the attribute with "pip_" just in case to prevent + # possible conflicts with the base class. + self.pip_trusted_origins: List[Tuple[str, Optional[int]]] = [] + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth(index_urls=index_urls) + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + # A 500 may indicate transient error in Amazon S3 + # A 502 may be a transient error from a CDN like CloudFlare or CloudFront + # A 520 or 527 - may indicate transient error in CloudFlare + status_forcelist=[500, 502, 503, 520, 527], + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) # type: ignore + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching so we'll use it for all http:// URLs. + # If caching is disabled, we will also use it for + # https:// hosts that we've marked as ignoring + # TLS errors for (trusted-hosts). + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + # We want to _only_ cache responses on securely fetched origins or when + # the host is specified as trusted. We do this because + # we can't validate the response of an insecurely/untrusted fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ssl_context=ssl_context, + ) + self._trusted_host_adapter = InsecureCacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries, ssl_context=ssl_context) + self._trusted_host_adapter = insecure_adapter + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + for host in trusted_hosts: + self.add_trusted_host(host, suppress_logging=True) + + def update_index_urls(self, new_index_urls: List[str]) -> None: + """ + :param new_index_urls: New index urls to update the authentication + handler with. + """ + self.auth.index_urls = new_index_urls + + def add_trusted_host( + self, host: str, source: Optional[str] = None, suppress_logging: bool = False + ) -> None: + """ + :param host: It is okay to provide a host that has previously been + added. + :param source: An optional source string, for logging where the host + string came from. + """ + if not suppress_logging: + msg = f"adding trusted host: {host!r}" + if source is not None: + msg += f" (from {source})" + logger.info(msg) + + parsed_host, parsed_port = parse_netloc(host) + if parsed_host is None: + raise ValueError(f"Trusted host URL must include a host part: {host!r}") + if (parsed_host, parsed_port) not in self.pip_trusted_origins: + self.pip_trusted_origins.append((parsed_host, parsed_port)) + + self.mount( + build_url_from_netloc(host, scheme="http") + "/", self._trusted_host_adapter + ) + self.mount(build_url_from_netloc(host) + "/", self._trusted_host_adapter) + if not parsed_port: + self.mount( + build_url_from_netloc(host, scheme="http") + ":", + self._trusted_host_adapter, + ) + # Mount wildcard ports for the same host. + self.mount(build_url_from_netloc(host) + ":", self._trusted_host_adapter) + + def iter_secure_origins(self) -> Generator[SecureOrigin, None, None]: + yield from SECURE_ORIGINS + for host, port in self.pip_trusted_origins: + yield ("*", host, "*" if port is None else port) + + def is_secure_origin(self, location: Link) -> bool: + # Determine if this url used a secure transport mechanism + parsed = urllib.parse.urlparse(str(location)) + origin_protocol, origin_host, origin_port = ( + parsed.scheme, + parsed.hostname, + parsed.port, + ) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + origin_protocol = origin_protocol.rsplit("+", 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in self.iter_secure_origins(): + secure_protocol, secure_host, secure_port = secure_origin + if origin_protocol != secure_protocol and secure_protocol != "*": + continue + + try: + addr = ipaddress.ip_address(origin_host or "") + network = ipaddress.ip_network(secure_host) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if ( + origin_host + and origin_host.lower() != secure_host.lower() + and secure_host != "*" + ): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port matches. + if ( + origin_port != secure_port + and secure_port != "*" + and secure_port is not None + ): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS we " + "recommend you use HTTPS instead, otherwise you may silence " + "this warning and allow it anyway with '--trusted-host %s'.", + origin_host, + origin_host, + ) + + return False + + def request(self, method: str, url: str, *args: Any, **kwargs: Any) -> Response: + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + # Allow setting a default proxies on a session + kwargs.setdefault("proxies", self.proxies) + + # Dispatch the actual request + return super().request(method, url, *args, **kwargs) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/utils.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/utils.py new file mode 100644 index 0000000..134848a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/utils.py @@ -0,0 +1,96 @@ +from typing import Dict, Generator + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response + +from pip._internal.exceptions import NetworkConnectionError + +# The following comments and HTTP headers were originally added by +# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03. +# +# We use Accept-Encoding: identity here because requests defaults to +# accepting compressed responses. This breaks in a variety of ways +# depending on how the server is configured. +# - Some servers will notice that the file isn't a compressible file +# and will leave the file alone and with an empty Content-Encoding +# - Some servers will notice that the file is already compressed and +# will leave the file alone, adding a Content-Encoding: gzip header +# - Some servers won't notice anything at all and will take a file +# that's already been compressed and compress it again, and set +# the Content-Encoding: gzip header +# By setting this to request only the identity encoding we're hoping +# to eliminate the third case. Hopefully there does not exist a server +# which when given a file will notice it is already compressed and that +# you're not asking for a compressed file and will then decompress it +# before sending because if that's the case I don't think it'll ever be +# possible to make this work. +HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"} + + +def raise_for_status(resp: Response) -> None: + http_error_msg = "" + if isinstance(resp.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. + try: + reason = resp.reason.decode("utf-8") + except UnicodeDecodeError: + reason = resp.reason.decode("iso-8859-1") + else: + reason = resp.reason + + if 400 <= resp.status_code < 500: + http_error_msg = ( + f"{resp.status_code} Client Error: {reason} for url: {resp.url}" + ) + + elif 500 <= resp.status_code < 600: + http_error_msg = ( + f"{resp.status_code} Server Error: {reason} for url: {resp.url}" + ) + + if http_error_msg: + raise NetworkConnectionError(http_error_msg, response=resp) + + +def response_chunks( + response: Response, chunk_size: int = CONTENT_CHUNK_SIZE +) -> Generator[bytes, None, None]: + """Given a requests Response, provide the data chunks.""" + try: + # Special case for urllib3. + for chunk in response.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False, + ): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = response.raw.read(chunk_size) + if not chunk: + break + yield chunk diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py b/.venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py new file mode 100644 index 0000000..22ec8d2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/network/xmlrpc.py @@ -0,0 +1,62 @@ +"""xmlrpclib.Transport implementation +""" + +import logging +import urllib.parse +import xmlrpc.client +from typing import TYPE_CHECKING, Tuple + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status + +if TYPE_CHECKING: + from xmlrpc.client import _HostType, _Marshallable + + from _typeshed import SizedBuffer + +logger = logging.getLogger(__name__) + + +class PipXmlrpcTransport(xmlrpc.client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__( + self, index_url: str, session: PipSession, use_datetime: bool = False + ) -> None: + super().__init__(use_datetime) + index_parts = urllib.parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request( + self, + host: "_HostType", + handler: str, + request_body: "SizedBuffer", + verbose: bool = False, + ) -> Tuple["_Marshallable", ...]: + assert isinstance(host, str) + parts = (self._scheme, host, handler, None, None, None) + url = urllib.parse.urlunparse(parts) + try: + headers = {"Content-Type": "text/xml"} + response = self._session.post( + url, + data=request_body, + headers=headers, + stream=True, + ) + raise_for_status(response) + self.verbose = verbose + return self.parse_response(response.raw) + except NetworkConnectionError as exc: + assert exc.response + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, + url, + ) + raise diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000000000000000000000000000000000000..1d282f492d18d339e27f9d4365d7396ef1c80bd7 GIT binary patch literal 218 zcmZ9GL5jja5Jl7KfCwJMMN+6+5fry0G8YJKERxcss_E(p3qP2(rp)tk}BfS7)y*+IJ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..854d99591fba9a802a895f45956c06bb9d6a5f6f GIT binary patch literal 7600 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..4ab934e9bfea2731cc2f817054c16cf45793eb90 GIT binary patch literal 10138 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 RXZ>_5)k{4>BvzFAe*h6GElmIb literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3f9558d6fab2a3caed34820a04c477d3c3807a9 GIT binary patch literal 25768 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 MVflh%FSdC8FG`uC&Hw-a literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__init__.py new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000000000000000000000000000000000000..d3054fc712119cdb742fae4a58e413a9b154d1e8 GIT binary patch literal 224 zcmZ8bL2AQ54D`kcgn}Q^LoAwHL!lwphSCq1wJjW_wzRA^0iXJezM)@fJ`ubGs8FRr9)7won`p&a=OYRv(=YvP{EOdf;gaO+k!as oHjuDFd8D?6BK{~8Meh1xzV4(xw)pC2v5$$PqqwEU7@tP<00rJZvH$=8 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..dede48d8378b437b19a9a113421d016b95c2878c GIT binary patch literal 7844 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 P#UC(C`*VUdCEfo3f8p2< literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e636bcb87249f1c070f990b24ee83e0ab210937 GIT binary patch literal 1901 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8e51ef0fb59690e9d3c26d80faa624e521f333b GIT binary patch literal 3086 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..8e9cd3f0cc64db7ba9378f0991c98cdacf39311f GIT binary patch literal 2046 zcmZuyU2GFa5Z=4}qRq2Cx2j>PI>=?W}vAOjhkh4R77m-#o*6;4L(n$iT@-!KfmDct4*A%|Re{a_eE6Mcb<$d^L+ zJ~NRfH^HoI7|lqi@iO9&j6h4?paN`DX!2i+p`{s7hCH%gC;{7>!&A@8(?mzOyFNFC z57%yd2lXS@M-kf}KqNCGJrj9y{ZzOzq$X-gw^!eULu264jP!xD(XR%0$>dYq6uKmg zp((B(?MIZG;@KGvexJP;%H33WJ9y9uF@-Fec%BE*CR&ew3^mp)WMb2w`-oB<8>?^F z)CD-5$M4qx2~}*nYMlW}bt_mscjBBnJiJdWG61SI;*R>vKpPPeHq)EeN?1fVzTI#nx^;RwUV^r>UIZ9DF&(`%;&sanJ( zy-u+oS`gA|J*b9*smcyj^vPf!)GJV4(>qS;riG2*w*R>?NHYmfvYc`m>QOXsv0f$; zIN`O?dU5r=3PD|ZDc@uvbcnEW@I*|ALp=G!r4sfl-s2tWMcAWykqO7NGf~3og2WhM z@eu|@s8YY!y}1AE1;4sEOqnW64vdvgw2D*38r5l4x3zP@Q&H`_Q*>OjM77h7U3QM= z_8x@>Qnp#Z z6D;>2rQ-?WpNV1k+sOUctG{g8(h@ z1s{Bc0%1+a6R1o4WIPM`zJw)=asQXX! z)*tBPqTF?L+XK1luH1EVr8tVo2H4lG!Sb Rq}0{YM;De8z>$8i@Glj$5vKqE literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..797c47076ac64a39567f85b36353d2f8e3c0165d GIT binary patch literal 3950 zcmb_fU2Gf25#HnR$h+gOL{fi_lapo1Vr`SQ6Imtf-oRS&0QMo&17sXEN<`{1YW&I^y-JSu!elJ zL^Equ(=c?Bn^s{;ZhltN^jsbn322EG^17ZQwdpE03K}6!V#dJbyqPN)<#N8FGC3#w z3Kwm1M$N3&OecV030x`oJWH{jhn_yq{|MbZWHzjbkcFBSUG%A(<+~3z_A$T6TO0{m zyvo-j2m#06KxRwdoWOLx(miT$W}x|bPHa-`_swyHFxBgC>$SK!@T&by^)>fJ1dCIB zOrC?8z}-h5dpz4$I#54yl^IEC7(yvkJgdP?C_pK?Q7o1!#Y{c%re{V4%v~#M6_boA zFFCoMY<~AlII2t<6|H_`|JNVah^8DreoMpC22iG{~{qXpD~?zf4G}SfR(wW^QSf zTUos{ovkjIvqohkbMjP{luYe-HD9=yFKQ%PEmgC*QpMCTxN#PI80V==lk9Y@q^nua zU3IOPFDzuL3r=dEgJO-;bQ5>MCd2TMkDyyZyD~~1S+}+lr)mhd5_4@$H#(Mw1otX4)S(}SCsrGxWqfIv{ zi?fg_&~V+{%?$+5z4Q(XR*E^RDb>6=OF>0x=4Jq7#?V_ba2_ux%m)>?Fz|E(FJ$T` zCzvNT28@};M2h0|Kr56fdT2W`HBJ|f$7M| zS3whVAZ55PYZwG#3yff5rbm_c7iCP%otB&cF)*y{1U!f^Okip*Cpe$S6`1AlpvdtV z#PQR^JN`MNRDs~p145YE3ezOGCm!%+QY19Q(g2_+I0dA00HRCILu8H_sF72sN<2t~ z;UV{-10Z&yNMiNOFWy?Z^5;<8j=lLLdTld$?RVo_(YKd*TMTW-4zK^@lS^B%u}yJo znSUxq??hLkt7Ka|V8;@l2j%56AJkVb|NN(}pNnMwDaTiaR+Fo>E%~4uL{iulq5Cg7 zJ`Y9ioLf0}=iUG? z&R`jqI1%rtO;(^zq83`|2ei=;%na!3eKOT}_&}`w^ z3O3Pq1}%zaLmHvEpf_)CuY$&=Z*xdOW(&<$0MeGv5+4Uz>q%w{BXga5wRm(J^^3Ae zFPSwYe=6#Rth`TKLA; zkPcz${FwTM7i9!ho30fxbyY{Ya=V~0=Z}o5uVa#g|Y7h(@7kMNlwrp85ZN&B?im|xQimD zu?y2c<@7-*muZ!9nSx%*G@OkvFL4gPDo`^^I?3$)2Zdsld6o0}*NV3OPhn2x)w~j9 zqY1C<6>~Y7CUZH@A9-S8FA375#BTZ5h{b*83NOWrWkap$+9f;&;}EkE`4@C9&vD#e zP!#^2q5glMk-wt1pP}nJQr~j-6RB@g>RUU%B^_Q0xPcBXZg+OAlDlb_2YM&j?F#UA z11KOp4^nElhdXA6Vypbh1(yeUC)(i(@CK1k)D2QfM2T*uLZY0EQax^nQehN`E*F0G z-t!3M&K~9ZY&qic@ZL#uxdOaFNk`HRQc6Ur-nGQt@3|7?p!)4uX^8jRD6j@gKIN?bQGP literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py new file mode 100644 index 0000000..3791932 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/build_tracker.py @@ -0,0 +1,139 @@ +import contextlib +import hashlib +import logging +import os +from types import TracebackType +from typing import Dict, Generator, Optional, Set, Type, Union + +from pip._internal.models.link import Link +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +@contextlib.contextmanager +def update_env_context_manager(**changes: str) -> Generator[None, None, None]: + target = os.environ + + # Save values from the target and change them. + non_existent_marker = object() + saved_values: Dict[str, Union[object, str]] = {} + for name, new_value in changes.items(): + try: + saved_values[name] = target[name] + except KeyError: + saved_values[name] = non_existent_marker + target[name] = new_value + + try: + yield + finally: + # Restore original values in the target. + for name, original_value in saved_values.items(): + if original_value is non_existent_marker: + del target[name] + else: + assert isinstance(original_value, str) # for mypy + target[name] = original_value + + +@contextlib.contextmanager +def get_build_tracker() -> Generator["BuildTracker", None, None]: + root = os.environ.get("PIP_BUILD_TRACKER") + with contextlib.ExitStack() as ctx: + if root is None: + root = ctx.enter_context(TempDirectory(kind="build-tracker")).path + ctx.enter_context(update_env_context_manager(PIP_BUILD_TRACKER=root)) + logger.debug("Initialized build tracking at %s", root) + + with BuildTracker(root) as tracker: + yield tracker + + +class TrackerId(str): + """Uniquely identifying string provided to the build tracker.""" + + +class BuildTracker: + """Ensure that an sdist cannot request itself as a setup requirement. + + When an sdist is prepared, it identifies its setup requirements in the + context of ``BuildTracker.track()``. If a requirement shows up recursively, this + raises an exception. + + This stops fork bombs embedded in malicious packages.""" + + def __init__(self, root: str) -> None: + self._root = root + self._entries: Dict[TrackerId, InstallRequirement] = {} + logger.debug("Created build tracker: %s", self._root) + + def __enter__(self) -> "BuildTracker": + logger.debug("Entered build tracker: %s", self._root) + return self + + def __exit__( + self, + exc_type: Optional[Type[BaseException]], + exc_val: Optional[BaseException], + exc_tb: Optional[TracebackType], + ) -> None: + self.cleanup() + + def _entry_path(self, key: TrackerId) -> str: + hashed = hashlib.sha224(key.encode()).hexdigest() + return os.path.join(self._root, hashed) + + def add(self, req: InstallRequirement, key: TrackerId) -> None: + """Add an InstallRequirement to build tracking.""" + + # Get the file to write information about this requirement. + entry_path = self._entry_path(key) + + # Try reading from the file. If it exists and can be read from, a build + # is already in progress, so a LookupError is raised. + try: + with open(entry_path) as fp: + contents = fp.read() + except FileNotFoundError: + pass + else: + message = "{} is already being built: {}".format(req.link, contents) + raise LookupError(message) + + # If we're here, req should really not be building already. + assert key not in self._entries + + # Start tracking this requirement. + with open(entry_path, "w", encoding="utf-8") as fp: + fp.write(str(req)) + self._entries[key] = req + + logger.debug("Added %s to build tracker %r", req, self._root) + + def remove(self, req: InstallRequirement, key: TrackerId) -> None: + """Remove an InstallRequirement from build tracking.""" + + # Delete the created file and the corresponding entry. + os.unlink(self._entry_path(key)) + del self._entries[key] + + logger.debug("Removed %s from build tracker %r", req, self._root) + + def cleanup(self) -> None: + for key, req in list(self._entries.items()): + self.remove(req, key) + + logger.debug("Removed build tracker: %r", self._root) + + @contextlib.contextmanager + def track(self, req: InstallRequirement, key: str) -> Generator[None, None, None]: + """Ensure that `key` cannot install itself as a setup requirement. + + :raises LookupError: If `key` was already provided in a parent invocation of + the context introduced by this method.""" + tracker_id = TrackerId(key) + self.add(req, tracker_id) + yield + self.remove(req, tracker_id) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py new file mode 100644 index 0000000..c66ac35 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata.py @@ -0,0 +1,39 @@ +"""Metadata generation logic for source distributions. +""" + +import os + +from pip._vendor.pyproject_hooks import BuildBackendHookCaller + +from pip._internal.build_env import BuildEnvironment +from pip._internal.exceptions import ( + InstallationSubprocessError, + MetadataGenerationFailed, +) +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory + + +def generate_metadata( + build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str +) -> str: + """Generate metadata using mechanisms described in PEP 517. + + Returns the generated metadata directory. + """ + metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True) + + metadata_dir = metadata_tmpdir.path + + with build_env: + # Note that BuildBackendHookCaller implements a fallback for + # prepare_metadata_for_build_wheel, so we don't have to + # consider the possibility that this hook doesn't exist. + runner = runner_with_spinner_message("Preparing metadata (pyproject.toml)") + with backend.subprocess_runner(runner): + try: + distinfo_dir = backend.prepare_metadata_for_build_wheel(metadata_dir) + except InstallationSubprocessError as error: + raise MetadataGenerationFailed(package_details=details) from error + + return os.path.join(metadata_dir, distinfo_dir) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py new file mode 100644 index 0000000..27c69f0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_editable.py @@ -0,0 +1,41 @@ +"""Metadata generation logic for source distributions. +""" + +import os + +from pip._vendor.pyproject_hooks import BuildBackendHookCaller + +from pip._internal.build_env import BuildEnvironment +from pip._internal.exceptions import ( + InstallationSubprocessError, + MetadataGenerationFailed, +) +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory + + +def generate_editable_metadata( + build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str +) -> str: + """Generate metadata using mechanisms described in PEP 660. + + Returns the generated metadata directory. + """ + metadata_tmpdir = TempDirectory(kind="modern-metadata", globally_managed=True) + + metadata_dir = metadata_tmpdir.path + + with build_env: + # Note that BuildBackendHookCaller implements a fallback for + # prepare_metadata_for_build_wheel/editable, so we don't have to + # consider the possibility that this hook doesn't exist. + runner = runner_with_spinner_message( + "Preparing editable metadata (pyproject.toml)" + ) + with backend.subprocess_runner(runner): + try: + distinfo_dir = backend.prepare_metadata_for_build_editable(metadata_dir) + except InstallationSubprocessError as error: + raise MetadataGenerationFailed(package_details=details) from error + + return os.path.join(metadata_dir, distinfo_dir) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py new file mode 100644 index 0000000..e60988d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/metadata_legacy.py @@ -0,0 +1,74 @@ +"""Metadata generation logic for legacy source distributions. +""" + +import logging +import os + +from pip._internal.build_env import BuildEnvironment +from pip._internal.cli.spinners import open_spinner +from pip._internal.exceptions import ( + InstallationError, + InstallationSubprocessError, + MetadataGenerationFailed, +) +from pip._internal.utils.setuptools_build import make_setuptools_egg_info_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory + +logger = logging.getLogger(__name__) + + +def _find_egg_info(directory: str) -> str: + """Find an .egg-info subdirectory in `directory`.""" + filenames = [f for f in os.listdir(directory) if f.endswith(".egg-info")] + + if not filenames: + raise InstallationError(f"No .egg-info directory found in {directory}") + + if len(filenames) > 1: + raise InstallationError( + "More than one .egg-info directory found in {}".format(directory) + ) + + return os.path.join(directory, filenames[0]) + + +def generate_metadata( + build_env: BuildEnvironment, + setup_py_path: str, + source_dir: str, + isolated: bool, + details: str, +) -> str: + """Generate metadata using setup.py-based defacto mechanisms. + + Returns the generated metadata directory. + """ + logger.debug( + "Running setup.py (path:%s) egg_info for package %s", + setup_py_path, + details, + ) + + egg_info_dir = TempDirectory(kind="pip-egg-info", globally_managed=True).path + + args = make_setuptools_egg_info_args( + setup_py_path, + egg_info_dir=egg_info_dir, + no_user_config=isolated, + ) + + with build_env: + with open_spinner("Preparing metadata (setup.py)") as spinner: + try: + call_subprocess( + args, + cwd=source_dir, + command_desc="python setup.py egg_info", + spinner=spinner, + ) + except InstallationSubprocessError as error: + raise MetadataGenerationFailed(package_details=details) from error + + # Return the .egg-info directory. + return _find_egg_info(egg_info_dir) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py new file mode 100644 index 0000000..064811a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel.py @@ -0,0 +1,37 @@ +import logging +import os +from typing import Optional + +from pip._vendor.pyproject_hooks import BuildBackendHookCaller + +from pip._internal.utils.subprocess import runner_with_spinner_message + +logger = logging.getLogger(__name__) + + +def build_wheel_pep517( + name: str, + backend: BuildBackendHookCaller, + metadata_directory: str, + tempd: str, +) -> Optional[str]: + """Build one InstallRequirement using the PEP 517 build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + assert metadata_directory is not None + try: + logger.debug("Destination directory: %s", tempd) + + runner = runner_with_spinner_message( + f"Building wheel for {name} (pyproject.toml)" + ) + with backend.subprocess_runner(runner): + wheel_name = backend.build_wheel( + tempd, + metadata_directory=metadata_directory, + ) + except Exception: + logger.error("Failed building wheel for %s", name) + return None + return os.path.join(tempd, wheel_name) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py new file mode 100644 index 0000000..719d69d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_editable.py @@ -0,0 +1,46 @@ +import logging +import os +from typing import Optional + +from pip._vendor.pyproject_hooks import BuildBackendHookCaller, HookMissing + +from pip._internal.utils.subprocess import runner_with_spinner_message + +logger = logging.getLogger(__name__) + + +def build_wheel_editable( + name: str, + backend: BuildBackendHookCaller, + metadata_directory: str, + tempd: str, +) -> Optional[str]: + """Build one InstallRequirement using the PEP 660 build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + assert metadata_directory is not None + try: + logger.debug("Destination directory: %s", tempd) + + runner = runner_with_spinner_message( + f"Building editable for {name} (pyproject.toml)" + ) + with backend.subprocess_runner(runner): + try: + wheel_name = backend.build_editable( + tempd, + metadata_directory=metadata_directory, + ) + except HookMissing as e: + logger.error( + "Cannot build editable %s because the build " + "backend does not have the %s hook", + name, + e, + ) + return None + except Exception: + logger.error("Failed building editable for %s", name) + return None + return os.path.join(tempd, wheel_name) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py new file mode 100644 index 0000000..c5f0492 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/build/wheel_legacy.py @@ -0,0 +1,102 @@ +import logging +import os.path +from typing import List, Optional + +from pip._internal.cli.spinners import open_spinner +from pip._internal.utils.setuptools_build import make_setuptools_bdist_wheel_args +from pip._internal.utils.subprocess import call_subprocess, format_command_args + +logger = logging.getLogger(__name__) + + +def format_command_result( + command_args: List[str], + command_output: str, +) -> str: + """Format command information for logging.""" + command_desc = format_command_args(command_args) + text = f"Command arguments: {command_desc}\n" + + if not command_output: + text += "Command output: None" + elif logger.getEffectiveLevel() > logging.DEBUG: + text += "Command output: [use --verbose to show]" + else: + if not command_output.endswith("\n"): + command_output += "\n" + text += f"Command output:\n{command_output}" + + return text + + +def get_legacy_build_wheel_path( + names: List[str], + temp_dir: str, + name: str, + command_args: List[str], + command_output: str, +) -> Optional[str]: + """Return the path to the wheel in the temporary build directory.""" + # Sort for determinism. + names = sorted(names) + if not names: + msg = ("Legacy build of wheel for {!r} created no files.\n").format(name) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + return None + + if len(names) > 1: + msg = ( + "Legacy build of wheel for {!r} created more than one file.\n" + "Filenames (choosing first): {}\n" + ).format(name, names) + msg += format_command_result(command_args, command_output) + logger.warning(msg) + + return os.path.join(temp_dir, names[0]) + + +def build_wheel_legacy( + name: str, + setup_py_path: str, + source_dir: str, + global_options: List[str], + build_options: List[str], + tempd: str, +) -> Optional[str]: + """Build one unpacked package using the "legacy" build process. + + Returns path to wheel if successfully built. Otherwise, returns None. + """ + wheel_args = make_setuptools_bdist_wheel_args( + setup_py_path, + global_options=global_options, + build_options=build_options, + destination_dir=tempd, + ) + + spin_message = f"Building wheel for {name} (setup.py)" + with open_spinner(spin_message) as spinner: + logger.debug("Destination directory: %s", tempd) + + try: + output = call_subprocess( + wheel_args, + command_desc="python setup.py bdist_wheel", + cwd=source_dir, + spinner=spinner, + ) + except Exception: + spinner.finish("error") + logger.error("Failed building wheel for %s", name) + return None + + names = os.listdir(tempd) + wheel_path = get_legacy_build_wheel_path( + names=names, + temp_dir=tempd, + name=name, + command_args=wheel_args, + command_output=output, + ) + return wheel_path diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/check.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000..90c6a58 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,187 @@ +"""Validation of dependencies of packages +""" + +import logging +from typing import Callable, Dict, List, NamedTuple, Optional, Set, Tuple + +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import LegacySpecifier +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.version import LegacyVersion + +from pip._internal.distributions import make_distribution_for_install_requirement +from pip._internal.metadata import get_default_environment +from pip._internal.metadata.base import DistributionVersion +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.deprecation import deprecated + +logger = logging.getLogger(__name__) + + +class PackageDetails(NamedTuple): + version: DistributionVersion + dependencies: List[Requirement] + + +# Shorthands +PackageSet = Dict[NormalizedName, PackageDetails] +Missing = Tuple[NormalizedName, Requirement] +Conflicting = Tuple[NormalizedName, DistributionVersion, Requirement] + +MissingDict = Dict[NormalizedName, List[Missing]] +ConflictingDict = Dict[NormalizedName, List[Conflicting]] +CheckResult = Tuple[MissingDict, ConflictingDict] +ConflictDetails = Tuple[PackageSet, CheckResult] + + +def create_package_set_from_installed() -> Tuple[PackageSet, bool]: + """Converts a list of distributions into a PackageSet.""" + package_set = {} + problems = False + env = get_default_environment() + for dist in env.iter_installed_distributions(local_only=False, skip=()): + name = dist.canonical_name + try: + dependencies = list(dist.iter_dependencies()) + package_set[name] = PackageDetails(dist.version, dependencies) + except (OSError, ValueError) as e: + # Don't crash on unreadable or broken metadata. + logger.warning("Error parsing requirements for %s: %s", name, e) + problems = True + return package_set, problems + + +def check_package_set( + package_set: PackageSet, should_ignore: Optional[Callable[[str], bool]] = None +) -> CheckResult: + """Check if a package set is consistent + + If should_ignore is passed, it should be a callable that takes a + package name and returns a boolean. + """ + + warn_legacy_versions_and_specifiers(package_set) + + missing = {} + conflicting = {} + + for package_name, package_detail in package_set.items(): + # Info about dependencies of package_name + missing_deps: Set[Missing] = set() + conflicting_deps: Set[Conflicting] = set() + + if should_ignore and should_ignore(package_name): + continue + + for req in package_detail.dependencies: + name = canonicalize_name(req.name) + + # Check if it's missing + if name not in package_set: + missed = True + if req.marker is not None: + missed = req.marker.evaluate({"extra": ""}) + if missed: + missing_deps.add((name, req)) + continue + + # Check if there's a conflict + version = package_set[name].version + if not req.specifier.contains(version, prereleases=True): + conflicting_deps.add((name, version, req)) + + if missing_deps: + missing[package_name] = sorted(missing_deps, key=str) + if conflicting_deps: + conflicting[package_name] = sorted(conflicting_deps, key=str) + + return missing, conflicting + + +def check_install_conflicts(to_install: List[InstallRequirement]) -> ConflictDetails: + """For checking if the dependency graph would be consistent after \ + installing given requirements + """ + # Start from the current state + package_set, _ = create_package_set_from_installed() + # Install packages + would_be_installed = _simulate_installation_of(to_install, package_set) + + # Only warn about directly-dependent packages; create a whitelist of them + whitelist = _create_whitelist(would_be_installed, package_set) + + return ( + package_set, + check_package_set( + package_set, should_ignore=lambda name: name not in whitelist + ), + ) + + +def _simulate_installation_of( + to_install: List[InstallRequirement], package_set: PackageSet +) -> Set[NormalizedName]: + """Computes the version of packages after installing to_install.""" + # Keep track of packages that were installed + installed = set() + + # Modify it as installing requirement_set would (assuming no errors) + for inst_req in to_install: + abstract_dist = make_distribution_for_install_requirement(inst_req) + dist = abstract_dist.get_metadata_distribution() + name = dist.canonical_name + package_set[name] = PackageDetails(dist.version, list(dist.iter_dependencies())) + + installed.add(name) + + return installed + + +def _create_whitelist( + would_be_installed: Set[NormalizedName], package_set: PackageSet +) -> Set[NormalizedName]: + packages_affected = set(would_be_installed) + + for package_name in package_set: + if package_name in packages_affected: + continue + + for req in package_set[package_name].dependencies: + if canonicalize_name(req.name) in packages_affected: + packages_affected.add(package_name) + break + + return packages_affected + + +def warn_legacy_versions_and_specifiers(package_set: PackageSet) -> None: + for project_name, package_details in package_set.items(): + if isinstance(package_details.version, LegacyVersion): + deprecated( + reason=( + f"{project_name} {package_details.version} " + f"has a non-standard version number." + ), + replacement=( + f"to upgrade to a newer version of {project_name} " + f"or contact the author to suggest that they " + f"release a version with a conforming version number" + ), + issue=12063, + gone_in="24.1", + ) + for dep in package_details.dependencies: + if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier): + deprecated( + reason=( + f"{project_name} {package_details.version} " + f"has a non-standard dependency specifier {dep}." + ), + replacement=( + f"to upgrade to a newer version of {project_name} " + f"or contact the author to suggest that they " + f"release a version with a conforming dependency specifiers" + ), + issue=12063, + gone_in="24.1", + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000..3544568 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,255 @@ +import collections +import logging +import os +from typing import Container, Dict, Generator, Iterable, List, NamedTuple, Optional, Set + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.metadata import BaseDistribution, get_environment +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_file import COMMENT_RE +from pip._internal.utils.direct_url_helpers import direct_url_as_pep440_direct_reference + +logger = logging.getLogger(__name__) + + +class _EditableInfo(NamedTuple): + requirement: str + comments: List[str] + + +def freeze( + requirement: Optional[List[str]] = None, + local_only: bool = False, + user_only: bool = False, + paths: Optional[List[str]] = None, + isolated: bool = False, + exclude_editable: bool = False, + skip: Container[str] = (), +) -> Generator[str, None, None]: + installations: Dict[str, FrozenRequirement] = {} + + dists = get_environment(paths).iter_installed_distributions( + local_only=local_only, + skip=(), + user_only=user_only, + ) + for dist in dists: + req = FrozenRequirement.from_dist(dist) + if exclude_editable and req.editable: + continue + installations[req.canonical_name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options: Set[str] = set() + # keep track of which files a requirement is in so that we can + # give an accurate warning if a requirement appears multiple times. + req_files: Dict[str, List[str]] = collections.defaultdict(list) + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if ( + not line.strip() + or line.strip().startswith("#") + or line.startswith( + ( + "-r", + "--requirement", + "-f", + "--find-links", + "-i", + "--index-url", + "--pre", + "--trusted-host", + "--process-dependency-links", + "--extra-index-url", + "--use-feature", + ) + ) + ): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith("-e") or line.startswith("--editable"): + if line.startswith("-e"): + line = line[2:].strip() + else: + line = line[len("--editable") :].strip().lstrip("=") + line_req = install_req_from_editable( + line, + isolated=isolated, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub("", line).strip(), + isolated=isolated, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, + line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + else: + line_req_canonical_name = canonicalize_name(line_req.name) + if line_req_canonical_name not in installations: + # either it's not installed, or it is installed + # but has been processed already + if not req_files[line_req.name]: + logger.warning( + "Requirement file [%s] contains %s, but " + "package %r is not installed", + req_file_path, + COMMENT_RE.sub("", line).strip(), + line_req.name, + ) + else: + req_files[line_req.name].append(req_file_path) + else: + yield str(installations[line_req_canonical_name]).rstrip() + del installations[line_req_canonical_name] + req_files[line_req.name].append(req_file_path) + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). + for name, files in req_files.items(): + if len(files) > 1: + logger.warning( + "Requirement %s included multiple times [%s]", + name, + ", ".join(sorted(set(files))), + ) + + yield ("## The following requirements were added by pip freeze:") + for installation in sorted(installations.values(), key=lambda x: x.name.lower()): + if installation.canonical_name not in skip: + yield str(installation).rstrip() + + +def _format_as_name_version(dist: BaseDistribution) -> str: + dist_version = dist.version + if isinstance(dist_version, Version): + return f"{dist.raw_name}=={dist_version}" + return f"{dist.raw_name}==={dist_version}" + + +def _get_editable_info(dist: BaseDistribution) -> _EditableInfo: + """ + Compute and return values (req, comments) for use in + FrozenRequirement.from_dist(). + """ + editable_project_location = dist.editable_project_location + assert editable_project_location + location = os.path.normcase(os.path.abspath(editable_project_location)) + + from pip._internal.vcs import RemoteNotFoundError, RemoteNotValidError, vcs + + vcs_backend = vcs.get_backend_for_dir(location) + + if vcs_backend is None: + display = _format_as_name_version(dist) + logger.debug( + 'No VCS found for editable requirement "%s" in: %r', + display, + location, + ) + return _EditableInfo( + requirement=location, + comments=[f"# Editable install with no version control ({display})"], + ) + + vcs_name = type(vcs_backend).__name__ + + try: + req = vcs_backend.get_src_requirement(location, dist.raw_name) + except RemoteNotFoundError: + display = _format_as_name_version(dist) + return _EditableInfo( + requirement=location, + comments=[f"# Editable {vcs_name} install with no remote ({display})"], + ) + except RemoteNotValidError as ex: + display = _format_as_name_version(dist) + return _EditableInfo( + requirement=location, + comments=[ + f"# Editable {vcs_name} install ({display}) with either a deleted " + f"local remote or invalid URI:", + f"# '{ex.url}'", + ], + ) + except BadCommand: + logger.warning( + "cannot determine version of editable source in %s " + "(%s command not found in path)", + location, + vcs_backend.name, + ) + return _EditableInfo(requirement=location, comments=[]) + except InstallationError as exc: + logger.warning("Error when trying to get requirement for VCS system %s", exc) + else: + return _EditableInfo(requirement=req, comments=[]) + + logger.warning("Could not determine repository location of %s", location) + + return _EditableInfo( + requirement=location, + comments=["## !! Could not determine repository location"], + ) + + +class FrozenRequirement: + def __init__( + self, + name: str, + req: str, + editable: bool, + comments: Iterable[str] = (), + ) -> None: + self.name = name + self.canonical_name = canonicalize_name(name) + self.req = req + self.editable = editable + self.comments = comments + + @classmethod + def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement": + editable = dist.editable + if editable: + req, comments = _get_editable_info(dist) + else: + comments = [] + direct_url = dist.direct_url + if direct_url: + # if PEP 610 metadata is present, use it + req = direct_url_as_pep440_direct_reference(direct_url, dist.raw_name) + else: + # name==version requirement + req = _format_as_name_version(dist) + + return cls(dist.raw_name, req, editable, comments=comments) + + def __str__(self) -> str: + req = self.req + if self.editable: + req = f"-e {req}" + return "\n".join(list(self.comments) + [str(req)]) + "\n" diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py new file mode 100644 index 0000000..24d6a5d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__init__.py @@ -0,0 +1,2 @@ +"""For modules related to installing packages. +""" diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bde2744d5991e30e0bec616a71100a296e34c53b GIT binary patch literal 287 zcmXv~K}rKb5ZqaVhCu$X*MQkZa}_}mZ;E*IGE8=x&FJj(Fx>{$o3HQ{Wiou#?%#Q|zhHp4_dvO0UpIyK4PPH!m|E!D3MxrpeD zU|k3m*0UJrtZ#~7su$BBm!tcM`Z>|Mrb_G2Y}2pYzQclA1UB3n4-b)^u@Z(ynv>Xy z;f{PmH`miEX507{y+FA{zXWIJ5IR|qpOt6lz*ca2I0eB5(AEn^ZyZp-XvC6d*yjSB iTq|0aq1z5G{tH~jxl)5TNOA{)D^{`q literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce3481ce51b4e44671e776152bc84f5fbed871ef GIT binary patch literal 1838 zcmZuxO>Epm6dwPsz5huzU7D8GC5>PaS*t&w3L#LcazJegYENAbuGgMy+92j?Uf@+4+shFy}|_q5~~prl?n%Lt{PM+C*Ihb1fssxJb&-a?7W%xzBfNs zDn$hH=m$BFHH01uV@Dbj@br5C_mGb|$d`OMlsXc8l~C?09i^{!R7v>NQ0wa*J;Pd9 z=o=j)!+L1;i=ASI3t_2W?vy1YBP?B;+X=My#nlZdw>mZ7z$&gE1k*RMGC{>%RKin! z8Q1*^j4I$7*8Mu*DPLY`_|u=No#~<6npuD60`9rq(87L@xZMz2LBtX_4BaFkkrh+o zVa8swf;MhjR~b$QaXTJbK3>BiiLbVce~a^6Y{}esKNf#+LvCEg9}RHi;g-bfX9q#( zzZlE%yBQH-qLM8wMh63X$2Xzl6pmVtUjNLX|Tg5Eqz8JJ3=Bq;0gsKZQ1&XVJsZ5+m+%lK#+qR&i&0Ge-?GGrq z24rIPCDJ8H;4yodL_Kou?BWt*K`-kJyS)ZHw8Nll=eIf4K5^0(EMA=Co!M~^+fEQA zm_no3B*rxBQ)W-P*4}AjC(PO!63Xk7IcH~%9)hEai?AW6TWISD(n_D5|K$8n^%LKm z{#8BmXX)j!F*|N9jGITtwb^lHYJ6aRt5PWIx6W-%qhjOp!%vW|RmYVBPmpF-e=i>z z*Bj#(-xxO*|0$@I>N5qr&lFgaignLqFjKK|d(XMeUV`Y~Doe+d?`li4+C$X<_;6NR zI$}PYD+qi{TY9z7n&Ab<@rma+ydbIuY~gw`jNwZK^Qj7oHz76c-PE*q`Y);p9TjF~f1hE95w|DbAv>1bEkl zw=OHh*XSvD5f#tN)<_xp2(7sFiIz9f zn~%)7+s)t1xu4Cs`)7VJkKfW$-IA)08i(&+{&G3h0H->t%-+3xXE`kh%Rr@>yQl6f zrlzopsPRHt5~z$y)wCi|6*Zb^O`tle&7@NTO{2P%&Ir^%)_giE&;g_ux0-?yA@$YRmGl%EyPc?(z8REGAOfTw_9S_|5R|3b~ z;6%>DiM*&8<~=;~H69J~wH__=1&_ddokz!fy+_Y{gU85xlgGq-v&YPQi^sxztH%mo zJ8T=t@MQ2*hcIj($@FBhux{8flI6)7$@XNA+*i$@G;wc#^^^}g3dCEpy9@j{@r+lQsQ!!HMsT`^DRE<=7sz+)(HLNVd zaP3H)r*5R)Q_tRw!wn;io<hkNV}(fq{Gv};xdMJjO_I69O?9QvUmINu94lI-6MNEd)RyC@ZOPqo_!ijmmCa7Q>X_a#ou^Pl*(R+&pruZHvMhyE{hij%(o)VKL~nkSLQ zCFc82e_Ioe>GN0l`~0W<)&4X7(*qhOq6f6(dd`YHVuAm(==7fv3z6z{;=5ifdP(Ow zCl=#tp}z=U3gS5hvE(Je^AyWh=>OD!AeQ3&yjbSXL`r&h;oT#a`?s?)DQ~=1R*u>N zr5%D;fz(fnmHrC;tz+c1NziJ7>-;@S|mJrUQ>;@T0{;oq8=X;IukJrOl1 zZ6`|evs9f(waed_NHt*Mlz-w;aW~TM@z;ra;qUYB5%>E)C3fNO0LHVE_2(eosKhL> z8*_WnuN?|2D!Lg#SKkw;Z)j3ee{AYV-^BR% zm=tskj7hG+(Lm5QJUlph(d8QzT^A+>hedh`Uh=ytu3Yl_hbvqc21k9;q-)&Qf7y4@ zPuWI%LAUu=)TG{eUUp1K!vWub-+Q6G%|F^dCi)R+IN=Wjki1?i>-sPG21jMfz~FGu zFAeyH1Af_nYEjI1y+hXYoRn>6&YwEuJ@~|-gU5P$kI34t(Mj2OfYpny#)D|0@4~QO z7P<%fgR<#}f7CDef@6|w>_L(2lMwhHSI$h1`(@+F@!;UtsBc&{_W3_M zffo8@ZJ$3V>&{G!qfFh|QGAw#eqSJ1uaOO&!STa`h^Ii$=)f2{W9jRc2FHUZe3$(a zUNbNHgI@phG>BgRXi%D@LFo65j*SlX`-TUn{N7RD2n~XxM;TrpHQGkZkH6l>Gg{v(hk-+jI z`O#F3?C^<0zJBz^8yp$;VyZ9zl7D;{arJ_1@Ar)dCnUe<4FtupiJ)xuj|T9LHb}DR zk}q&c$zmG!Nx?yCIWpP$CnSkVoEY&1F3Z`20q>Q;;HAM)%7ylfNt5!n02|7nWA{bQ1N$``yO+XGU+7rnx}cbp{63wBu+yild zc1&__^1W}uk%fSNcpyMYT@PPqximK7ZwdGUBfilV^k)bI7ic*>c3~_y*dJ&)F*bT} ztouN7S0FHWadZTu-Qs@EKl)tD@Zg1(@yXz&vC&rd)@>~Tj8}7f2M1cl2gh5ygQM6w z*cL5gVf6H@nyC8yy`BDwQHaixn>m7skehrF|$- z+C|QOa`uu#6UCe^1OoYR{)Ag|R!5E1YuR~U%2;#ce#v%U;7txC4@I$2Lze3;!7fei0hzs#5f_MfwrZv+b(t_&Z;X;}r%Dd@N!L&*W z3>h{hz^kZ9d^M%{s#U*E;p4Or5`q>rRVeXoK-0(73sd_}vvGITDPUK`#=3}A*W?=Z zk6sL3s%Idc#=tcZAgE3o6$`^1xa8Zmy}hn}%Akby$ogSS#^~0E{GNKf)QAct>Zz<( z3WSf9E&4Ih1e?aCBluXalX@uOQA)^PLJ_P|sTc2TiMZ%tGe`@B>X!QO?G$}K0cVE$ zAk(=}wP=cFHhjKkrfcT8HCx__vteoLcXqtF zh&uZsw!WFeYu2o<9bC|V^;p>A3JWea^|C+!W5X_^&BZ1-1A!Y{h*998_Ejw#z*F%7 zyzHO6f}QK~Nf#$*69rtsG1s_6J7aj#1#i^la~u$X5@06i>H^Lidgo9g*~jh8#<~-GS<;8SglyecvYUE)b*~$rCGZnPF;Bc z%~9?Pd@!+gY0%WYu$`OcL;O&hm8-7(X$|&9V$G_pNL}{hSH@F)E0q>**uoUN^UC`;IC+S?mWli-_@?? zwdc7j{O0&cqY__lntJlIg6&%7ruaHd!+kV4y_S4+dz1$pY;- z*(mvaBHKG=q85VzY7lV6WB}wxk{r-0z)#KvWkk6ZYDwK~vVlqn_{U{|ij#F$B%s1L z?2`2XM%c-wfk8kvR+j-q6P}{9W^;)oL*P5aBeN9@W#pOC!;ylv!*qB!K%G7Vy|30 zwqoD1mR<0`fHW-OeIu8hcf)<%y_!`U$*O%zyIR*3sq2c?9gJpm&l=WpoC`;;pM2xO zw}xIHTCM4b)O6f3L~Hg(a`scPxz|27_qi`k&k7H0DDZv;XU<5pLZd4}D~uV}taH}+ z^DD+OHGF2pSolDP&oMn`&3urnHs#@i3^np$pcK9RtIBRp`%Yn34ZI)cRCZgnKjC!U zCc{sxy6#*M0VDpPPxJ+S8z58y6hDCy2ow(`uq`6u4;XNbx<7^po^5*6Jtk@|*Kr&H z@!l!>>9Mh3b5Ef86i8CwH|ZQQ$wCmT6O^qM>q6EIk6j_!T^h%yRH&P)(9#l0AaZg% zUcv;DNtCn#4rm=q_O;z}yTb+bOWUFr_o}5WVrh$7+HciFEc?U4{y6p_iadRn@1mF+ z+#rA_{xs>ku6C`P`!rC;v^Jzo2f9U`IxZFBD-GzTC&VM3P{F2$DA=n_k10_@B?kDb zyB|}g=3~k{CjdZ&G>jn8_JUCW894@;PuU{@*>Z7WP<;VU;K1gS6sE~LJ_ZsrAnPtl zV-w@D?m6G^1a=)`odoGQG+26`oJlyc1xQ(%jAx;K6BJVLrkyw<_E{-VdJ&%~dw^gy z<}R;b=Hyyd&djm3{G!$T=16{XG{5B|PSEY0wX9k4RxQO5OL5q>^wJp0=w{%6e zbj_YzIJR2c6e(_c*V?>hD_FIaMr@^v%~4y+T2ser)1FAvp4npymeqo~NI~7Z*7}$U z<;4I3iWWeT)P{vxWlb331F>OQ5cXua`U{vU+6e)El26i}>83rHm~0j90}pfYrJ@NY zrhLeVvNt{HuyH7XVWzd8kUN28lf|aw7HG*4f*P_XzpJ583YNNxrIJ!prkW<{52*sv zWE807B$Y6foAMzZOerz%(XZ-Qb#vEr{hEuQ{XhLQww5j>SGw^^gKs|}N718ohjeP~ zsy|fnXsVQQ39xQ@)STBfS3fOq)B0&$NIz7*DG^>%O8Nh-Kc7&CB~7acc(P}xN)07_ zwQrB}8Rb)(Os|HLVDg&w8rQEKq**=8ag0mTPX8YnizFwewmzhPj+X$kL)=$2SD(OK z8K%*ekb!pgMQ+*{G7NoeUp_sY=z%F%ul6-$Qp=w<2OATyW;Iq67&0(}vNel(TDISS zpyHd_Y0GGnXdG%*b0mGyG}MxOQ$xtji{`=v2OwGqb|ThHWl~tJaoQ3x4y9RvqBUee z-L{YxZpJqgYag`dsKAN#X=_URjcRG}_LrwGGh|KMgG@EI>hs*F>klfyq2^S5o(pbO z$3o0{DRm9~ABI!v>Q-AeZ423?Y%zN}<1szVP*Z{r$Vof5iP7%EPT}!4t-GRxutKM` zSGfAzsc&}sftVqWWc0jmbkY^aTVRR(F34(JW23H9U1y$PVq!%E43-$I>L^%Rh2B?~ zf$GQf8m+Zu{KW$XCTv_va2F*zeqfM?NolP>&m6U z{!1t#wV&=O|3Q#4gQ8z@`9vlYN4by+ut6A;ny6EgV-t`P_y>>%UjmS;gJ*#3;1bG; z4^0z9FMd~Z^XOPJ#WX98Y=%UNNx$4v87y@|B0A+j|B*wD81x4=kPbvPwBqmHHaN$? z+7bEiX`o(W%R$5GLy%_Drd8;~KjUxk*n$p$o7DVk?G+wk2^z77{0qH`96}>&+*@RR zka1>llIo#p_eDt9pC6a@PPN77(CfPZxvxsed9fnAW20Ug4EJv8%y3|@JCW=As3K4X z=f82ko#8&>G*10mf%B(N&hoS6bHcp#%a#wdi2EJm4!>Z?=U=hq^HYPpC(j(}bkQ`$ zCCAEOgQN`N03qNaMzk(a?_%5_$+H-T`k5C#(-fb=5#J=6!wY^FT8{$_O!9?E7fugc zzVY#4NavZD)BTV)*E?kkB*ViHr;_ATw!}rwA{{VjA(fWAXurai8Za4*B{W`ue4d50 zSQD}Vg4RK4Y*Y#$sjLg22An%kD~aDULbgAGLj{O_C`8fHDN2aa$7MYl@|D#eLdIMzS__gY3TY%Wwhb&Oz&Dw(MOzC*EVZh&&|K_)b*!sc&~dGKNZb& z&zjfs3T|AyesMLgCX!dP_!pRbxTKSnz7r zYxy_xSDp0{XZ=!n)Y%j@Hm@4nBF472Pyg+?@1A?lxI3mr&Y2T24qnX4>9SUZf{0MC zZZxMa=BfL@lL@g&Oom(Q#Eo+%MH%!+}3+|=t zrHXK2b2QVvoFB>Dacl3LipZ`Lk<1hKw3@<<*`pA9nzEEJUCYRt@0@R17+V@zdM3Q{ zRM`6DueBPJIhKvK#EN;-K|ZF%I6`h>w9M}O;tLCocMXMWMu(Dpp=8zB9C0>>-Fu_X zeeW6f-+lB;SJZi6#dr`aD$n^LL9O+Yve!H}J+D1`^Vy}t%Lk$*9jhgKA|-o%aN&nT z-y4dS^kDFv`g@!vPe0T9p`J75#RT5A2kkHwrZhe5Y*;ZiGNH<~opU?q&qtYX=yxAo z<}v%fyO)Kr4v^6EN87tx$J>Q}>*}%`-=&ug0J+lGm5msH7(vE7)*z62WFhh|0T*<0 zn@I==Ni@FKfHr&SP;n0dB*Kz@AMhhmGGYMW z7$=qhQTRewFDSWPr>PX86o9-&T?)h?lsJMDQ&tq>U5vq3C;;F|0j6hd1B}pp5Ko^ z0?&iReFk$s&C_ud$z75{gCt8bpp9ew0$_cPu)frZLLmHN$2NjAi#OSldRVDrN4Plc zLtg^4zz9Awz%0Fjx2YN+{P@}tz41kv2Jinzqz%xrRPlb6{{5!zrDL~pZws?U^Zr-+ z7IVG#ytr^DJ59yXU*r4VQ$pgHA$V=ztXbHu zS^BjnPGUo5(T23@V;tZj6T(Q#NJoO;3xxDGoc|>@(*Y#;qfSjAg;C{|eVj7T+9d5D zeG}!@Ygh}Uw-HXIv*axG#28230}wtEiSRc_^q(mq9Wcyraqvyj#|6V;QPk2L7MfYF z()VK=gZD@EBf;T`Jduw>iPImv#2*!~q@14+F}dkUcP@&vRGL&gpIkcQXkDLB2T9O2 zJ*g+Q;JmqXQm;=aHE{x!EIKKThGZcj;S#~;Cubcp=r~o~BGI{Xy)n4}WHTcRMF|Im z5>-!X6~k<%>+mMsAg6#bX_VvCw-Mg7NU$`pKvMRrLDPA|EkStQzVfhPtSs;jIhbx%lSARrmggd;jhHsJrJ*dBlBu)qOhRJ{@(R zdDn1e-R`({W$wzg7w2AF2rfPqwKvZScP(~Wc&}<+Gu$-1X1Qru?2B6JXS&xd_Ic}q z7`0S}g-W&GUcP|s`=mrNl ziH^xM{{kRRp90zvr*B|tx-njP!Nj^tx7tKaNUzQgIQzs(#;oZ@fmXNrb=r{rEHh*X zB~IxeyD^3|&vBA7c@m#IdBitCElpkHOcG_9Hjg%>?Sqc>0g^mXM{OD9Mg6oXZAwET zr6G~h9MYwgv>HREka-}XApoZD4T$EokPvUdXj+1a^V5(e*;Earox8lqm*4`?CTGzlb=u#uE0A)&BADq)=RGVI$4 zhQ>Dn#L86F{9>o83xvhEYd{(s0nyQa30q60UT9+xo{lGG{9gPUD~s?NrBN{MuW0d2 zHN`0(Vt$CgP)L*@basH>C6EmMVdzNVsG`$V9bg-r?RM#l$gGGYXf@WGrEkE48pPN| z;D6ab%n*=1+uX8td~BR)KQQ<%El^h4#Il}IMS=PZ=?4_|Z^)r7BmF<*5HOH45Ob$qMOUMcvX~@Q6?u zicf*`4=DFT`~~U&*P)$|$2GTr@v?*QGV8kdSuODbfiLZt_r0=v-I{f+cdj>F)N`kG z#d>nRuyi4`)VJ(-^IWvBZPvb4SovD>&E_Tht+Hrg58f?>3l}4n>h+S!m8$mT7w;IN zRi~mQPtNtu9=?9~gR+Xn)^9w&RQ~6o`Mw3mSI#YG%^qG)7YVLz)mRcSmMpxutiPoX zmlDw*%cbU_xf%JyBDT-+>-O@+@;JO(cLHpF!#S5RtDn~=^9L8tEu9OOw!LTEPJCfI zD0^$++M(XnLuVt0&PESC^&73GM8DS56*iSFY8DT?Ypnf;~EcR&c~bxwL% zC<-A>sstDiRX{)3ldhz6co!N9Iu%Na`pX?y+I5eqjsJK(5RpMb$e00>zrlSo<$UR} zei(#JTaIYm?Zq1LZrGCD&_;1o!@bv{ z2b?laQbmSWk_XBz68I5Lfsm7FAvb}^By~rHM+99< z0vH_cl^utA`_7&|bgJi+_vEQFJtuqnru6vW+T&t~L^k_|p@zqRtSZ^wvM;NQ}G$M~i(y!G%M{=K@RUnvhej?L=ftT}+kcvI1A*IH)& z0)O2c)8g%JVfkx~Hyam6m&It|E_CgE2*T3!BV4;9ysI}-+Y7v$Xv$hf(MoY^w0L{C zsC~IV+<79rvo~xd&KkltGgnXsbXR0v%gTPE;#+mE*M%F8+;O~DaqN|@7{$-p!FU&$ zA?(U3Pt!EuPF=yOP#O_RU%j$8xmw#1sqMIx|HG2+m3+S}TGq279KCBP4GX2e`>2u2 zI?n&@qXLu`D8TIfXjjqUYT?KE0^Gk7iVimjf9De5_TyXPQIEgx5fPQt?pAcW{ZP#F z>qJ4+4Hy+|Zc*?X1~j4pn%zdxBpTtHpy6$nZP5NdfEy2f@i27Cq;`}h0c~-zk+jcA z(9V>Jd;1f(5FZI25J`ED)NO#jft~3TL3u(@`|-xPV4ST2H+bXtgScY6QBL+i6Kz@hmF9PKr?e z314Ibwlf1XZ2W0dQ-woDKoR!&kq*Y=FH#2yT|f)So_+0wxffRL)e(F3qPSYq8L8=v z+INMGyB_Uaf)3k{8T#X-Dd{L9wUg6)Fd@H4N=86e#Rmh@m&D3QM5UEyU@(cM-1MmR zNM%W`awv^#_y;Z=8U$e<~I5=zEg7NVW*l1kiiRlfFIbctT4V|2FH;UTsaUTYI%5u3M|=m7YcM zVDShTC{mtl6e4&cHNYXf3DrRy{!NU63%xA;ioVbekbLZO;1aF_4bzF`_$0xeu?s^I z*m_QCfdge$1`J-Oqz1qVwqt%uZvvePZqi|8S1hKoHi87Y+d=A>0b=YRP%?5jCsR+( z&V1|H*Po5%w&C>5h%=ZKW9j`IE>+j{HTO;Tilu%XaXoWA^Wu%s>!T~y>bqHaH=3_E zf3;=SaM$Lz_UzoVt2S4}=32~;+UkKNOifEqt~+v-^ot@+*J2!RYg!7fXXdSDRzxx@ z7Pl^nE18WCw1|EH{L_SnSt}N6S1b+d*1flaKYZbPFT7{%#d(Xh@qqz}SOE`NQdYsk zKovUlqq_X=9PN+x<#!u(Kh85E{1cOw*=@dx2a-Q*l1hva!o zZY~jFD*YF7h%8m6Xdi`kk+Ywiz2y8-b)Kd1+=;iBxR?g2v}Q38Du03#^u?g)dC<^?KRd z#}p+9iS=%f7*in(fL~>WY{FICP@=YEARScNfYZ|F5|SmTp^w9T(g#QdRZ*RSWfPc} ziA5kKrL`^#vI{`gEds1j|Vpxq{Pro#*Y?s(tTuAZb^+%h`|sd38YQf zQQ7A9lmVeV{$&~r7m{NzjMi)BIrBC9oPEXUT+c6GtooMwb$2v>YXakg%=k4G-A2v< zIK9d)(QV!(Y+Iz;3?ETWa{d)nOgQKz?w&?~M!Hp_+or5<`n+L%tG&KQ67rW*S2(+& zCu#?TS#AKg++VVK5PF{X=y1lVlXFiD;u_rO#e;E1t#4ug>iW_PC?!s;sXGTT9L)mT z08I#doliycb0F2ejPD@JcnAwL-%Js!PIC3y-l^iAQ3&n^MU~ZK?4pY)Aa}Z^UUD_N zx)S^%6oaT8q+lfB0xIr4if-4z0oSEqa6Hi2g8LP8u@>#dg*&>i=N^+Twh+@YIG}2* zw6OA<6$R&(N~jbygZqjr69w4$CDW{8kRPlVR9f6neb+%H$uF_30mUDNBxo>Xlh@l1 z)s2AH8=w(zxn>m5MbkJ{nsyWu+v%$`k>yDETO3XqZF2^wJFI0u1z@Qex(qvt;Mj_O zt<{;#Y<6*->>v`SK-44jN;S#0cr*I_!^2+ho4oYTh-C!cex*IXrKIG1g(@X1{aXc= znimh!9SQSN_XF}iBu80Hlv=iJ7`+W5w#2M|1_?3eC|G%t*Plp}>ht9go}{jpc&eG4 z@`7sUF;)PE~t&KB>)(qCLp@ z^BCEFG+{sL<&0B<;|ly?H>Z`}8phal>;T)5{j~hlhqgVv-|K|KHe_;g66c2ipNVOi z0}u6OP||b|uF`x-27(4jdWfQMD-dc3D$7JeF6rnSTWwS+Bna^hU_ks-_QqzrDfeIE zL!cCCvFdT!W~x~nSuwV)+jHiw%!L+CEOkWftzl!UvIeRBk5UqB-IGjFB87U%Ob&@A zC8|{V7YhBqa8lR5KwrrD3#y(-39SFShmU@!6?AQDB`)adz;A3_?!J|MtN&KPcY7m^ z2U#R&irM}-ClrBnZBXUWwJ9xQ1x}+J*`(Zi0ktTbl^`99(1j?v5H6d>C;P`n#(`wz z9H!M3U}S_>)mxO!I7x@@oELZhWfOrl5SlpMQx_7d;HZaeKI_oomo+c%czNf`oiFct zdH2hEUf#Q5>w)`K=Z~G88F3z#SQ$wjXbBR=bj;%`dq=J5uc*ATcaVE!PmpAh)uZDOYLNjs7cD6A%{)eLR16>vpZ+^%E87xtggsh>= zM7$$dmWa299BMpU5n0pO8ZKlD@pF6C`01Qrc_MvIC@T@4`!PMqMo;om>d6abtNB3n6km?LIjP7~`AWKieqZp>dU=C61|D+>}OmZXe!ai}CEJ~4VhqJY@5 z;d^TDlKFB&xsc@p#7&omO4I3^tMg3;ba2*OI6TI0E*h>=qq%m4b>iY?o@pJd8;hRszwS>_N^%7`rqdE5Q{Kq~8PyuChmMIb;;& z!Ch+X&~7!9^ph!pkCG|(CR3;(u|SVM<{2UkzUdk2*%X1-P?efHe%m{-yTzhV)zGm=b9_P_ z#~;lh7OSgG^{1=Zny5zYaV(Bm{SyHubUiKsUdZ& z)t+Lm&Z^(&F8I(n^)2bEEtswmouL|UVrPgYmw3E4t0~!6xB3<&$qswwI@Q{yYlA*D zHrTI*#L^*A4JZA%-9s0XZ)zyf{`goWO0Eml3=OKOl76T*k)l3i9=e?TqJ~2CY6?jA z(x*sYa-CQvItLS008&cPNfp77N6VjX2sL~@H)Ym{Tix`88j|HeyYjQ@XSLsUZi)xw z+ZB}5FGE2!l=L_3@$%qv>X!|W;`u~u5>n`AcL9S|yku4Dn{NDsHU2rZl%zkmH)Kq# znWjHHf2c{UOyqCI?r01(tD^!q6_`8Rw>*gL!>%Q*yi}OU2CVKOg*K zS{j^D%T2Vng|)aPrNvu9Thg@{_Z9)~Hb3gf5BKlo!c|FGi09I`&Rw8i6zD}-S$U~Q=K{<)7#lv*gk+O z?!m7rWnvZ+d;AUlPkF59_F%G4UsF>h{UntsHip{OvJ-vi2z3nINTyap!JBF*)TX9Y z{YO{Lq;9hee_nfaXL>07yjzsJod|p$9{37|1zml;_I7zUk14EQIr5&xMD|U zzv8O#aZNZxcrWlKh=S{u0mRbrpL>To_m`DhxI$eEC z@trQmv3q#T(3skrxRi`^?wY8PtRU(Ssn(&w)PfUyYNzX|OMZ+6yNY*V66UVr3~aJn zu2W3D#;!Ohiv8*U(S4{*9PWnl(BuBg7-?T{!{x<|14b=mS0a3gQGo3r<=UjuWG2)J zxX!|M8Z*J?7tv<_aA%@}4K&?~Vu`|E!$z9x5>)-zWrB%O5*1>=lu2W^-v(iDkM5QY z;wnX=7#EF&f4KRaVp%Q`pHjS=Y*jRqypS5}636_?^+%@1!PXS3gk^=bKB=Ds?$RQ* z8LWGZi4aXfx0fi^-bZ9OB;&Yu3eMNH8@c?sgjOyzg0yqc^(E38+G$yHLfWI^RIw}W zQP*px+D}07GmXwCyZsTLj#Q;jrtBGT_d?e8ebglp1cW+DCg=**8HrRvn2{zDe<+%l z|ArF24JV;qGBcydhvdw-#${sSr|MK49L4Hf@>0a+l<%UV03Fwn1PP{Hsuw@CbS0YA z0ZT3=RkNn~+K936eM|P5HT&AJxnm24_pA`_;QE!cpPEAy{02Fu77=Wqs}Ojcsvr(h zQ7b)k2dG!Ng%H`rgNhC_rUQ{H6`XpeZh;XLEC_ZNVXE?Rol8mdGwPQEr6_0Y({s}c zeNlUP*jUb5F58$LJhe#K0!u!mqeH{J;XKNKB3Oq!2yBx6L9(D?fc?5)Bd_br47>i(!~LeFh*?*+ZWzm!_G{uTL>z#NJ_Pgn_k$8nFvkUg#wsGe3$VexCVH)~f58zO~ttFy3U_SkwsA-n!zy=h%MgSdi@*`D{URS54~ zv2I__b}bey4c;nMmRUpZScs{PWVcdCL8V_3^(^ z$>pf@A?Y#+xC{E$RE#74*1!Yi(E>8wOx+X6^1p`5CLO6tdN zkOYAIE!zI|4XsPN#N%W=x|4WPH;9SEwQ07&Cv2Ql0e`&Qq`D5fn~0l<(}IbcGb?6+ zaFHibf7*7^sBd~g`lM3Nv=Lj;gqQ@IjjfokX24c7&RLUNv64#)%c}s6LHuim=)7gq zGiOO2Ilvqc=2Ur-Mti=%U*S|zJt?9`nPi1rn@-6osi-uUFwml+o@s+9Jf zj3U6M2iOPEH9UA3tVHeZI86^bn0s4x6MWiR>y}=kie5Q8dUF#* zn-dYd2>eep;4uI`rZL#IuDN%vy7xug`+lPNcZPQi|IYG`<<65)_sK|R<2*m#xe$tF znvklrVW~4x+CFcA2AH!bfft@$Zi*J{Q}Dvm%k?YP-J9^jZ|~Q0Ii(+QI@?yzBe~uD zf6XemUb9fR_;fUDYs|no3P7CX*DV=fkRhD8C1ydwf4-l^KY^ z+PNQDYY(()f7ITkJ-AZ~`mGux#1u|_!JwodFoS}?2wBKltPKgjWWY&HE`8~`13))wo!hNK^Xvx+{!Pa;4 zduFXMp&*X%$R~tHeoS%O6twrdk9KkeM|p;QujaH18<>d%O?MR4J)W6J-c=FVSxY*e zO-$U#Q7ArQIh31n8tRIUfd+76Z0F|DNaj;N@t3&rP0U5gj-YKg)%ZAaAWh;N)D);h zfeJ@5H8-_Tx$s%=B#Y${d)2DFIbv^Kx)`;$hmGxw8c07DUqA`J&sad4H_m6!38)J2 zMGf8Ee4Zy11d}|H15A${NLhB+Q z&8(Q!t=qHaw|{ABp=<7ickSmEtC#rK>sBjTBNeU7N1_$G5qCTH_GjNIxN|{Ybjd)1{E2;L6C@EoTZ9|*TJ0@fip2H#xK<|SDF3*u1K;JthxMAe> zSN}rBvCMHLGU%QS{)Qq1gnZyw7=J>?T5L5*^AsXVPFZ7mMKO>TnndRlFdsx0fQYD2 z?)qq9_fzF(Tqo*Hk0#$r>yPBUKxeqNkeNez{j);+{`#uD zEMhNP6r%R(Sz!(L*Ks-LOS@Msu8768n73l7TPrAD=v%n3V4CY$gHp0*&hw>bR&5m# zTgBq;726j2d}g74!Tjjw_7z)0O76P_#j`zk?F9=*!uHB{?H!Ag%Ll`?9bsdK0#wk# z#G^;36@T}z!?B_jdmc&Mvm^kSODoQJoJb#s$e#kum};&r4Sk+&C6nq@g4yMR)IRp4 zH6NOWeopQq97i7nCy}6_)si6kPwWgqcrG2nZe>I(j)bjc>)E+CI<9xz*n531j3cdNx36UutYz1%X17GLTb6b4h^*?U zwfaLFiuoPeNnh;RcR<6Zhm`x#o>YD{EvJs(LcfRtD=V_VZY$s?Q9w3(={jY6&>z9|b{b&_ z*T!L5j^X)Qbt*TDQ#U0%Pm@4gAbv!^UZC9UX^Gfdma*}-hmG6W7KAlG9H8%COT2#MD2{RGa}`OP~x1byT| zAo}4<_jswot_S3y{Jidoo6nginKupl{Rg1SJP#c&Bt6<_bVK2(k75j zs-?cuhNIu>9Ng(J$-BZU*Ct&J2|?p62m)A1H!VY zr?>A+*YV?rPP3li`l(rZiaKjLedx)vhx*PO>Sot>WW8c00;9sf%MPSTG19ZzgK3&fru0J7drSB6|kRfhV90B~w2H7WPkdCdvBb4e1a!7$rvXWCnP60V2R)_UK zAk|@<)=0DlB@(;JTHIBZXgf<4Zc1zoZ)J!#e;YOo7I&SHRSM{1^>7^|Cb{W>cF2rT;sXOG`#EXY(VfK_~hzOk9+W z5cY#%p71k0Rz|VH_QCMjg$uBeVSLUf4dU`N>=4rL0MT+{_jF}}^_MuSAH+ z_+6kuSOk;S=<8eL{Ft0(IMi4OI-X}hC?g)m59i=_s3b+<)C3*=P7kqx+$ zJQRQ}#{r7+Q54i#eWGIMPzy5{^dlxdG%#+_hnY&0{((}{iG{3ZKP&({ii!^_ILuB} z{XS(w5#m?m0mMy87ILiQ*vP@J_F+!wG>#5mn2~yAZROC{0&*(I*+Nb$9HQ=FW>UuQ z5vXWPcBG0@mCugNj8s4>XSpd`Kx(IO7PGjj${&<`@tfKkA{q0$DK#qw5))dT@t>@c zc2S|53Q(#eLSNd4&r&y~(JQ|trEn{Tz(jvPjHk**g7nl1nIVT%6DPQ24so{hJ`Hz% zie32)_sN|}7=;C}!4|z17APhB&>t=cFd2_J-btcDIrmC^ru{SP!&$0&~Qk^*wxMqFSY9GB}e zu0Q74uRIDdl(7*qOLR}_|HfaS7jWi(LFTZHE395T4~**E9ue|po|wJ#fuVTeQ!9p= zwX6dAVTw7!T_rGUfONfO>r4*}v3z4<=Ez!6$xIJu^SuZ8548qe`MRSh#{Aj_#c%Iq zKD5*y=RYb5B`kR(m3w}}AtqL+(Y3$YPb1X+Mn4TuJM54e9qYE@sI4@{Sxnvh zV$Hh4x$3BkIO?K~h7UQjt(#xZELhE~jbzp?8CNp5zF)jGTHN{(@^$m`-Rq@wtEJl` zrQ4Uq+f{eOmD0ZXBXWM}`<2_4+oF{_7L4m<<*!}2dF8biZoaT|X{D?qT+k6N>sW7a zuNBuV)vY#kMjASQuL)qqAQKz zMqlZxeK>H_mA-|WS@7QO`N5?-!W}WZ_jIJK?>;S^N98qJ@)Dc1_sy1ix%wvlgvd%OlqE z#fH_2wn#-=w4!~rVo#)E54LZ`;T0<*IG^B?wqnaxjP+#b)v*roH*EaAR(!K~ab)@F zsIvhG>1tM8B&%+zAeyyp=Gb~x-q!** zrmjydHm+9eh*a!|R&+-5cSW;y&m8-}QoUx(Udt-NslDRfx9F|GJdst(YbVt4F5WI&cei1 zhGHCd#o4rUChFX_Vr+e2LJpSgK|n(bS=n8`MJ_rJ0QYUq__-!B_V@Vx; zf@d!d*7=7Is?>rW2A&4e`uqIc^EukTZ|Xd6(EYTws}9~jWEv6mZc*0xa&5G&<-A+_ zUITxAmv&9)^cc188+7L#ruVIs=KUPq`BKCC<+}5YhWFjN^E(Xh@50X{t8tyr*!%$A*33G~x% zkm$~$NSXIbbovy(qUlnwwu5>KA_&&l;`FU_6^U*W|8xkgU^0oAAn3N>a^`Q>EjT~X z=(eoolq`&eOWaErmK}ea_uaf({CAzpmT=bYS;Klp*`gtuQMcs74Z(jV$h_rW0>vol z3;ZtrB^>0=!k;tk(0--7Rr}Yv9ok#kUHpc7Uc|JJ$o3v-Zfw-}5@EPT6*EEi)0kv` zljdqkk2ZO!k`8p~K3__W)HJ$5qWDjs*D+0uXQSa~#8bUV$x5d}5~K2I8q<>c{8Ye%S`svz zGPkz47)z0TNb|`3_cX5;kreb;4rg}E7cL1aCfu~O=rWdp5=$3e#7`1oi<&alVUsS` zxmH-TR#d%~UjnmwYt=g`Tv+*Qhsl=lK#N4!#h{=l_X7=HW0aA$c0BT7pbT~WskO_Z z{qvlCy1z0a@Izgf(e#dBK=2(4cxXdCBpqBP!~Qz;9TmqUrcMdg-mRRM1f(KLQIIYL$;Ol$ z!5a!l-U4S@wv3&R7eFSZ>6cm|weR=GZ_hGtk)kDCTh`Ds2y z#2PW&V~?NK-778O zEBKE+xDC#)*=r5QSN{{&`g3mk&$*rd#I^sN+x0*r@ETkyMh-K_pW=VP?fM0G z=oj3%D0l9GM#pRJad3WZ<@jyC)Mox%D7!D{b&lER@8g!fBbLRPi)QmzO@$Ft;lhrn zscJ_5OIyzTGmEVtn`U5E3>?hj!KK`#!1BSR=ie*~7wm{+?VJIfE%4>@*`Q4L@&TRpFZ<< z+x>XIYtNoPx@ZpPG(_x;F&$z+4`%>v%z0x5dNbm{Z2sWfWXwcwW_*iTC}idI=Gm5r zuJE3XUib4ydA@8>Txwm`F70@IG{({Ut(;r^-*et$;RhZ~HGdLxAGr@s@g{!Tha8;y z1qR+6E^fY$HkxBOT7JtiIt1@l&OP$(TXlTf{L}ZbM%rS9Tz=77dG%UN!&+X^hmJmh zZ(B3lVpUN(yzHo`RPDqk4k3u9QZTNVQ`v}eoGWO!#!q~$2|RCsXWuw%!*Zf<5S ypTSxQ|88b>Oa~urM8gaeg6Sk@%tRrjg%na6NTHPW(W}xt3Q_CsXCuUpF8)8zn-H-8 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py new file mode 100644 index 0000000..bebe24e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/editable_legacy.py @@ -0,0 +1,46 @@ +"""Legacy editable installation process, i.e. `setup.py develop`. +""" +import logging +from typing import Optional, Sequence + +from pip._internal.build_env import BuildEnvironment +from pip._internal.utils.logging import indent_log +from pip._internal.utils.setuptools_build import make_setuptools_develop_args +from pip._internal.utils.subprocess import call_subprocess + +logger = logging.getLogger(__name__) + + +def install_editable( + *, + global_options: Sequence[str], + prefix: Optional[str], + home: Optional[str], + use_user_site: bool, + name: str, + setup_py_path: str, + isolated: bool, + build_env: BuildEnvironment, + unpacked_source_directory: str, +) -> None: + """Install a package in editable mode. Most arguments are pass-through + to setuptools. + """ + logger.info("Running setup.py develop for %s", name) + + args = make_setuptools_develop_args( + setup_py_path, + global_options=global_options, + no_user_config=isolated, + prefix=prefix, + home=home, + use_user_site=use_user_site, + ) + + with indent_log(): + with build_env: + call_subprocess( + args, + command_desc="python setup.py develop", + cwd=unpacked_source_directory, + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py new file mode 100644 index 0000000..f67180c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/install/wheel.py @@ -0,0 +1,734 @@ +"""Support for installing and building the "wheel" binary package format. +""" + +import collections +import compileall +import contextlib +import csv +import importlib +import logging +import os.path +import re +import shutil +import sys +import warnings +from base64 import urlsafe_b64encode +from email.message import Message +from itertools import chain, filterfalse, starmap +from typing import ( + IO, + TYPE_CHECKING, + Any, + BinaryIO, + Callable, + Dict, + Generator, + Iterable, + Iterator, + List, + NewType, + Optional, + Sequence, + Set, + Tuple, + Union, + cast, +) +from zipfile import ZipFile, ZipInfo + +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.distlib.util import get_export_entry +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_major_minor_version +from pip._internal.metadata import ( + BaseDistribution, + FilesystemWheel, + get_wheel_distribution, +) +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from pip._internal.models.scheme import SCHEME_KEYS, Scheme +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file, partition +from pip._internal.utils.unpacking import ( + current_umask, + is_within_directory, + set_extracted_file_to_default_mode_plus_executable, + zip_item_is_executable, +) +from pip._internal.utils.wheel import parse_wheel + +if TYPE_CHECKING: + from typing import Protocol + + class File(Protocol): + src_record_path: "RecordPath" + dest_path: str + changed: bool + + def save(self) -> None: + pass + + +logger = logging.getLogger(__name__) + +RecordPath = NewType("RecordPath", str) +InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] + + +def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]: + """Return (encoded_digest, length) for path using hashlib.sha256()""" + h, length = hash_file(path, blocksize) + digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=") + return (digest, str(length)) + + +def csv_io_kwargs(mode: str) -> Dict[str, Any]: + """Return keyword arguments to properly open a CSV file + in the given mode. + """ + return {"mode": mode, "newline": "", "encoding": "utf-8"} + + +def fix_script(path: str) -> bool: + """Replace #!python with #!/path/to/python + Return True if file was changed. + """ + # XXX RECORD hashes will need to be updated + assert os.path.isfile(path) + + with open(path, "rb") as script: + firstline = script.readline() + if not firstline.startswith(b"#!python"): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b"#!" + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, "wb") as script: + script.write(firstline) + script.write(rest) + return True + + +def wheel_root_is_purelib(metadata: Message) -> bool: + return metadata.get("Root-Is-Purelib", "").lower() == "true" + + +def get_entrypoints(dist: BaseDistribution) -> Tuple[Dict[str, str], Dict[str, str]]: + console_scripts = {} + gui_scripts = {} + for entry_point in dist.iter_entry_points(): + if entry_point.group == "console_scripts": + console_scripts[entry_point.name] = entry_point.value + elif entry_point.group == "gui_scripts": + gui_scripts[entry_point.name] = entry_point.value + return console_scripts, gui_scripts + + +def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]: + """Determine if any scripts are not on PATH and format a warning. + Returns a warning message if one or more scripts are not on PATH, + otherwise None. + """ + if not scripts: + return None + + # Group scripts by the path they were installed in + grouped_by_dir: Dict[str, Set[str]] = collections.defaultdict(set) + for destfile in scripts: + parent_dir = os.path.dirname(destfile) + script_name = os.path.basename(destfile) + grouped_by_dir[parent_dir].add(script_name) + + # We don't want to warn for directories that are on PATH. + not_warn_dirs = [ + os.path.normcase(os.path.normpath(i)).rstrip(os.sep) + for i in os.environ.get("PATH", "").split(os.pathsep) + ] + # If an executable sits with sys.executable, we don't warn for it. + # This covers the case of venv invocations without activating the venv. + not_warn_dirs.append( + os.path.normcase(os.path.normpath(os.path.dirname(sys.executable))) + ) + warn_for: Dict[str, Set[str]] = { + parent_dir: scripts + for parent_dir, scripts in grouped_by_dir.items() + if os.path.normcase(os.path.normpath(parent_dir)) not in not_warn_dirs + } + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, dir_scripts in warn_for.items(): + sorted_scripts: List[str] = sorted(dir_scripts) + if len(sorted_scripts) == 1: + start_text = f"script {sorted_scripts[0]} is" + else: + start_text = "scripts {} are".format( + ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1] + ) + + msg_lines.append( + f"The {start_text} installed in '{parent_dir}' which is not on PATH." + ) + + last_line_fmt = ( + "Consider adding {} to PATH or, if you prefer " + "to suppress this warning, use --no-warn-script-location." + ) + if len(msg_lines) == 1: + msg_lines.append(last_line_fmt.format("this directory")) + else: + msg_lines.append(last_line_fmt.format("these directories")) + + # Add a note if any directory starts with ~ + warn_for_tilde = any( + i[0] == "~" for i in os.environ.get("PATH", "").split(os.pathsep) if i + ) + if warn_for_tilde: + tilde_warning_msg = ( + "NOTE: The current PATH contains path(s) starting with `~`, " + "which may not be expanded by all applications." + ) + msg_lines.append(tilde_warning_msg) + + # Returns the formatted multiline message + return "\n".join(msg_lines) + + +def _normalized_outrows( + outrows: Iterable[InstalledCSVRow], +) -> List[Tuple[str, str, str]]: + """Normalize the given rows of a RECORD file. + + Items in each row are converted into str. Rows are then sorted to make + the value more predictable for tests. + + Each row is a 3-tuple (path, hash, size) and corresponds to a record of + a RECORD file (see PEP 376 and PEP 427 for details). For the rows + passed to this function, the size can be an integer as an int or string, + or the empty string. + """ + # Normally, there should only be one row per path, in which case the + # second and third elements don't come into play when sorting. + # However, in cases in the wild where a path might happen to occur twice, + # we don't want the sort operation to trigger an error (but still want + # determinism). Since the third element can be an int or string, we + # coerce each element to a string to avoid a TypeError in this case. + # For additional background, see-- + # https://github.com/pypa/pip/issues/5868 + return sorted( + (record_path, hash_, str(size)) for record_path, hash_, size in outrows + ) + + +def _record_to_fs_path(record_path: RecordPath, lib_dir: str) -> str: + return os.path.join(lib_dir, record_path) + + +def _fs_to_record_path(path: str, lib_dir: str) -> RecordPath: + # On Windows, do not handle relative paths if they belong to different + # logical disks + if os.path.splitdrive(path)[0].lower() == os.path.splitdrive(lib_dir)[0].lower(): + path = os.path.relpath(path, lib_dir) + + path = path.replace(os.path.sep, "/") + return cast("RecordPath", path) + + +def get_csv_rows_for_installed( + old_csv_rows: List[List[str]], + installed: Dict[RecordPath, RecordPath], + changed: Set[RecordPath], + generated: List[str], + lib_dir: str, +) -> List[InstalledCSVRow]: + """ + :param installed: A map from archive RECORD path to installation RECORD + path. + """ + installed_rows: List[InstalledCSVRow] = [] + for row in old_csv_rows: + if len(row) > 3: + logger.warning("RECORD line has more than three elements: %s", row) + old_record_path = cast("RecordPath", row[0]) + new_record_path = installed.pop(old_record_path, old_record_path) + if new_record_path in changed: + digest, length = rehash(_record_to_fs_path(new_record_path, lib_dir)) + else: + digest = row[1] if len(row) > 1 else "" + length = row[2] if len(row) > 2 else "" + installed_rows.append((new_record_path, digest, length)) + for f in generated: + path = _fs_to_record_path(f, lib_dir) + digest, length = rehash(f) + installed_rows.append((path, digest, length)) + return installed_rows + [ + (installed_record_path, "", "") for installed_record_path in installed.values() + ] + + +def get_console_script_specs(console: Dict[str, str]) -> List[str]: + """ + Given the mapping from entrypoint name to callable, return the relevant + console script specs. + """ + # Don't mutate caller's version + console = console.copy() + + scripts_to_generate = [] + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop("pip", None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append("pip = " + pip_script) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + scripts_to_generate.append(f"pip{sys.version_info[0]} = {pip_script}") + + scripts_to_generate.append(f"pip{get_major_minor_version()} = {pip_script}") + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r"pip(\d+(\.\d+)?)?$", k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop("easy_install", None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append("easy_install = " + easy_install_script) + + scripts_to_generate.append( + f"easy_install-{get_major_minor_version()} = {easy_install_script}" + ) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r"easy_install(-\d+\.\d+)?$", k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console entry points specified in the wheel + scripts_to_generate.extend(starmap("{} = {}".format, console.items())) + + return scripts_to_generate + + +class ZipBackedFile: + def __init__( + self, src_record_path: RecordPath, dest_path: str, zip_file: ZipFile + ) -> None: + self.src_record_path = src_record_path + self.dest_path = dest_path + self._zip_file = zip_file + self.changed = False + + def _getinfo(self) -> ZipInfo: + return self._zip_file.getinfo(self.src_record_path) + + def save(self) -> None: + # directory creation is lazy and after file filtering + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + parent_dir = os.path.dirname(self.dest_path) + ensure_dir(parent_dir) + + # When we open the output file below, any existing file is truncated + # before we start writing the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(self.dest_path): + os.unlink(self.dest_path) + + zipinfo = self._getinfo() + + with self._zip_file.open(zipinfo) as f: + with open(self.dest_path, "wb") as dest: + shutil.copyfileobj(f, dest) + + if zip_item_is_executable(zipinfo): + set_extracted_file_to_default_mode_plus_executable(self.dest_path) + + +class ScriptFile: + def __init__(self, file: "File") -> None: + self._file = file + self.src_record_path = self._file.src_record_path + self.dest_path = self._file.dest_path + self.changed = False + + def save(self) -> None: + self._file.save() + self.changed = fix_script(self.dest_path) + + +class MissingCallableSuffix(InstallationError): + def __init__(self, entry_point: str) -> None: + super().__init__( + f"Invalid script entry point: {entry_point} - A callable " + "suffix is required. Cf https://packaging.python.org/" + "specifications/entry-points/#use-for-scripts for more " + "information." + ) + + +def _raise_for_invalid_entrypoint(specification: str) -> None: + entry = get_export_entry(specification) + if entry is not None and entry.suffix is None: + raise MissingCallableSuffix(str(entry)) + + +class PipScriptMaker(ScriptMaker): + def make( + self, specification: str, options: Optional[Dict[str, Any]] = None + ) -> List[str]: + _raise_for_invalid_entrypoint(specification) + return super().make(specification, options) + + +def _install_wheel( + name: str, + wheel_zip: ZipFile, + wheel_path: str, + scheme: Scheme, + pycompile: bool = True, + warn_script_location: bool = True, + direct_url: Optional[DirectUrl] = None, + requested: bool = False, +) -> None: + """Install a wheel. + + :param name: Name of the project to install + :param wheel_zip: open ZipFile for wheel being installed + :param scheme: Distutils scheme dictating the install directories + :param req_description: String used in place of the requirement, for + logging + :param pycompile: Whether to byte-compile installed Python files + :param warn_script_location: Whether to check that scripts are installed + into a directory on PATH + :raises UnsupportedWheel: + * when the directory holds an unpacked wheel with incompatible + Wheel-Version + * when the .dist-info dir does not match the wheel + """ + info_dir, metadata = parse_wheel(wheel_zip, name) + + if wheel_root_is_purelib(metadata): + lib_dir = scheme.purelib + else: + lib_dir = scheme.platlib + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed: Dict[RecordPath, RecordPath] = {} + changed: Set[RecordPath] = set() + generated: List[str] = [] + + def record_installed( + srcfile: RecordPath, destfile: str, modified: bool = False + ) -> None: + """Map archive RECORD paths to installation RECORD paths.""" + newpath = _fs_to_record_path(destfile, lib_dir) + installed[srcfile] = newpath + if modified: + changed.add(newpath) + + def is_dir_path(path: RecordPath) -> bool: + return path.endswith("/") + + def assert_no_path_traversal(dest_dir_path: str, target_path: str) -> None: + if not is_within_directory(dest_dir_path, target_path): + message = ( + "The wheel {!r} has a file {!r} trying to install" + " outside the target directory {!r}" + ) + raise InstallationError( + message.format(wheel_path, target_path, dest_dir_path) + ) + + def root_scheme_file_maker( + zip_file: ZipFile, dest: str + ) -> Callable[[RecordPath], "File"]: + def make_root_scheme_file(record_path: RecordPath) -> "File": + normed_path = os.path.normpath(record_path) + dest_path = os.path.join(dest, normed_path) + assert_no_path_traversal(dest, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_root_scheme_file + + def data_scheme_file_maker( + zip_file: ZipFile, scheme: Scheme + ) -> Callable[[RecordPath], "File"]: + scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS} + + def make_data_scheme_file(record_path: RecordPath) -> "File": + normed_path = os.path.normpath(record_path) + try: + _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2) + except ValueError: + message = ( + "Unexpected file in {}: {!r}. .data directory contents" + " should be named like: '/'." + ).format(wheel_path, record_path) + raise InstallationError(message) + + try: + scheme_path = scheme_paths[scheme_key] + except KeyError: + valid_scheme_keys = ", ".join(sorted(scheme_paths)) + message = ( + "Unknown scheme key used in {}: {} (for file {!r}). .data" + " directory contents should be in subdirectories named" + " with a valid scheme key ({})" + ).format(wheel_path, scheme_key, record_path, valid_scheme_keys) + raise InstallationError(message) + + dest_path = os.path.join(scheme_path, dest_subpath) + assert_no_path_traversal(scheme_path, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_data_scheme_file + + def is_data_scheme_path(path: RecordPath) -> bool: + return path.split("/", 1)[0].endswith(".data") + + paths = cast(List[RecordPath], wheel_zip.namelist()) + file_paths = filterfalse(is_dir_path, paths) + root_scheme_paths, data_scheme_paths = partition(is_data_scheme_path, file_paths) + + make_root_scheme_file = root_scheme_file_maker(wheel_zip, lib_dir) + files: Iterator[File] = map(make_root_scheme_file, root_scheme_paths) + + def is_script_scheme_path(path: RecordPath) -> bool: + parts = path.split("/", 2) + return len(parts) > 2 and parts[0].endswith(".data") and parts[1] == "scripts" + + other_scheme_paths, script_scheme_paths = partition( + is_script_scheme_path, data_scheme_paths + ) + + make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme) + other_scheme_files = map(make_data_scheme_file, other_scheme_paths) + files = chain(files, other_scheme_files) + + # Get the defined entry points + distribution = get_wheel_distribution( + FilesystemWheel(wheel_path), + canonicalize_name(name), + ) + console, gui = get_entrypoints(distribution) + + def is_entrypoint_wrapper(file: "File") -> bool: + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + path = file.dest_path + name = os.path.basename(path) + if name.lower().endswith(".exe"): + matchname = name[:-4] + elif name.lower().endswith("-script.py"): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return matchname in console or matchname in gui + + script_scheme_files: Iterator[File] = map( + make_data_scheme_file, script_scheme_paths + ) + script_scheme_files = filterfalse(is_entrypoint_wrapper, script_scheme_files) + script_scheme_files = map(ScriptFile, script_scheme_files) + files = chain(files, script_scheme_files) + + for file in files: + file.save() + record_installed(file.src_record_path, file.dest_path, file.changed) + + def pyc_source_file_paths() -> Generator[str, None, None]: + # We de-duplicate installation paths, since there can be overlap (e.g. + # file in .data maps to same location as file in wheel root). + # Sorting installation paths makes it easier to reproduce and debug + # issues related to permissions on existing files. + for installed_path in sorted(set(installed.values())): + full_installed_path = os.path.join(lib_dir, installed_path) + if not os.path.isfile(full_installed_path): + continue + if not full_installed_path.endswith(".py"): + continue + yield full_installed_path + + def pyc_output_path(path: str) -> str: + """Return the path the pyc file would have been written to.""" + return importlib.util.cache_from_source(path) + + # Compile all of the pyc files for the installed files + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + for path in pyc_source_file_paths(): + success = compileall.compile_file(path, force=True, quiet=True) + if success: + pyc_path = pyc_output_path(path) + assert os.path.exists(pyc_path) + pyc_record_path = cast( + "RecordPath", pyc_path.replace(os.path.sep, "/") + ) + record_installed(pyc_record_path, pyc_path) + logger.debug(stdout.getvalue()) + + maker = PipScriptMaker(None, scheme.scripts) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = {""} + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Generate the console and GUI entry points specified in the wheel + scripts_to_generate = get_console_script_specs(console) + + gui_scripts_to_generate = list(starmap("{} = {}".format, gui.items())) + + generated_console_scripts = maker.make_multiple(scripts_to_generate) + generated.extend(generated_console_scripts) + + generated.extend(maker.make_multiple(gui_scripts_to_generate, {"gui": True})) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + generated_file_mode = 0o666 & ~current_umask() + + @contextlib.contextmanager + def _generate_file(path: str, **kwargs: Any) -> Generator[BinaryIO, None, None]: + with adjacent_tmp_file(path, **kwargs) as f: + yield f + os.chmod(f.name, generated_file_mode) + replace(f.name, path) + + dest_info_dir = os.path.join(lib_dir, info_dir) + + # Record pip as the installer + installer_path = os.path.join(dest_info_dir, "INSTALLER") + with _generate_file(installer_path) as installer_file: + installer_file.write(b"pip\n") + generated.append(installer_path) + + # Record the PEP 610 direct URL reference + if direct_url is not None: + direct_url_path = os.path.join(dest_info_dir, DIRECT_URL_METADATA_NAME) + with _generate_file(direct_url_path) as direct_url_file: + direct_url_file.write(direct_url.to_json().encode("utf-8")) + generated.append(direct_url_path) + + # Record the REQUESTED file + if requested: + requested_path = os.path.join(dest_info_dir, "REQUESTED") + with open(requested_path, "wb"): + pass + generated.append(requested_path) + + record_text = distribution.read_text("RECORD") + record_rows = list(csv.reader(record_text.splitlines())) + + rows = get_csv_rows_for_installed( + record_rows, + installed=installed, + changed=changed, + generated=generated, + lib_dir=lib_dir, + ) + + # Record details of all files installed + record_path = os.path.join(dest_info_dir, "RECORD") + + with _generate_file(record_path, **csv_io_kwargs("w")) as record_file: + # Explicitly cast to typing.IO[str] as a workaround for the mypy error: + # "writer" has incompatible type "BinaryIO"; expected "_Writer" + writer = csv.writer(cast("IO[str]", record_file)) + writer.writerows(_normalized_outrows(rows)) + + +@contextlib.contextmanager +def req_error_context(req_description: str) -> Generator[None, None, None]: + try: + yield + except InstallationError as e: + message = f"For req: {req_description}. {e.args[0]}" + raise InstallationError(message) from e + + +def install_wheel( + name: str, + wheel_path: str, + scheme: Scheme, + req_description: str, + pycompile: bool = True, + warn_script_location: bool = True, + direct_url: Optional[DirectUrl] = None, + requested: bool = False, +) -> None: + with ZipFile(wheel_path, allowZip64=True) as z: + with req_error_context(req_description): + _install_wheel( + name=name, + wheel_zip=z, + wheel_path=wheel_path, + scheme=scheme, + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=requested, + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py b/.venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000..956717d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,730 @@ +"""Prepares a distribution for installation +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import mimetypes +import os +import shutil +from pathlib import Path +from typing import Dict, Iterable, List, Optional + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.distributions import make_distribution_for_install_requirement +from pip._internal.distributions.installed import InstalledDistribution +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, + HashMismatch, + HashUnpinned, + InstallationError, + MetadataInconsistent, + NetworkConnectionError, + VcsHashUnsupported, +) +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution, get_metadata_distribution +from pip._internal.models.direct_url import ArchiveInfo +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.network.download import BatchDownloader, Downloader +from pip._internal.network.lazy_wheel import ( + HTTPRangeRequestUnsupported, + dist_from_wheel_url, +) +from pip._internal.network.session import PipSession +from pip._internal.operations.build.build_tracker import BuildTracker +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils._log import getLogger +from pip._internal.utils.direct_url_helpers import ( + direct_url_for_editable, + direct_url_from_link, +) +from pip._internal.utils.hashes import Hashes, MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, + hash_file, + hide_url, + redact_auth_from_requirement, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.unpacking import unpack_file +from pip._internal.vcs import vcs + +logger = getLogger(__name__) + + +def _get_prepared_distribution( + req: InstallRequirement, + build_tracker: BuildTracker, + finder: PackageFinder, + build_isolation: bool, + check_build_deps: bool, +) -> BaseDistribution: + """Prepare a distribution for installation.""" + abstract_dist = make_distribution_for_install_requirement(req) + tracker_id = abstract_dist.build_tracker_id + if tracker_id is not None: + with build_tracker.track(req, tracker_id): + abstract_dist.prepare_distribution_metadata( + finder, build_isolation, check_build_deps + ) + return abstract_dist.get_metadata_distribution() + + +def unpack_vcs_link(link: Link, location: str, verbosity: int) -> None: + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend is not None + vcs_backend.unpack(location, url=hide_url(link.url), verbosity=verbosity) + + +class File: + def __init__(self, path: str, content_type: Optional[str]) -> None: + self.path = path + if content_type is None: + self.content_type = mimetypes.guess_type(path)[0] + else: + self.content_type = content_type + + +def get_http_url( + link: Link, + download: Downloader, + download_dir: Optional[str] = None, + hashes: Optional[Hashes] = None, +) -> File: + temp_dir = TempDirectory(kind="unpack", globally_managed=True) + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, download_dir, hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = None + else: + # let's download to a tmp dir + from_path, content_type = download(link, temp_dir.path) + if hashes: + hashes.check_against_path(from_path) + + return File(from_path, content_type) + + +def get_file_url( + link: Link, download_dir: Optional[str] = None, hashes: Optional[Hashes] = None +) -> File: + """Get file and optionally check its hash.""" + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir(link, download_dir, hashes) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link.file_path + + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(from_path) + return File(from_path, None) + + +def unpack_url( + link: Link, + location: str, + download: Downloader, + verbosity: int, + download_dir: Optional[str] = None, + hashes: Optional[Hashes] = None, +) -> Optional[File]: + """Unpack link into location, downloading if required. + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if link.is_vcs: + unpack_vcs_link(link, location, verbosity=verbosity) + return None + + assert not link.is_existing_dir() + + # file urls + if link.is_file: + file = get_file_url(link, download_dir, hashes=hashes) + + # http urls + else: + file = get_http_url( + link, + download, + download_dir, + hashes=hashes, + ) + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies, except wheels + if not link.is_wheel: + unpack_file(file.path, location, file.content_type) + + return file + + +def _check_download_dir( + link: Link, + download_dir: str, + hashes: Optional[Hashes], + warn_on_hash_mismatch: bool = True, +) -> Optional[str]: + """Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + + if not os.path.exists(download_path): + return None + + # If already downloaded, does its hash match? + logger.info("File was already downloaded %s", download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + if warn_on_hash_mismatch: + logger.warning( + "Previously-downloaded file %s has bad hash. Re-downloading.", + download_path, + ) + os.unlink(download_path) + return None + return download_path + + +class RequirementPreparer: + """Prepares a Requirement""" + + def __init__( + self, + build_dir: str, + download_dir: Optional[str], + src_dir: str, + build_isolation: bool, + check_build_deps: bool, + build_tracker: BuildTracker, + session: PipSession, + progress_bar: str, + finder: PackageFinder, + require_hashes: bool, + use_user_site: bool, + lazy_wheel: bool, + verbosity: int, + legacy_resolver: bool, + ) -> None: + super().__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.build_tracker = build_tracker + self._session = session + self._download = Downloader(session, progress_bar) + self._batch_download = BatchDownloader(session, progress_bar) + self.finder = finder + + # Where still-packed archives should be written to. If None, they are + # not saved, and are deleted immediately after unpacking. + self.download_dir = download_dir + + # Is build isolation allowed? + self.build_isolation = build_isolation + + # Should check build dependencies? + self.check_build_deps = check_build_deps + + # Should hash-checking be required? + self.require_hashes = require_hashes + + # Should install in user site-packages? + self.use_user_site = use_user_site + + # Should wheels be downloaded lazily? + self.use_lazy_wheel = lazy_wheel + + # How verbose should underlying tooling be? + self.verbosity = verbosity + + # Are we using the legacy resolver? + self.legacy_resolver = legacy_resolver + + # Memoized downloaded files, as mapping of url: path. + self._downloaded: Dict[str, str] = {} + + # Previous "header" printed for a link-based InstallRequirement + self._previous_requirement_header = ("", "") + + def _log_preparing_link(self, req: InstallRequirement) -> None: + """Provide context for the requirement being prepared.""" + if req.link.is_file and not req.is_wheel_from_cache: + message = "Processing %s" + information = str(display_path(req.link.file_path)) + else: + message = "Collecting %s" + information = redact_auth_from_requirement(req.req) if req.req else str(req) + + # If we used req.req, inject requirement source if available (this + # would already be included if we used req directly) + if req.req and req.comes_from: + if isinstance(req.comes_from, str): + comes_from: Optional[str] = req.comes_from + else: + comes_from = req.comes_from.from_path() + if comes_from: + information += f" (from {comes_from})" + + if (message, information) != self._previous_requirement_header: + self._previous_requirement_header = (message, information) + logger.info(message, information) + + if req.is_wheel_from_cache: + with indent_log(): + logger.info("Using cached %s", req.link.filename) + + def _ensure_link_req_src_dir( + self, req: InstallRequirement, parallel_builds: bool + ) -> None: + """Ensure source_dir of a linked InstallRequirement.""" + # Since source_dir is only set for editable requirements. + if req.link.is_wheel: + # We don't need to unpack wheels, so no need for a source + # directory. + return + assert req.source_dir is None + if req.link.is_existing_dir(): + # build local directories in-tree + req.source_dir = req.link.file_path + return + + # We always delete unpacked sdists after pip runs. + req.ensure_has_source_dir( + self.build_dir, + autodelete=True, + parallel_builds=parallel_builds, + ) + req.ensure_pristine_source_checkout() + + def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes: + # By the time this is called, the requirement's link should have + # been checked so we can tell what kind of requirements req is + # and raise some more informative errors than otherwise. + # (For example, we can raise VcsHashUnsupported for a VCS URL + # rather than HashMissing.) + if not self.require_hashes: + return req.hashes(trust_internet=True) + + # We could check these first 2 conditions inside unpack_url + # and save repetition of conditions, but then we would + # report less-useful error messages for unhashable + # requirements, complaining that there's no hash provided. + if req.link.is_vcs: + raise VcsHashUnsupported() + if req.link.is_existing_dir(): + raise DirectoryUrlHashUnsupported() + + # Unpinned packages are asking for trouble when a new version + # is uploaded. This isn't a security check, but it saves users + # a surprising hash mismatch in the future. + # file:/// URLs aren't pinnable, so don't complain about them + # not being pinned. + if not req.is_direct and not req.is_pinned: + raise HashUnpinned() + + # If known-good hashes are missing for this requirement, + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + return req.hashes(trust_internet=False) or MissingHashes() + + def _fetch_metadata_only( + self, + req: InstallRequirement, + ) -> Optional[BaseDistribution]: + if self.legacy_resolver: + logger.debug( + "Metadata-only fetching is not used in the legacy resolver", + ) + return None + if self.require_hashes: + logger.debug( + "Metadata-only fetching is not used as hash checking is required", + ) + return None + # Try PEP 658 metadata first, then fall back to lazy wheel if unavailable. + return self._fetch_metadata_using_link_data_attr( + req + ) or self._fetch_metadata_using_lazy_wheel(req.link) + + def _fetch_metadata_using_link_data_attr( + self, + req: InstallRequirement, + ) -> Optional[BaseDistribution]: + """Fetch metadata from the data-dist-info-metadata attribute, if possible.""" + # (1) Get the link to the metadata file, if provided by the backend. + metadata_link = req.link.metadata_link() + if metadata_link is None: + return None + assert req.req is not None + logger.verbose( + "Obtaining dependency information for %s from %s", + req.req, + metadata_link, + ) + # (2) Download the contents of the METADATA file, separate from the dist itself. + metadata_file = get_http_url( + metadata_link, + self._download, + hashes=metadata_link.as_hashes(), + ) + with open(metadata_file.path, "rb") as f: + metadata_contents = f.read() + # (3) Generate a dist just from those file contents. + metadata_dist = get_metadata_distribution( + metadata_contents, + req.link.filename, + req.req.name, + ) + # (4) Ensure the Name: field from the METADATA file matches the name from the + # install requirement. + # + # NB: raw_name will fall back to the name from the install requirement if + # the Name: field is not present, but it's noted in the raw_name docstring + # that that should NEVER happen anyway. + if canonicalize_name(metadata_dist.raw_name) != canonicalize_name(req.req.name): + raise MetadataInconsistent( + req, "Name", req.req.name, metadata_dist.raw_name + ) + return metadata_dist + + def _fetch_metadata_using_lazy_wheel( + self, + link: Link, + ) -> Optional[BaseDistribution]: + """Fetch metadata using lazy wheel, if possible.""" + # --use-feature=fast-deps must be provided. + if not self.use_lazy_wheel: + return None + if link.is_file or not link.is_wheel: + logger.debug( + "Lazy wheel is not used as %r does not point to a remote wheel", + link, + ) + return None + + wheel = Wheel(link.filename) + name = canonicalize_name(wheel.name) + logger.info( + "Obtaining dependency information from %s %s", + name, + wheel.version, + ) + url = link.url.split("#", 1)[0] + try: + return dist_from_wheel_url(name, url, self._session) + except HTTPRangeRequestUnsupported: + logger.debug("%s does not support range requests", url) + return None + + def _complete_partial_requirements( + self, + partially_downloaded_reqs: Iterable[InstallRequirement], + parallel_builds: bool = False, + ) -> None: + """Download any requirements which were only fetched by metadata.""" + # Download to a temporary directory. These will be copied over as + # needed for downstream 'download', 'wheel', and 'install' commands. + temp_dir = TempDirectory(kind="unpack", globally_managed=True).path + + # Map each link to the requirement that owns it. This allows us to set + # `req.local_file_path` on the appropriate requirement after passing + # all the links at once into BatchDownloader. + links_to_fully_download: Dict[Link, InstallRequirement] = {} + for req in partially_downloaded_reqs: + assert req.link + links_to_fully_download[req.link] = req + + batch_download = self._batch_download( + links_to_fully_download.keys(), + temp_dir, + ) + for link, (filepath, _) in batch_download: + logger.debug("Downloading link %s to %s", link, filepath) + req = links_to_fully_download[link] + # Record the downloaded file path so wheel reqs can extract a Distribution + # in .get_dist(). + req.local_file_path = filepath + # Record that the file is downloaded so we don't do it again in + # _prepare_linked_requirement(). + self._downloaded[req.link.url] = filepath + + # If this is an sdist, we need to unpack it after downloading, but the + # .source_dir won't be set up until we are in _prepare_linked_requirement(). + # Add the downloaded archive to the install requirement to unpack after + # preparing the source dir. + if not req.is_wheel: + req.needs_unpacked_archive(Path(filepath)) + + # This step is necessary to ensure all lazy wheels are processed + # successfully by the 'download', 'wheel', and 'install' commands. + for req in partially_downloaded_reqs: + self._prepare_linked_requirement(req, parallel_builds) + + def prepare_linked_requirement( + self, req: InstallRequirement, parallel_builds: bool = False + ) -> BaseDistribution: + """Prepare a requirement to be obtained from req.link.""" + assert req.link + self._log_preparing_link(req) + with indent_log(): + # Check if the relevant file is already available + # in the download directory + file_path = None + if self.download_dir is not None and req.link.is_wheel: + hashes = self._get_linked_req_hashes(req) + file_path = _check_download_dir( + req.link, + self.download_dir, + hashes, + # When a locally built wheel has been found in cache, we don't warn + # about re-downloading when the already downloaded wheel hash does + # not match. This is because the hash must be checked against the + # original link, not the cached link. It that case the already + # downloaded file will be removed and re-fetched from cache (which + # implies a hash check against the cache entry's origin.json). + warn_on_hash_mismatch=not req.is_wheel_from_cache, + ) + + if file_path is not None: + # The file is already available, so mark it as downloaded + self._downloaded[req.link.url] = file_path + else: + # The file is not available, attempt to fetch only metadata + metadata_dist = self._fetch_metadata_only(req) + if metadata_dist is not None: + req.needs_more_preparation = True + return metadata_dist + + # None of the optimizations worked, fully prepare the requirement + return self._prepare_linked_requirement(req, parallel_builds) + + def prepare_linked_requirements_more( + self, reqs: Iterable[InstallRequirement], parallel_builds: bool = False + ) -> None: + """Prepare linked requirements more, if needed.""" + reqs = [req for req in reqs if req.needs_more_preparation] + for req in reqs: + # Determine if any of these requirements were already downloaded. + if self.download_dir is not None and req.link.is_wheel: + hashes = self._get_linked_req_hashes(req) + file_path = _check_download_dir(req.link, self.download_dir, hashes) + if file_path is not None: + self._downloaded[req.link.url] = file_path + req.needs_more_preparation = False + + # Prepare requirements we found were already downloaded for some + # reason. The other downloads will be completed separately. + partially_downloaded_reqs: List[InstallRequirement] = [] + for req in reqs: + if req.needs_more_preparation: + partially_downloaded_reqs.append(req) + else: + self._prepare_linked_requirement(req, parallel_builds) + + # TODO: separate this part out from RequirementPreparer when the v1 + # resolver can be removed! + self._complete_partial_requirements( + partially_downloaded_reqs, + parallel_builds=parallel_builds, + ) + + def _prepare_linked_requirement( + self, req: InstallRequirement, parallel_builds: bool + ) -> BaseDistribution: + assert req.link + link = req.link + + hashes = self._get_linked_req_hashes(req) + + if hashes and req.is_wheel_from_cache: + assert req.download_info is not None + assert link.is_wheel + assert link.is_file + # We need to verify hashes, and we have found the requirement in the cache + # of locally built wheels. + if ( + isinstance(req.download_info.info, ArchiveInfo) + and req.download_info.info.hashes + and hashes.has_one_of(req.download_info.info.hashes) + ): + # At this point we know the requirement was built from a hashable source + # artifact, and we verified that the cache entry's hash of the original + # artifact matches one of the hashes we expect. We don't verify hashes + # against the cached wheel, because the wheel is not the original. + hashes = None + else: + logger.warning( + "The hashes of the source archive found in cache entry " + "don't match, ignoring cached built wheel " + "and re-downloading source." + ) + req.link = req.cached_wheel_source_link + link = req.link + + self._ensure_link_req_src_dir(req, parallel_builds) + + if link.is_existing_dir(): + local_file = None + elif link.url not in self._downloaded: + try: + local_file = unpack_url( + link, + req.source_dir, + self._download, + self.verbosity, + self.download_dir, + hashes, + ) + except NetworkConnectionError as exc: + raise InstallationError( + f"Could not install requirement {req} because of HTTP " + f"error {exc} for URL {link}" + ) + else: + file_path = self._downloaded[link.url] + if hashes: + hashes.check_against_path(file_path) + local_file = File(file_path, content_type=None) + + # If download_info is set, we got it from the wheel cache. + if req.download_info is None: + # Editables don't go through this function (see + # prepare_editable_requirement). + assert not req.editable + req.download_info = direct_url_from_link(link, req.source_dir) + # Make sure we have a hash in download_info. If we got it as part of the + # URL, it will have been verified and we can rely on it. Otherwise we + # compute it from the downloaded file. + # FIXME: https://github.com/pypa/pip/issues/11943 + if ( + isinstance(req.download_info.info, ArchiveInfo) + and not req.download_info.info.hashes + and local_file + ): + hash = hash_file(local_file.path)[0].hexdigest() + # We populate info.hash for backward compatibility. + # This will automatically populate info.hashes. + req.download_info.info.hash = f"sha256={hash}" + + # For use in later processing, + # preserve the file path on the requirement. + if local_file: + req.local_file_path = local_file.path + + dist = _get_prepared_distribution( + req, + self.build_tracker, + self.finder, + self.build_isolation, + self.check_build_deps, + ) + return dist + + def save_linked_requirement(self, req: InstallRequirement) -> None: + assert self.download_dir is not None + assert req.link is not None + link = req.link + if link.is_vcs or (link.is_existing_dir() and req.editable): + # Make a .zip of the source_dir we already created. + req.archive(self.download_dir) + return + + if link.is_existing_dir(): + logger.debug( + "Not copying link to destination directory " + "since it is a directory: %s", + link, + ) + return + if req.local_file_path is None: + # No distribution was downloaded for this requirement. + return + + download_location = os.path.join(self.download_dir, link.filename) + if not os.path.exists(download_location): + shutil.copy(req.local_file_path, download_location) + download_path = display_path(download_location) + logger.info("Saved %s", download_path) + + def prepare_editable_requirement( + self, + req: InstallRequirement, + ) -> BaseDistribution: + """Prepare an editable requirement.""" + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info("Obtaining %s", req) + + with indent_log(): + if self.require_hashes: + raise InstallationError( + f"The editable requirement {req} cannot be installed when " + "requiring hashes, because there is no single file to " + "hash." + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable() + assert req.source_dir + req.download_info = direct_url_for_editable(req.unpacked_source_directory) + + dist = _get_prepared_distribution( + req, + self.build_tracker, + self.finder, + self.build_isolation, + self.check_build_deps, + ) + + req.check_if_exists(self.use_user_site) + + return dist + + def prepare_installed_requirement( + self, + req: InstallRequirement, + skip_reason: str, + ) -> BaseDistribution: + """Prepare an already-installed requirement.""" + assert req.satisfied_by, "req should have been satisfied but isn't" + assert skip_reason is not None, ( + "did not get skip reason skipped but req.satisfied_by " + f"is set to {req.satisfied_by}" + ) + logger.info( + "Requirement %s: %s (%s)", skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + if self.require_hashes: + logger.debug( + "Since it is already installed, we are trusting this " + "package without checking its hash. To ensure a " + "completely repeatable environment, install into an " + "empty virtualenv." + ) + return InstalledDistribution(req).get_metadata_distribution() diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/pyproject.py b/.venv/lib/python3.12/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000..8de36b8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,179 @@ +import importlib.util +import os +from collections import namedtuple +from typing import Any, List, Optional + +from pip._vendor import tomli +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement + +from pip._internal.exceptions import ( + InstallationError, + InvalidPyProjectBuildRequires, + MissingPyProjectBuildRequires, +) + + +def _is_list_of_str(obj: Any) -> bool: + return isinstance(obj, list) and all(isinstance(item, str) for item in obj) + + +def make_pyproject_path(unpacked_source_directory: str) -> str: + return os.path.join(unpacked_source_directory, "pyproject.toml") + + +BuildSystemDetails = namedtuple( + "BuildSystemDetails", ["requires", "backend", "check", "backend_path"] +) + + +def load_pyproject_toml( + use_pep517: Optional[bool], pyproject_toml: str, setup_py: str, req_name: str +) -> Optional[BuildSystemDetails]: + """Load the pyproject.toml file. + + Parameters: + use_pep517 - Has the user requested PEP 517 processing? None + means the user hasn't explicitly specified. + pyproject_toml - Location of the project's pyproject.toml file + setup_py - Location of the project's setup.py file + req_name - The name of the requirement we're processing (for + error reporting) + + Returns: + None if we should use the legacy code path, otherwise a tuple + ( + requirements from pyproject.toml, + name of PEP 517 backend, + requirements we should check are installed after setting + up the build environment + directory paths to import the backend from (backend-path), + relative to the project root. + ) + """ + has_pyproject = os.path.isfile(pyproject_toml) + has_setup = os.path.isfile(setup_py) + + if not has_pyproject and not has_setup: + raise InstallationError( + f"{req_name} does not appear to be a Python project: " + f"neither 'setup.py' nor 'pyproject.toml' found." + ) + + if has_pyproject: + with open(pyproject_toml, encoding="utf-8") as f: + pp_toml = tomli.loads(f.read()) + build_system = pp_toml.get("build-system") + else: + build_system = None + + # The following cases must use PEP 517 + # We check for use_pep517 being non-None and falsey because that means + # the user explicitly requested --no-use-pep517. The value 0 as + # opposed to False can occur when the value is provided via an + # environment variable or config file option (due to the quirk of + # strtobool() returning an integer in pip's configuration code). + if has_pyproject and not has_setup: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project does not have a setup.py" + ) + use_pep517 = True + elif build_system and "build-backend" in build_system: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project specifies a build backend of {} " + "in pyproject.toml".format(build_system["build-backend"]) + ) + use_pep517 = True + + # If we haven't worked out whether to use PEP 517 yet, + # and the user hasn't explicitly stated a preference, + # we do so if the project has a pyproject.toml file + # or if we cannot import setuptools or wheels. + + # We fallback to PEP 517 when without setuptools or without the wheel package, + # so setuptools can be installed as a default build backend. + # For more info see: + # https://discuss.python.org/t/pip-without-setuptools-could-the-experience-be-improved/11810/9 + # https://github.com/pypa/pip/issues/8559 + elif use_pep517 is None: + use_pep517 = ( + has_pyproject + or not importlib.util.find_spec("setuptools") + or not importlib.util.find_spec("wheel") + ) + + # At this point, we know whether we're going to use PEP 517. + assert use_pep517 is not None + + # If we're using the legacy code path, there is nothing further + # for us to do here. + if not use_pep517: + return None + + if build_system is None: + # Either the user has a pyproject.toml with no build-system + # section, or the user has no pyproject.toml, but has opted in + # explicitly via --use-pep517. + # In the absence of any explicit backend specification, we + # assume the setuptools backend that most closely emulates the + # traditional direct setup.py execution, and require wheel and + # a version of setuptools that supports that backend. + + build_system = { + "requires": ["setuptools>=40.8.0"], + "build-backend": "setuptools.build_meta:__legacy__", + } + + # If we're using PEP 517, we have build system information (either + # from pyproject.toml, or defaulted by the code above). + # Note that at this point, we do not know if the user has actually + # specified a backend, though. + assert build_system is not None + + # Ensure that the build-system section in pyproject.toml conforms + # to PEP 518. + + # Specifying the build-system table but not the requires key is invalid + if "requires" not in build_system: + raise MissingPyProjectBuildRequires(package=req_name) + + # Error out if requires is not a list of strings + requires = build_system["requires"] + if not _is_list_of_str(requires): + raise InvalidPyProjectBuildRequires( + package=req_name, + reason="It is not a list of strings.", + ) + + # Each requirement must be valid as per PEP 508 + for requirement in requires: + try: + Requirement(requirement) + except InvalidRequirement as error: + raise InvalidPyProjectBuildRequires( + package=req_name, + reason=f"It contains an invalid requirement: {requirement!r}", + ) from error + + backend = build_system.get("build-backend") + backend_path = build_system.get("backend-path", []) + check: List[str] = [] + if backend is None: + # If the user didn't specify a backend, we assume they want to use + # the setuptools backend. But we can't be sure they have included + # a version of setuptools which supplies the backend. So we + # make a note to check that this requirement is present once + # we have set up the environment. + # This is quite a lot of work to check for a very specific case. But + # the problem is, that case is potentially quite common - projects that + # adopted PEP 518 early for the ability to specify requirements to + # execute setup.py, but never considered needing to mention the build + # tools themselves. The original PEP 518 code had a similar check (but + # implemented in a different way). + backend = "setuptools.build_meta:__legacy__" + check = ["setuptools>=40.8.0"] + + return BuildSystemDetails(requires, backend, check, backend_path) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000..16de903 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,92 @@ +import collections +import logging +from typing import Generator, List, Optional, Sequence, Tuple + +from pip._internal.utils.logging import indent_log + +from .req_file import parse_requirements +from .req_install import InstallRequirement +from .req_set import RequirementSet + +__all__ = [ + "RequirementSet", + "InstallRequirement", + "parse_requirements", + "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +class InstallationResult: + def __init__(self, name: str) -> None: + self.name = name + + def __repr__(self) -> str: + return f"InstallationResult(name={self.name!r})" + + +def _validate_requirements( + requirements: List[InstallRequirement], +) -> Generator[Tuple[str, InstallRequirement], None, None]: + for req in requirements: + assert req.name, f"invalid to-be-installed requirement: {req}" + yield req.name, req + + +def install_given_reqs( + requirements: List[InstallRequirement], + global_options: Sequence[str], + root: Optional[str], + home: Optional[str], + prefix: Optional[str], + warn_script_location: bool, + use_user_site: bool, + pycompile: bool, +) -> List[InstallationResult]: + """ + Install everything in the given list. + + (to be called after having downloaded and unpacked the packages) + """ + to_install = collections.OrderedDict(_validate_requirements(requirements)) + + if to_install: + logger.info( + "Installing collected packages: %s", + ", ".join(to_install.keys()), + ) + + installed = [] + + with indent_log(): + for req_name, requirement in to_install.items(): + if requirement.should_reinstall: + logger.info("Attempting uninstall: %s", req_name) + with indent_log(): + uninstalled_pathset = requirement.uninstall(auto_confirm=True) + else: + uninstalled_pathset = None + + try: + requirement.install( + global_options, + root=root, + home=home, + prefix=prefix, + warn_script_location=warn_script_location, + use_user_site=use_user_site, + pycompile=pycompile, + ) + except Exception: + # if install did not succeed, rollback previous uninstall + if uninstalled_pathset and not requirement.install_succeeded: + uninstalled_pathset.rollback() + raise + else: + if uninstalled_pathset and requirement.install_succeeded: + uninstalled_pathset.commit() + + installed.append(InstallationResult(req_name)) + + return installed diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a31b32760553d6b65446e7da6cccc30c92d589f8 GIT binary patch literal 3764 zcmahLTWlN0aqq=9#fL~)wkVsntVkzYVw}K9Y{zk2*pBVmN@4jYM2Z8&TS>HTu<0HxHmlf5^yzh)aSxXah9p4{aH6>;81+$dhCx zX@|Voncdmlnc3Ny{Yx|&A^`V3=CwZn^*3ZZ=RFg z0jVibjlM1Bdp5{+Q6EjaQZXuZsQ4O{Sfi43i`ore3cd*JlycWgs%coScb1cTzD7mH zWyuM?LJi6aw#gjvjAq$R;MIz)nMOf(0u%JjDm9AKk8T6V#SP7J)@r7!e+;lhoSNlxgh(HE>a@ z2>ojycBs{1AeIOvc&d3`<*16427DAp6$TDWGZRs8!W%M>Gm$ z>i8J7tIQZLc8sdF0%@jt7sa0kSAikLPN*X7+U?rD3mCXWV0+0OcoMzCtNgpd1d)aDx!!MXIgHLd z{Fsv&UP_r`DQGO*C(F`+QDH_aOi{i=;r1Ygt%dTSa6-PZ`}guq`9}ZkLOu3EJ@5kC z0wZv`uoiBoC{9RG%BEV?k&Y{V}eTQs~o8o`TEpZ(A*@y_C`8(kvC-dWTgPL)n zplfQ@Hb*DvXy;l{HM>FEgW2}s@GJ{EI0D|bb_lnRs7G;^DA-TlZieRc!)Lt&=pW=? z3#8>E@yr9lOPNKX8B1Qd^pi{Vz=?X_%Xhl(NcF8JmIEgiFEzUoS7t8HTo)G0%YnZ8 z5=j5sLifCt+Xty(S~n*Px?;N7$8tnwns7()CON(eqf^=?Cv~yF48Nk12RzoM2_PXqGFQPKyCp+yl%pZdiW|CN2?sE#ymR@+Pn)ucJo1Jnpsgfm(n) zYx_Kj65gjxpmHi-!W}n3El`SX>H^j~V{ZqqN??_l584=IE_ibLA&Oun47E+*NkDx4 znvmDQviZ<_xE3y99@_-$#s6c2P+O~49Qh6{!X>;jn?M!clRR7IBdQO7>oys@>YI-Q z3224acN0LWs{ZmB(ct z8w5YFrw|Px!Ya>XL?eLYq!aG!1XbK}!ml!wGO8Zaing1S93Q+(rYXE+G-Jwi#FD8Q zj`#+hwHT%XM}quNww$;%V^(z)>Q6@=}#N7N!&ofi-BCHSA`HPytJY@LnJfx07W5N@BQ?7``hXxf5BI&n_p%7fyfa zPkbKex)Qz|Zbov=Nc_sF%criEmdtDBa?j9mWcZ;Z1VgP5Npvrq`g^eZr-PzlnBbZt`&UMeG)9j6`Fwrk$nwa!dTQvAfFn?H5&Ou{o_c(sB_^W#n(@As_&_5* z&`j-WZtcf^-_E9dxVbCW+&c)>&a%VotopINEx{-SNZt9t4JlzVy@Z(2A8`gMi9R_dzDSK2n-YdL6(0wfHfB(5K z&>!%<$M%XJY&!(>hgs>E9R6_7kMv&oc-sF_8s$Igm5%T7%Ujtftl{`EnBm$uK6|zT zEyMD~uwXJQPYf#%!}RF%!P{_b?QJ+#wKd%u>&$LPz8g+cP!rqFY>X){>fG12HbKx3HB)*n`n12488V=uAO zFo5~h`X`{4z;WDXB>5TX`YSp7DLMQVdFCrJ`~}(b1sQlCN@0G%-|8ZvzQrvo!EKG; zw(Bn}2X`;{9{D8hzb7Gucz$A~%vFEdjWDQrBYj>O_6p-g8lf$&$hzI mMM&R{Ruri&lIm^6kcyLds+B-0NqnJtDASPoA9e%pI^(~pw1)=( literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/constructors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51516624dd856a831c07f79b4f6ac9d01da9af5e GIT binary patch literal 21603 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_file.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09ca3647b4746dcecfc1774f685a6e91ec45b0f3 GIT binary patch literal 21482 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%0D_vzO0ZG9pgJY-~MpD{9H${kloCYab1 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 G!v6&#x^7wk literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..89cf33ca03c154eebae1e258a4ad585375120882 GIT binary patch literal 38435 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 Kx5CEup8p?Y1r3e> literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..161bd6119f622e9cc5077863ca27603d03de1859 GIT binary patch literal 7239 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..adcedbf02cd57b4186301142b2f922aa07a5cb95 GIT binary patch literal 32998 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py b/.venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000..7e2d0e5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,576 @@ +"""Backing implementation for InstallRequirement's various constructors + +The idea here is that these formed a major chunk of InstallRequirement's size +so, moving them and support code dedicated to them outside of that class +helps creates for better understandability for the rest of the code. + +These are meant to be used elsewhere within pip to create instances of +InstallRequirement. +""" + +import copy +import logging +import os +import re +from typing import Collection, Dict, List, Optional, Set, Tuple, Union + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.specifiers import Specifier + +from pip._internal.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.req.req_file import ParsedRequirement +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.filetypes import is_archive_file +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.packaging import get_requirement +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import is_url, vcs + +__all__ = [ + "install_req_from_editable", + "install_req_from_line", + "parse_editable", +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +def _strip_extras(path: str) -> Tuple[str, Optional[str]]: + m = re.match(r"^(.+)(\[[^\]]+\])$", path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +def convert_extras(extras: Optional[str]) -> Set[str]: + if not extras: + return set() + return get_requirement("placeholder" + extras.lower()).extras + + +def _set_requirement_extras(req: Requirement, new_extras: Set[str]) -> Requirement: + """ + Returns a new requirement based on the given one, with the supplied extras. If the + given requirement already has extras those are replaced (or dropped if no new extras + are given). + """ + match: Optional[re.Match[str]] = re.fullmatch( + # see https://peps.python.org/pep-0508/#complete-grammar + r"([\w\t .-]+)(\[[^\]]*\])?(.*)", + str(req), + flags=re.ASCII, + ) + # ireq.req is a valid requirement so the regex should always match + assert ( + match is not None + ), f"regex match on requirement {req} failed, this should never happen" + pre: Optional[str] = match.group(1) + post: Optional[str] = match.group(3) + assert ( + pre is not None and post is not None + ), f"regex group selection for requirement {req} failed, this should never happen" + extras: str = "[%s]" % ",".join(sorted(new_extras)) if new_extras else "" + return Requirement(f"{pre}{extras}{post}") + + +def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]: + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + url = editable_req + + # If a file path is specified with extras, strip off the extras. + url_no_extras, extras = _strip_extras(url) + + if os.path.isdir(url_no_extras): + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith("file:"): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + get_requirement("placeholder" + extras.lower()).extras, + ) + else: + return package_name, url_no_extras, set() + + for version_control in vcs: + if url.lower().startswith(f"{version_control}:"): + url = f"{version_control}+{url}" + break + + link = Link(url) + + if not link.is_vcs: + backends = ", ".join(vcs.all_schemes) + raise InstallationError( + f"{editable_req} is not a valid editable requirement. " + f"It should either be a path to a local project or a VCS URL " + f"(beginning with {backends})." + ) + + package_name = link.egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name for '{}', please specify one " + "with #egg=your_package_name".format(editable_req) + ) + return package_name, url, set() + + +def check_first_requirement_in_file(filename: str) -> None: + """Check if file is parsable as a requirements file. + + This is heavily based on ``pkg_resources.parse_requirements``, but + simplified to just check the first meaningful line. + + :raises InvalidRequirement: If the first meaningful line cannot be parsed + as an requirement. + """ + with open(filename, encoding="utf-8", errors="ignore") as f: + # Create a steppable iterator, so we can handle \-continuations. + lines = ( + line + for line in (line.strip() for line in f) + if line and not line.startswith("#") # Skip blank lines/comments. + ) + + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if " #" in line: + line = line[: line.find(" #")] + # If there is a line continuation, drop it, and append the next line. + if line.endswith("\\"): + line = line[:-2].strip() + next(lines, "") + Requirement(line) + return + + +def deduce_helpful_msg(req: str) -> str: + """Returns helpful msg in case requirements file does not exist, + or cannot be parsed. + + :params req: Requirements file path + """ + if not os.path.exists(req): + return f" File '{req}' does not exist." + msg = " The path does exist. " + # Try to parse and check if it is a requirements file. + try: + check_first_requirement_in_file(req) + except InvalidRequirement: + logger.debug("Cannot parse '%s' as requirements file", req) + else: + msg += ( + f"The argument you provided " + f"({req}) appears to be a" + f" requirements file. If that is the" + f" case, use the '-r' flag to install" + f" the packages specified within it." + ) + return msg + + +class RequirementParts: + def __init__( + self, + requirement: Optional[Requirement], + link: Optional[Link], + markers: Optional[Marker], + extras: Set[str], + ): + self.requirement = requirement + self.link = link + self.markers = markers + self.extras = extras + + +def parse_req_from_editable(editable_req: str) -> RequirementParts: + name, url, extras_override = parse_editable(editable_req) + + if name is not None: + try: + req: Optional[Requirement] = Requirement(name) + except InvalidRequirement: + raise InstallationError(f"Invalid requirement: '{name}'") + else: + req = None + + link = Link(url) + + return RequirementParts(req, link, None, extras_override) + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req: str, + comes_from: Optional[Union[InstallRequirement, str]] = None, + *, + use_pep517: Optional[bool] = None, + isolated: bool = False, + global_options: Optional[List[str]] = None, + hash_options: Optional[Dict[str, List[str]]] = None, + constraint: bool = False, + user_supplied: bool = False, + permit_editable_wheels: bool = False, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, +) -> InstallRequirement: + parts = parse_req_from_editable(editable_req) + + return InstallRequirement( + parts.requirement, + comes_from=comes_from, + user_supplied=user_supplied, + editable=True, + permit_editable_wheels=permit_editable_wheels, + link=parts.link, + constraint=constraint, + use_pep517=use_pep517, + isolated=isolated, + global_options=global_options, + hash_options=hash_options, + config_settings=config_settings, + extras=parts.extras, + ) + + +def _looks_like_path(name: str) -> bool: + """Checks whether the string "looks like" a path on the filesystem. + + This does not check whether the target actually exists, only judge from the + appearance. + + Returns true if any of the following conditions is true: + * a path separator is found (either os.path.sep or os.path.altsep); + * a dot is found (which represents the current directory). + """ + if os.path.sep in name: + return True + if os.path.altsep is not None and os.path.altsep in name: + return True + if name.startswith("."): + return True + return False + + +def _get_url_from_path(path: str, name: str) -> Optional[str]: + """ + First, it checks whether a provided path is an installable directory. If it + is, returns the path. + + If false, check if the path is an archive file (such as a .whl). + The function checks if the path is a file. If false, if the path has + an @, it will treat it as a PEP 440 URL requirement and return the path. + """ + if _looks_like_path(name) and os.path.isdir(path): + if is_installable_dir(path): + return path_to_url(path) + # TODO: The is_installable_dir test here might not be necessary + # now that it is done in load_pyproject_toml too. + raise InstallationError( + f"Directory {name!r} is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found." + ) + if not is_archive_file(path): + return None + if os.path.isfile(path): + return path_to_url(path) + urlreq_parts = name.split("@", 1) + if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]): + # If the path contains '@' and the part before it does not look + # like a path, try to treat it as a PEP 440 URL req instead. + return None + logger.warning( + "Requirement %r looks like a filename, but the file does not exist", + name, + ) + return path_to_url(path) + + +def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementParts: + if is_url(name): + marker_sep = "; " + else: + marker_sep = ";" + if marker_sep in name: + name, markers_as_string = name.split(marker_sep, 1) + markers_as_string = markers_as_string.strip() + if not markers_as_string: + markers = None + else: + markers = Marker(markers_as_string) + else: + markers = None + name = name.strip() + req_as_string = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras_as_string = None + + if is_url(name): + link = Link(name) + else: + p, extras_as_string = _strip_extras(path) + url = _get_url_from_path(p, name) + if url is not None: + link = Link(url) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == "file" and re.search(r"\.\./", link.url): + link = Link(path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req_as_string = f"{wheel.name}=={wheel.version}" + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req_as_string = link.egg_fragment + + # a requirement specifier + else: + req_as_string = name + + extras = convert_extras(extras_as_string) + + def with_source(text: str) -> str: + if not line_source: + return text + return f"{text} (from {line_source})" + + def _parse_req_string(req_as_string: str) -> Requirement: + try: + req = get_requirement(req_as_string) + except InvalidRequirement: + if os.path.sep in req_as_string: + add_msg = "It looks like a path." + add_msg += deduce_helpful_msg(req_as_string) + elif "=" in req_as_string and not any( + op in req_as_string for op in operators + ): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = "" + msg = with_source(f"Invalid requirement: {req_as_string!r}") + if add_msg: + msg += f"\nHint: {add_msg}" + raise InstallationError(msg) + else: + # Deprecate extras after specifiers: "name>=1.0[extras]" + # This currently works by accident because _strip_extras() parses + # any extras in the end of the string and those are saved in + # RequirementParts + for spec in req.specifier: + spec_str = str(spec) + if spec_str.endswith("]"): + msg = f"Extras after version '{spec_str}'." + raise InstallationError(msg) + return req + + if req_as_string is not None: + req: Optional[Requirement] = _parse_req_string(req_as_string) + else: + req = None + + return RequirementParts(req, link, markers, extras) + + +def install_req_from_line( + name: str, + comes_from: Optional[Union[str, InstallRequirement]] = None, + *, + use_pep517: Optional[bool] = None, + isolated: bool = False, + global_options: Optional[List[str]] = None, + hash_options: Optional[Dict[str, List[str]]] = None, + constraint: bool = False, + line_source: Optional[str] = None, + user_supplied: bool = False, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, +) -> InstallRequirement: + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + + :param line_source: An optional string describing where the line is from, + for logging purposes in case of an error. + """ + parts = parse_req_from_line(name, line_source) + + return InstallRequirement( + parts.requirement, + comes_from, + link=parts.link, + markers=parts.markers, + use_pep517=use_pep517, + isolated=isolated, + global_options=global_options, + hash_options=hash_options, + config_settings=config_settings, + constraint=constraint, + extras=parts.extras, + user_supplied=user_supplied, + ) + + +def install_req_from_req_string( + req_string: str, + comes_from: Optional[InstallRequirement] = None, + isolated: bool = False, + use_pep517: Optional[bool] = None, + user_supplied: bool = False, +) -> InstallRequirement: + try: + req = get_requirement(req_string) + except InvalidRequirement: + raise InstallationError(f"Invalid requirement: '{req_string}'") + + domains_not_allowed = [ + PyPI.file_storage_domain, + TestPyPI.file_storage_domain, + ] + if ( + req.url + and comes_from + and comes_from.link + and comes_from.link.netloc in domains_not_allowed + ): + # Explicitly disallow pypi packages that depend on external urls + raise InstallationError( + "Packages installed from PyPI cannot depend on packages " + "which are not also hosted on PyPI.\n" + f"{comes_from.name} depends on {req} " + ) + + return InstallRequirement( + req, + comes_from, + isolated=isolated, + use_pep517=use_pep517, + user_supplied=user_supplied, + ) + + +def install_req_from_parsed_requirement( + parsed_req: ParsedRequirement, + isolated: bool = False, + use_pep517: Optional[bool] = None, + user_supplied: bool = False, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, +) -> InstallRequirement: + if parsed_req.is_editable: + req = install_req_from_editable( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + constraint=parsed_req.constraint, + isolated=isolated, + user_supplied=user_supplied, + config_settings=config_settings, + ) + + else: + req = install_req_from_line( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + isolated=isolated, + global_options=( + parsed_req.options.get("global_options", []) + if parsed_req.options + else [] + ), + hash_options=( + parsed_req.options.get("hashes", {}) if parsed_req.options else {} + ), + constraint=parsed_req.constraint, + line_source=parsed_req.line_source, + user_supplied=user_supplied, + config_settings=config_settings, + ) + return req + + +def install_req_from_link_and_ireq( + link: Link, ireq: InstallRequirement +) -> InstallRequirement: + return InstallRequirement( + req=ireq.req, + comes_from=ireq.comes_from, + editable=ireq.editable, + link=link, + markers=ireq.markers, + use_pep517=ireq.use_pep517, + isolated=ireq.isolated, + global_options=ireq.global_options, + hash_options=ireq.hash_options, + config_settings=ireq.config_settings, + user_supplied=ireq.user_supplied, + ) + + +def install_req_drop_extras(ireq: InstallRequirement) -> InstallRequirement: + """ + Creates a new InstallationRequirement using the given template but without + any extras. Sets the original requirement as the new one's parent + (comes_from). + """ + return InstallRequirement( + req=( + _set_requirement_extras(ireq.req, set()) if ireq.req is not None else None + ), + comes_from=ireq, + editable=ireq.editable, + link=ireq.link, + markers=ireq.markers, + use_pep517=ireq.use_pep517, + isolated=ireq.isolated, + global_options=ireq.global_options, + hash_options=ireq.hash_options, + constraint=ireq.constraint, + extras=[], + config_settings=ireq.config_settings, + user_supplied=ireq.user_supplied, + permit_editable_wheels=ireq.permit_editable_wheels, + ) + + +def install_req_extend_extras( + ireq: InstallRequirement, + extras: Collection[str], +) -> InstallRequirement: + """ + Returns a copy of an installation requirement with some additional extras. + Makes a shallow copy of the ireq object. + """ + result = copy.copy(ireq) + result.extras = {*ireq.extras, *extras} + result.req = ( + _set_requirement_extras(ireq.req, result.extras) + if ireq.req is not None + else None + ) + return result diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000..1ef3d5e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,554 @@ +""" +Requirements file parsing +""" + +import logging +import optparse +import os +import re +import shlex +import urllib.parse +from optparse import Values +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Generator, + Iterable, + List, + Optional, + Tuple, +) + +from pip._internal.cli import cmdoptions +from pip._internal.exceptions import InstallationError, RequirementsFileParseError +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.session import PipSession +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.urls import get_url_scheme + +if TYPE_CHECKING: + # NoReturn introduced in 3.6.2; imported only for type checking to maintain + # pip compatibility with older patch versions of Python 3.6 + from typing import NoReturn + + from pip._internal.index.package_finder import PackageFinder + +__all__ = ["parse_requirements"] + +ReqFileLines = Iterable[Tuple[int, str]] + +LineParser = Callable[[str], Tuple[str, Values]] + +SCHEME_RE = re.compile(r"^(http|https|file):", re.I) +COMMENT_RE = re.compile(r"(^|\s+)#.*$") + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r"(?P\$\{(?P[A-Z0-9_]+)\})") + +SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [ + cmdoptions.index_url, + cmdoptions.extra_index_url, + cmdoptions.no_index, + cmdoptions.constraints, + cmdoptions.requirements, + cmdoptions.editable, + cmdoptions.find_links, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.prefer_binary, + cmdoptions.require_hashes, + cmdoptions.pre, + cmdoptions.trusted_host, + cmdoptions.use_new_feature, +] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [ + cmdoptions.global_options, + cmdoptions.hash, + cmdoptions.config_settings, +] + +SUPPORTED_OPTIONS_EDITABLE_REQ: List[Callable[..., optparse.Option]] = [ + cmdoptions.config_settings, +] + + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] +SUPPORTED_OPTIONS_EDITABLE_REQ_DEST = [ + str(o().dest) for o in SUPPORTED_OPTIONS_EDITABLE_REQ +] + +logger = logging.getLogger(__name__) + + +class ParsedRequirement: + def __init__( + self, + requirement: str, + is_editable: bool, + comes_from: str, + constraint: bool, + options: Optional[Dict[str, Any]] = None, + line_source: Optional[str] = None, + ) -> None: + self.requirement = requirement + self.is_editable = is_editable + self.comes_from = comes_from + self.options = options + self.constraint = constraint + self.line_source = line_source + + +class ParsedLine: + def __init__( + self, + filename: str, + lineno: int, + args: str, + opts: Values, + constraint: bool, + ) -> None: + self.filename = filename + self.lineno = lineno + self.opts = opts + self.constraint = constraint + + if args: + self.is_requirement = True + self.is_editable = False + self.requirement = args + elif opts.editables: + self.is_requirement = True + self.is_editable = True + # We don't support multiple -e on one line + self.requirement = opts.editables[0] + else: + self.is_requirement = False + + +def parse_requirements( + filename: str, + session: PipSession, + finder: Optional["PackageFinder"] = None, + options: Optional[optparse.Values] = None, + constraint: bool = False, +) -> Generator[ParsedRequirement, None, None]: + """Parse a requirements file and yield ParsedRequirement instances. + + :param filename: Path or url of requirements file. + :param session: PipSession instance. + :param finder: Instance of pip.index.PackageFinder. + :param options: cli options. + :param constraint: If true, parsing a constraint file rather than + requirements file. + """ + line_parser = get_line_parser(finder) + parser = RequirementsFileParser(session, line_parser) + + for parsed_line in parser.parse(filename, constraint): + parsed_req = handle_line( + parsed_line, options=options, finder=finder, session=session + ) + if parsed_req is not None: + yield parsed_req + + +def preprocess(content: str) -> ReqFileLines: + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + """ + lines_enum: ReqFileLines = enumerate(content.splitlines(), start=1) + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def handle_requirement_line( + line: ParsedLine, + options: Optional[optparse.Values] = None, +) -> ParsedRequirement: + # preserve for the nested code path + line_comes_from = "{} {} (line {})".format( + "-c" if line.constraint else "-r", + line.filename, + line.lineno, + ) + + assert line.is_requirement + + # get the options that apply to requirements + if line.is_editable: + supported_dest = SUPPORTED_OPTIONS_EDITABLE_REQ_DEST + else: + supported_dest = SUPPORTED_OPTIONS_REQ_DEST + req_options = {} + for dest in supported_dest: + if dest in line.opts.__dict__ and line.opts.__dict__[dest]: + req_options[dest] = line.opts.__dict__[dest] + + line_source = f"line {line.lineno} of {line.filename}" + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + options=req_options, + line_source=line_source, + ) + + +def handle_option_line( + opts: Values, + filename: str, + lineno: int, + finder: Optional["PackageFinder"] = None, + options: Optional[optparse.Values] = None, + session: Optional[PipSession] = None, +) -> None: + if opts.hashes: + logger.warning( + "%s line %s has --hash but no requirement, and will be ignored.", + filename, + lineno, + ) + + if options: + # percolate options upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + if opts.features_enabled: + options.features_enabled.extend( + f for f in opts.features_enabled if f not in options.features_enabled + ) + + # set finder options + if finder: + find_links = finder.find_links + index_urls = finder.index_urls + no_index = finder.search_scope.no_index + if opts.no_index is True: + no_index = True + index_urls = [] + if opts.index_url and not no_index: + index_urls = [opts.index_url] + if opts.extra_index_urls and not no_index: + index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + find_links.append(value) + + if session: + # We need to update the auth urls in session + session.update_index_urls(index_urls) + + search_scope = SearchScope( + find_links=find_links, + index_urls=index_urls, + no_index=no_index, + ) + finder.search_scope = search_scope + + if opts.pre: + finder.set_allow_all_prereleases() + + if opts.prefer_binary: + finder.set_prefer_binary() + + if session: + for host in opts.trusted_hosts or []: + source = f"line {lineno} of {filename}" + session.add_trusted_host(host, source=source) + + +def handle_line( + line: ParsedLine, + options: Optional[optparse.Values] = None, + finder: Optional["PackageFinder"] = None, + session: Optional[PipSession] = None, +) -> Optional[ParsedRequirement]: + """Handle a single parsed requirements line; This can result in + creating/yielding requirements, or updating the finder. + + :param line: The parsed line to be processed. + :param options: CLI options. + :param finder: The finder - updated by non-requirement lines. + :param session: The session - updated by non-requirement lines. + + Returns a ParsedRequirement object if the line is a requirement line, + otherwise returns None. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + """ + + if line.is_requirement: + parsed_req = handle_requirement_line(line, options) + return parsed_req + else: + handle_option_line( + line.opts, + line.filename, + line.lineno, + finder, + options, + session, + ) + return None + + +class RequirementsFileParser: + def __init__( + self, + session: PipSession, + line_parser: LineParser, + ) -> None: + self._session = session + self._line_parser = line_parser + + def parse( + self, filename: str, constraint: bool + ) -> Generator[ParsedLine, None, None]: + """Parse a given file, yielding parsed lines.""" + yield from self._parse_and_recurse(filename, constraint) + + def _parse_and_recurse( + self, filename: str, constraint: bool + ) -> Generator[ParsedLine, None, None]: + for line in self._parse_file(filename, constraint): + if not line.is_requirement and ( + line.opts.requirements or line.opts.constraints + ): + # parse a nested requirements file + if line.opts.requirements: + req_path = line.opts.requirements[0] + nested_constraint = False + else: + req_path = line.opts.constraints[0] + nested_constraint = True + + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib.parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join( + os.path.dirname(filename), + req_path, + ) + + yield from self._parse_and_recurse(req_path, nested_constraint) + else: + yield line + + def _parse_file( + self, filename: str, constraint: bool + ) -> Generator[ParsedLine, None, None]: + _, content = get_file_content(filename, self._session) + + lines_enum = preprocess(content) + + for line_number, line in lines_enum: + try: + args_str, opts = self._line_parser(line) + except OptionParsingError as e: + # add offending line + msg = f"Invalid requirement: {line}\n{e.msg}" + raise RequirementsFileParseError(msg) + + yield ParsedLine( + filename, + line_number, + args_str, + opts, + constraint, + ) + + +def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser: + def parse_line(line: str) -> Tuple[str, Values]: + # Build new parser for each line since it accumulates appendable + # options. + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + defaults.format_control = finder.format_control + + args_str, options_str = break_args_options(line) + + try: + options = shlex.split(options_str) + except ValueError as e: + raise OptionParsingError(f"Could not split options: {options_str}") from e + + opts, _ = parser.parse_args(options, defaults) + + return args_str, opts + + return parse_line + + +def break_args_options(line: str) -> Tuple[str, str]: + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(" ") + args = [] + options = tokens[:] + for token in tokens: + if token.startswith("-") or token.startswith("--"): + break + else: + args.append(token) + options.pop(0) + return " ".join(args), " ".join(options) + + +class OptionParsingError(Exception): + def __init__(self, msg: str) -> None: + self.msg = msg + + +def build_parser() -> optparse.OptionParser: + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self: Any, msg: str) -> "NoReturn": + raise OptionParsingError(msg) + + # NOTE: mypy disallows assigning to a method + # https://github.com/python/mypy/issues/2427 + parser.exit = parser_exit # type: ignore + + return parser + + +def join_lines(lines_enum: ReqFileLines) -> ReqFileLines: + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line: List[str] = [] + for line_number, line in lines_enum: + if not line.endswith("\\") or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = " " + line + if new_line: + new_line.append(line) + assert primary_line_number is not None + yield primary_line_number, "".join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip("\\")) + + # last line contains \ + if new_line: + assert primary_line_number is not None + yield primary_line_number, "".join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum: ReqFileLines) -> ReqFileLines: + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub("", line) + line = line.strip() + if line: + yield line_number, line + + +def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines: + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discussion on the `github pull + request #3514 `_. + + Valid characters in variable names follow the `POSIX standard + `_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line + + +def get_file_content(url: str, session: PipSession) -> Tuple[str, str]: + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + Respects # -*- coding: declarations on the retrieved files. + + :param url: File path or url. + :param session: PipSession instance. + """ + scheme = get_url_scheme(url) + + # Pip has special support for file:// URLs (LocalFSAdapter). + if scheme in ["http", "https", "file"]: + resp = session.get(url) + raise_for_status(resp) + return resp.url, resp.text + + # Assume this is a bare path. + try: + with open(url, "rb") as f: + content = auto_decode(f.read()) + except OSError as exc: + raise InstallationError(f"Could not open requirements file: {exc}") + return url, content diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000..a65611c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,923 @@ +import functools +import logging +import os +import shutil +import sys +import uuid +import zipfile +from optparse import Values +from pathlib import Path +from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pyproject_hooks import BuildBackendHookCaller + +from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError, PreviousBuildDirError +from pip._internal.locations import get_scheme +from pip._internal.metadata import ( + BaseDistribution, + get_default_environment, + get_directory_distribution, + get_wheel_distribution, +) +from pip._internal.metadata.base import FilesystemWheel +from pip._internal.models.direct_url import DirectUrl +from pip._internal.models.link import Link +from pip._internal.operations.build.metadata import generate_metadata +from pip._internal.operations.build.metadata_editable import generate_editable_metadata +from pip._internal.operations.build.metadata_legacy import ( + generate_metadata as generate_metadata_legacy, +) +from pip._internal.operations.install.editable_legacy import ( + install_editable as install_editable_legacy, +) +from pip._internal.operations.install.wheel import install_wheel +from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.misc import ( + ConfiguredBuildBackendHookCaller, + ask_path_exists, + backup_dir, + display_path, + hide_url, + is_installable_dir, + redact_auth_from_requirement, + redact_auth_from_url, +) +from pip._internal.utils.packaging import safe_extra +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.unpacking import unpack_file +from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._internal.vcs import vcs + +logger = logging.getLogger(__name__) + + +class InstallRequirement: + """ + Represents something that may be installed later on, may have information + about where to fetch the relevant requirement and also contains logic for + installing the said requirement. + """ + + def __init__( + self, + req: Optional[Requirement], + comes_from: Optional[Union[str, "InstallRequirement"]], + editable: bool = False, + link: Optional[Link] = None, + markers: Optional[Marker] = None, + use_pep517: Optional[bool] = None, + isolated: bool = False, + *, + global_options: Optional[List[str]] = None, + hash_options: Optional[Dict[str, List[str]]] = None, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, + constraint: bool = False, + extras: Collection[str] = (), + user_supplied: bool = False, + permit_editable_wheels: bool = False, + ) -> None: + assert req is None or isinstance(req, Requirement), req + self.req = req + self.comes_from = comes_from + self.constraint = constraint + self.editable = editable + self.permit_editable_wheels = permit_editable_wheels + + # source_dir is the local directory where the linked requirement is + # located, or unpacked. In case unpacking is needed, creating and + # populating source_dir is done by the RequirementPreparer. Note this + # is not necessarily the directory where pyproject.toml or setup.py is + # located - that one is obtained via unpacked_source_directory. + self.source_dir: Optional[str] = None + if self.editable: + assert link + if link.is_file: + self.source_dir = os.path.normpath(os.path.abspath(link.file_path)) + + # original_link is the direct URL that was provided by the user for the + # requirement, either directly or via a constraints file. + if link is None and req and req.url: + # PEP 508 URL requirement + link = Link(req.url) + self.link = self.original_link = link + + # When this InstallRequirement is a wheel obtained from the cache of locally + # built wheels, this is the source link corresponding to the cache entry, which + # was used to download and build the cached wheel. + self.cached_wheel_source_link: Optional[Link] = None + + # Information about the location of the artifact that was downloaded . This + # property is guaranteed to be set in resolver results. + self.download_info: Optional[DirectUrl] = None + + # Path to any downloaded or already-existing package. + self.local_file_path: Optional[str] = None + if self.link and self.link.is_file: + self.local_file_path = self.link.file_path + + if extras: + self.extras = extras + elif req: + self.extras = req.extras + else: + self.extras = set() + if markers is None and req: + markers = req.marker + self.markers = markers + + # This holds the Distribution object if this requirement is already installed. + self.satisfied_by: Optional[BaseDistribution] = None + # Whether the installation process should try to uninstall an existing + # distribution before installing this requirement. + self.should_reinstall = False + # Temporary build location + self._temp_build_dir: Optional[TempDirectory] = None + # Set to True after successful installation + self.install_succeeded: Optional[bool] = None + # Supplied options + self.global_options = global_options if global_options else [] + self.hash_options = hash_options if hash_options else {} + self.config_settings = config_settings + # Set to True after successful preparation of this requirement + self.prepared = False + # User supplied requirement are explicitly requested for installation + # by the user via CLI arguments or requirements files, as opposed to, + # e.g. dependencies, extras or constraints. + self.user_supplied = user_supplied + + self.isolated = isolated + self.build_env: BuildEnvironment = NoOpBuildEnvironment() + + # For PEP 517, the directory where we request the project metadata + # gets stored. We need this to pass to build_wheel, so the backend + # can ensure that the wheel matches the metadata (see the PEP for + # details). + self.metadata_directory: Optional[str] = None + + # The static build requirements (from pyproject.toml) + self.pyproject_requires: Optional[List[str]] = None + + # Build requirements that we will check are available + self.requirements_to_check: List[str] = [] + + # The PEP 517 backend we should use to build the project + self.pep517_backend: Optional[BuildBackendHookCaller] = None + + # Are we using PEP 517 for this requirement? + # After pyproject.toml has been loaded, the only valid values are True + # and False. Before loading, None is valid (meaning "use the default"). + # Setting an explicit value before loading pyproject.toml is supported, + # but after loading this flag should be treated as read only. + self.use_pep517 = use_pep517 + + # If config settings are provided, enforce PEP 517. + if self.config_settings: + if self.use_pep517 is False: + logger.warning( + "--no-use-pep517 ignored for %s " + "because --config-settings are specified.", + self, + ) + self.use_pep517 = True + + # This requirement needs more preparation before it can be built + self.needs_more_preparation = False + + # This requirement needs to be unpacked before it can be installed. + self._archive_source: Optional[Path] = None + + def __str__(self) -> str: + if self.req: + s = redact_auth_from_requirement(self.req) + if self.link: + s += f" from {redact_auth_from_url(self.link.url)}" + elif self.link: + s = redact_auth_from_url(self.link.url) + else: + s = "" + if self.satisfied_by is not None: + if self.satisfied_by.location is not None: + location = display_path(self.satisfied_by.location) + else: + location = "" + s += f" in {location}" + if self.comes_from: + if isinstance(self.comes_from, str): + comes_from: Optional[str] = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += f" (from {comes_from})" + return s + + def __repr__(self) -> str: + return "<{} object: {} editable={!r}>".format( + self.__class__.__name__, str(self), self.editable + ) + + def format_debug(self) -> str: + """An un-tested helper for getting state, for debugging.""" + attributes = vars(self) + names = sorted(attributes) + + state = (f"{attr}={attributes[attr]!r}" for attr in sorted(names)) + return "<{name} object: {{{state}}}>".format( + name=self.__class__.__name__, + state=", ".join(state), + ) + + # Things that are valid for all kinds of requirements? + @property + def name(self) -> Optional[str]: + if self.req is None: + return None + return self.req.name + + @functools.lru_cache() # use cached_property in python 3.8+ + def supports_pyproject_editable(self) -> bool: + if not self.use_pep517: + return False + assert self.pep517_backend + with self.build_env: + runner = runner_with_spinner_message( + "Checking if build backend supports build_editable" + ) + with self.pep517_backend.subprocess_runner(runner): + return "build_editable" in self.pep517_backend._supported_features() + + @property + def specifier(self) -> SpecifierSet: + assert self.req is not None + return self.req.specifier + + @property + def is_direct(self) -> bool: + """Whether this requirement was specified as a direct URL.""" + return self.original_link is not None + + @property + def is_pinned(self) -> bool: + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + assert self.req is not None + specifiers = self.req.specifier + return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="} + + def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool: + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ("",) + if self.markers is not None: + return any( + self.markers.evaluate({"extra": extra}) + # TODO: Remove these two variants when packaging is upgraded to + # support the marker comparison logic specified in PEP 685. + or self.markers.evaluate({"extra": safe_extra(extra)}) + or self.markers.evaluate({"extra": canonicalize_name(extra)}) + for extra in extras_requested + ) + else: + return True + + @property + def has_hash_options(self) -> bool: + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.hash_options) + + def hashes(self, trust_internet: bool = True) -> Hashes: + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.hash_options.copy() + if trust_internet: + link = self.link + elif self.is_direct and self.user_supplied: + link = self.original_link + else: + link = None + if link and link.hash: + assert link.hash_name is not None + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + def from_path(self) -> Optional[str]: + """Format a nice indicator to show where this "comes from" """ + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + comes_from: Optional[str] + if isinstance(self.comes_from, str): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += "->" + comes_from + return s + + def ensure_build_location( + self, build_dir: str, autodelete: bool, parallel_builds: bool + ) -> str: + assert build_dir is not None + if self._temp_build_dir is not None: + assert self._temp_build_dir.path + return self._temp_build_dir.path + if self.req is None: + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir = TempDirectory( + kind=tempdir_kinds.REQ_BUILD, globally_managed=True + ) + + return self._temp_build_dir.path + + # This is the only remaining place where we manually determine the path + # for the temporary directory. It is only needed for editables where + # it is the value of the --src option. + + # When parallel builds are enabled, add a UUID to the build directory + # name so multiple builds do not interfere with each other. + dir_name: str = canonicalize_name(self.req.name) + if parallel_builds: + dir_name = f"{dir_name}_{uuid.uuid4().hex}" + + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug("Creating directory %s", build_dir) + os.makedirs(build_dir) + actual_build_dir = os.path.join(build_dir, dir_name) + # `None` indicates that we respect the globally-configured deletion + # settings, which is what we actually want when auto-deleting. + delete_arg = None if autodelete else False + return TempDirectory( + path=actual_build_dir, + delete=delete_arg, + kind=tempdir_kinds.REQ_BUILD, + globally_managed=True, + ).path + + def _set_requirement(self) -> None: + """Set requirement after generating metadata.""" + assert self.req is None + assert self.metadata is not None + assert self.source_dir is not None + + # Construct a Requirement object from the generated metadata + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + + self.req = Requirement( + "".join( + [ + self.metadata["Name"], + op, + self.metadata["Version"], + ] + ) + ) + + def warn_on_mismatching_name(self) -> None: + assert self.req is not None + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) == metadata_name: + # Everything is fine. + return + + # If we're here, there's a mismatch. Log a warning about it. + logger.warning( + "Generating metadata for package %s " + "produced metadata for project name %s. Fix your " + "#egg=%s fragments.", + self.name, + metadata_name, + self.name, + ) + self.req = Requirement(metadata_name) + + def check_if_exists(self, use_user_site: bool) -> None: + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.should_reinstall appropriately. + """ + if self.req is None: + return + existing_dist = get_default_environment().get_distribution(self.req.name) + if not existing_dist: + return + + version_compatible = self.req.specifier.contains( + existing_dist.version, + prereleases=True, + ) + if not version_compatible: + self.satisfied_by = None + if use_user_site: + if existing_dist.in_usersite: + self.should_reinstall = True + elif running_under_virtualenv() and existing_dist.in_site_packages: + raise InstallationError( + f"Will not install to the user site because it will " + f"lack sys.path precedence to {existing_dist.raw_name} " + f"in {existing_dist.location}" + ) + else: + self.should_reinstall = True + else: + if self.editable: + self.should_reinstall = True + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + else: + self.satisfied_by = existing_dist + + # Things valid for wheels + @property + def is_wheel(self) -> bool: + if not self.link: + return False + return self.link.is_wheel + + @property + def is_wheel_from_cache(self) -> bool: + # When True, it means that this InstallRequirement is a local wheel file in the + # cache of locally built wheels. + return self.cached_wheel_source_link is not None + + # Things valid for sdists + @property + def unpacked_source_directory(self) -> str: + assert self.source_dir, f"No source dir for {self}" + return os.path.join( + self.source_dir, self.link and self.link.subdirectory_fragment or "" + ) + + @property + def setup_py_path(self) -> str: + assert self.source_dir, f"No source dir for {self}" + setup_py = os.path.join(self.unpacked_source_directory, "setup.py") + + return setup_py + + @property + def setup_cfg_path(self) -> str: + assert self.source_dir, f"No source dir for {self}" + setup_cfg = os.path.join(self.unpacked_source_directory, "setup.cfg") + + return setup_cfg + + @property + def pyproject_toml_path(self) -> str: + assert self.source_dir, f"No source dir for {self}" + return make_pyproject_path(self.unpacked_source_directory) + + def load_pyproject_toml(self) -> None: + """Load the pyproject.toml file. + + After calling this routine, all of the attributes related to PEP 517 + processing for this requirement have been set. In particular, the + use_pep517 attribute can be used to determine whether we should + follow the PEP 517 or legacy (setup.py) code path. + """ + pyproject_toml_data = load_pyproject_toml( + self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self) + ) + + if pyproject_toml_data is None: + assert not self.config_settings + self.use_pep517 = False + return + + self.use_pep517 = True + requires, backend, check, backend_path = pyproject_toml_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = ConfiguredBuildBackendHookCaller( + self, + self.unpacked_source_directory, + backend, + backend_path=backend_path, + ) + + def isolated_editable_sanity_check(self) -> None: + """Check that an editable requirement if valid for use with PEP 517/518. + + This verifies that an editable that has a pyproject.toml either supports PEP 660 + or as a setup.py or a setup.cfg + """ + if ( + self.editable + and self.use_pep517 + and not self.supports_pyproject_editable() + and not os.path.isfile(self.setup_py_path) + and not os.path.isfile(self.setup_cfg_path) + ): + raise InstallationError( + f"Project {self} has a 'pyproject.toml' and its build " + f"backend is missing the 'build_editable' hook. Since it does not " + f"have a 'setup.py' nor a 'setup.cfg', " + f"it cannot be installed in editable mode. " + f"Consider using a build backend that supports PEP 660." + ) + + def prepare_metadata(self) -> None: + """Ensure that project metadata is available. + + Under PEP 517 and PEP 660, call the backend hook to prepare the metadata. + Under legacy processing, call setup.py egg-info. + """ + assert self.source_dir, f"No source dir for {self}" + details = self.name or f"from {self.link}" + + if self.use_pep517: + assert self.pep517_backend is not None + if ( + self.editable + and self.permit_editable_wheels + and self.supports_pyproject_editable() + ): + self.metadata_directory = generate_editable_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + details=details, + ) + else: + self.metadata_directory = generate_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + details=details, + ) + else: + self.metadata_directory = generate_metadata_legacy( + build_env=self.build_env, + setup_py_path=self.setup_py_path, + source_dir=self.unpacked_source_directory, + isolated=self.isolated, + details=details, + ) + + # Act on the newly generated metadata, based on the name and version. + if not self.name: + self._set_requirement() + else: + self.warn_on_mismatching_name() + + self.assert_source_matches_version() + + @property + def metadata(self) -> Any: + if not hasattr(self, "_metadata"): + self._metadata = self.get_dist().metadata + + return self._metadata + + def get_dist(self) -> BaseDistribution: + if self.metadata_directory: + return get_directory_distribution(self.metadata_directory) + elif self.local_file_path and self.is_wheel: + assert self.req is not None + return get_wheel_distribution( + FilesystemWheel(self.local_file_path), + canonicalize_name(self.req.name), + ) + raise AssertionError( + f"InstallRequirement {self} has no metadata directory and no wheel: " + f"can't make a distribution." + ) + + def assert_source_matches_version(self) -> None: + assert self.source_dir, f"No source dir for {self}" + version = self.metadata["version"] + if self.req and self.req.specifier and version not in self.req.specifier: + logger.warning( + "Requested %s, but installing version %s", + self, + version, + ) + else: + logger.debug( + "Source in %s has version %s, which satisfies requirement %s", + display_path(self.source_dir), + version, + self, + ) + + # For both source distributions and editables + def ensure_has_source_dir( + self, + parent_dir: str, + autodelete: bool = False, + parallel_builds: bool = False, + ) -> None: + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.ensure_build_location( + parent_dir, + autodelete=autodelete, + parallel_builds=parallel_builds, + ) + + def needs_unpacked_archive(self, archive_source: Path) -> None: + assert self._archive_source is None + self._archive_source = archive_source + + def ensure_pristine_source_checkout(self) -> None: + """Ensure the source directory has not yet been built in.""" + assert self.source_dir is not None + if self._archive_source is not None: + unpack_file(str(self._archive_source), self.source_dir) + elif is_installable_dir(self.source_dir): + # If a checkout exists, it's unwise to keep going. + # version inconsistencies are logged later, but do not fail + # the installation. + raise PreviousBuildDirError( + f"pip can't proceed with requirements '{self}' due to a " + f"pre-existing build directory ({self.source_dir}). This is likely " + "due to a previous installation that failed . pip is " + "being responsible and not assuming it can delete this. " + "Please delete it and try again." + ) + + # For editable installations + def update_editable(self) -> None: + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == "file": + # Static paths don't get updated + return + vcs_backend = vcs.get_backend_for_scheme(self.link.scheme) + # Editable requirements are validated in Requirement constructors. + # So here, if it's neither a path nor a valid VCS URL, it's a bug. + assert vcs_backend, f"Unsupported VCS URL {self.link.url}" + hidden_url = hide_url(self.link.url) + vcs_backend.obtain(self.source_dir, url=hidden_url, verbosity=0) + + # Top-level Actions + def uninstall( + self, auto_confirm: bool = False, verbose: bool = False + ) -> Optional[UninstallPathSet]: + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + assert self.req + dist = get_default_environment().get_distribution(self.req.name) + if not dist: + logger.warning("Skipping %s as it is not installed.", self.name) + return None + logger.info("Found existing installation: %s", dist) + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _get_archive_name(self, path: str, parentdir: str, rootdir: str) -> str: + def _clean_zip_name(name: str, prefix: str) -> str: + assert name.startswith( + prefix + os.path.sep + ), f"name {name!r} doesn't start with prefix {prefix!r}" + name = name[len(prefix) + 1 :] + name = name.replace(os.path.sep, "/") + return name + + assert self.req is not None + path = os.path.join(parentdir, path) + name = _clean_zip_name(path, rootdir) + return self.req.name + "/" + name + + def archive(self, build_dir: Optional[str]) -> None: + """Saves archive to provided build_dir. + + Used for saving downloaded VCS requirements as part of `pip download`. + """ + assert self.source_dir + if build_dir is None: + return + + create_archive = True + archive_name = "{}-{}.zip".format(self.name, self.metadata["version"]) + archive_path = os.path.join(build_dir, archive_name) + + if os.path.exists(archive_path): + response = ask_path_exists( + f"The file {display_path(archive_path)} exists. (i)gnore, (w)ipe, " + "(b)ackup, (a)bort ", + ("i", "w", "b", "a"), + ) + if response == "i": + create_archive = False + elif response == "w": + logger.warning("Deleting %s", display_path(archive_path)) + os.remove(archive_path) + elif response == "b": + dest_file = backup_dir(archive_path) + logger.warning( + "Backing up %s to %s", + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + elif response == "a": + sys.exit(-1) + + if not create_archive: + return + + zip_output = zipfile.ZipFile( + archive_path, + "w", + zipfile.ZIP_DEFLATED, + allowZip64=True, + ) + with zip_output: + dir = os.path.normcase(os.path.abspath(self.unpacked_source_directory)) + for dirpath, dirnames, filenames in os.walk(dir): + for dirname in dirnames: + dir_arcname = self._get_archive_name( + dirname, + parentdir=dirpath, + rootdir=dir, + ) + zipdir = zipfile.ZipInfo(dir_arcname + "/") + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip_output.writestr(zipdir, "") + for filename in filenames: + file_arcname = self._get_archive_name( + filename, + parentdir=dirpath, + rootdir=dir, + ) + filename = os.path.join(dirpath, filename) + zip_output.write(filename, file_arcname) + + logger.info("Saved %s", display_path(archive_path)) + + def install( + self, + global_options: Optional[Sequence[str]] = None, + root: Optional[str] = None, + home: Optional[str] = None, + prefix: Optional[str] = None, + warn_script_location: bool = True, + use_user_site: bool = False, + pycompile: bool = True, + ) -> None: + assert self.req is not None + scheme = get_scheme( + self.req.name, + user=use_user_site, + home=home, + root=root, + isolated=self.isolated, + prefix=prefix, + ) + + if self.editable and not self.is_wheel: + if self.config_settings: + logger.warning( + "--config-settings ignored for legacy editable install of %s. " + "Consider upgrading to a version of setuptools " + "that supports PEP 660 (>= 64).", + self, + ) + install_editable_legacy( + global_options=global_options if global_options is not None else [], + prefix=prefix, + home=home, + use_user_site=use_user_site, + name=self.req.name, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + ) + self.install_succeeded = True + return + + assert self.is_wheel + assert self.local_file_path + + install_wheel( + self.req.name, + self.local_file_path, + scheme=scheme, + req_description=str(self.req), + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=self.download_info if self.is_direct else None, + requested=self.user_supplied, + ) + self.install_succeeded = True + + +def check_invalid_constraint_type(req: InstallRequirement) -> str: + # Check for unsupported forms + problem = "" + if not req.name: + problem = "Unnamed requirements are not allowed as constraints" + elif req.editable: + problem = "Editable requirements are not allowed as constraints" + elif req.extras: + problem = "Constraints cannot have extras" + + if problem: + deprecated( + reason=( + "Constraints are only allowed to take the form of a package " + "name and a version specifier. Other forms were originally " + "permitted as an accident of the implementation, but were " + "undocumented. The new implementation of the resolver no " + "longer supports these forms." + ), + replacement="replacing the constraint with a requirement", + # No plan yet for when the new resolver becomes default + gone_in=None, + issue=8210, + ) + + return problem + + +def _has_option(options: Values, reqs: List[InstallRequirement], option: str) -> bool: + if getattr(options, option, None): + return True + for req in reqs: + if getattr(req, option, None): + return True + return False + + +def check_legacy_setup_py_options( + options: Values, + reqs: List[InstallRequirement], +) -> None: + has_build_options = _has_option(options, reqs, "build_options") + has_global_options = _has_option(options, reqs, "global_options") + if has_build_options or has_global_options: + deprecated( + reason="--build-option and --global-option are deprecated.", + issue=11859, + replacement="to use --config-settings", + gone_in="24.2", + ) + logger.warning( + "Implying --no-binary=:all: due to the presence of " + "--build-option / --global-option. " + ) + options.format_control.disallow_binaries() diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000..bf36114 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,119 @@ +import logging +from collections import OrderedDict +from typing import Dict, List + +from pip._vendor.packaging.specifiers import LegacySpecifier +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import LegacyVersion + +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.deprecation import deprecated + +logger = logging.getLogger(__name__) + + +class RequirementSet: + def __init__(self, check_supported_wheels: bool = True) -> None: + """Create a RequirementSet.""" + + self.requirements: Dict[str, InstallRequirement] = OrderedDict() + self.check_supported_wheels = check_supported_wheels + + self.unnamed_requirements: List[InstallRequirement] = [] + + def __str__(self) -> str: + requirements = sorted( + (req for req in self.requirements.values() if not req.comes_from), + key=lambda req: canonicalize_name(req.name or ""), + ) + return " ".join(str(req.req) for req in requirements) + + def __repr__(self) -> str: + requirements = sorted( + self.requirements.values(), + key=lambda req: canonicalize_name(req.name or ""), + ) + + format_string = "<{classname} object; {count} requirement(s): {reqs}>" + return format_string.format( + classname=self.__class__.__name__, + count=len(requirements), + reqs=", ".join(str(req.req) for req in requirements), + ) + + def add_unnamed_requirement(self, install_req: InstallRequirement) -> None: + assert not install_req.name + self.unnamed_requirements.append(install_req) + + def add_named_requirement(self, install_req: InstallRequirement) -> None: + assert install_req.name + + project_name = canonicalize_name(install_req.name) + self.requirements[project_name] = install_req + + def has_requirement(self, name: str) -> bool: + project_name = canonicalize_name(name) + + return ( + project_name in self.requirements + and not self.requirements[project_name].constraint + ) + + def get_requirement(self, name: str) -> InstallRequirement: + project_name = canonicalize_name(name) + + if project_name in self.requirements: + return self.requirements[project_name] + + raise KeyError(f"No project with the name {name!r}") + + @property + def all_requirements(self) -> List[InstallRequirement]: + return self.unnamed_requirements + list(self.requirements.values()) + + @property + def requirements_to_install(self) -> List[InstallRequirement]: + """Return the list of requirements that need to be installed. + + TODO remove this property together with the legacy resolver, since the new + resolver only returns requirements that need to be installed. + """ + return [ + install_req + for install_req in self.all_requirements + if not install_req.constraint and not install_req.satisfied_by + ] + + def warn_legacy_versions_and_specifiers(self) -> None: + for req in self.requirements_to_install: + version = req.get_dist().version + if isinstance(version, LegacyVersion): + deprecated( + reason=( + f"pip has selected the non standard version {version} " + f"of {req}. In the future this version will be " + f"ignored as it isn't standard compliant." + ), + replacement=( + "set or update constraints to select another version " + "or contact the package author to fix the version number" + ), + issue=12063, + gone_in="24.1", + ) + for dep in req.get_dist().iter_dependencies(): + if any(isinstance(spec, LegacySpecifier) for spec in dep.specifier): + deprecated( + reason=( + f"pip has selected {req} {version} which has non " + f"standard dependency specifier {dep}. " + f"In the future this version of {req} will be " + f"ignored as it isn't standard compliant." + ), + replacement=( + "set or update constraints to select another version " + "or contact the package author to fix the version number" + ), + issue=12063, + gone_in="24.1", + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000..707fde1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,649 @@ +import functools +import os +import sys +import sysconfig +from importlib.util import cache_from_source +from typing import Any, Callable, Dict, Generator, Iterable, List, Optional, Set, Tuple + +from pip._internal.exceptions import UninstallationError +from pip._internal.locations import get_bin_prefix, get_bin_user +from pip._internal.metadata import BaseDistribution +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.egg_link import egg_link_path_from_location +from pip._internal.utils.logging import getLogger, indent_log +from pip._internal.utils.misc import ask, normalize_path, renames, rmtree +from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory +from pip._internal.utils.virtualenv import running_under_virtualenv + +logger = getLogger(__name__) + + +def _script_names( + bin_dir: str, script_name: str, is_gui: bool +) -> Generator[str, None, None]: + """Create the fully qualified name of the files created by + {console,gui}_scripts for the given ``dist``. + Returns the list of file names + """ + exe_name = os.path.join(bin_dir, script_name) + yield exe_name + if not WINDOWS: + return + yield f"{exe_name}.exe" + yield f"{exe_name}.exe.manifest" + if is_gui: + yield f"{exe_name}-script.pyw" + else: + yield f"{exe_name}-script.py" + + +def _unique( + fn: Callable[..., Generator[Any, None, None]] +) -> Callable[..., Generator[Any, None, None]]: + @functools.wraps(fn) + def unique(*args: Any, **kw: Any) -> Generator[Any, None, None]: + seen: Set[Any] = set() + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + + return unique + + +@_unique +def uninstallation_paths(dist: BaseDistribution) -> Generator[str, None, None]: + """ + Yield all the uninstallation paths for dist based on RECORD-without-.py[co] + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc and .pyo in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .py[co]. + + If RECORD is not found, raises UninstallationError, + with possible information from the INSTALLER file. + + https://packaging.python.org/specifications/recording-installed-packages/ + """ + location = dist.location + assert location is not None, "not installed" + + entries = dist.iter_declared_entries() + if entries is None: + msg = f"Cannot uninstall {dist}, RECORD file not found." + installer = dist.installer + if not installer or installer == "pip": + dep = f"{dist.raw_name}=={dist.version}" + msg += ( + " You might be able to recover from this via: " + f"'pip install --force-reinstall --no-deps {dep}'." + ) + else: + msg += f" Hint: The package was installed by {installer}." + raise UninstallationError(msg) + + for entry in entries: + path = os.path.join(location, entry) + yield path + if path.endswith(".py"): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + ".pyc") + yield path + path = os.path.join(dn, base + ".pyo") + yield path + + +def compact(paths: Iterable[str]) -> Set[str]: + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + + sep = os.path.sep + short_paths: Set[str] = set() + for path in sorted(paths, key=len): + should_skip = any( + path.startswith(shortpath.rstrip("*")) + and path[len(shortpath.rstrip("*").rstrip(sep))] == sep + for shortpath in short_paths + ) + if not should_skip: + short_paths.add(path) + return short_paths + + +def compress_for_rename(paths: Iterable[str]) -> Set[str]: + """Returns a set containing the paths that need to be renamed. + + This set may include directories when the original sequence of paths + included every file on disk. + """ + case_map = {os.path.normcase(p): p for p in paths} + remaining = set(case_map) + unchecked = sorted({os.path.split(p)[0] for p in case_map.values()}, key=len) + wildcards: Set[str] = set() + + def norm_join(*a: str) -> str: + return os.path.normcase(os.path.join(*a)) + + for root in unchecked: + if any(os.path.normcase(root).startswith(w) for w in wildcards): + # This directory has already been handled. + continue + + all_files: Set[str] = set() + all_subdirs: Set[str] = set() + for dirname, subdirs, files in os.walk(root): + all_subdirs.update(norm_join(root, dirname, d) for d in subdirs) + all_files.update(norm_join(root, dirname, f) for f in files) + # If all the files we found are in our remaining set of files to + # remove, then remove them from the latter set and add a wildcard + # for the directory. + if not (all_files - remaining): + remaining.difference_update(all_files) + wildcards.add(root + os.sep) + + return set(map(case_map.__getitem__, remaining)) | wildcards + + +def compress_for_output_listing(paths: Iterable[str]) -> Tuple[Set[str], Set[str]]: + """Returns a tuple of 2 sets of which paths to display to user + + The first set contains paths that would be deleted. Files of a package + are not added and the top-level directory of the package has a '*' added + at the end - to signify that all it's contents are removed. + + The second set contains files that would have been skipped in the above + folders. + """ + + will_remove = set(paths) + will_skip = set() + + # Determine folders and files + folders = set() + files = set() + for path in will_remove: + if path.endswith(".pyc"): + continue + if path.endswith("__init__.py") or ".dist-info" in path: + folders.add(os.path.dirname(path)) + files.add(path) + + _normcased_files = set(map(os.path.normcase, files)) + + folders = compact(folders) + + # This walks the tree using os.walk to not miss extra folders + # that might get added. + for folder in folders: + for dirpath, _, dirfiles in os.walk(folder): + for fname in dirfiles: + if fname.endswith(".pyc"): + continue + + file_ = os.path.join(dirpath, fname) + if ( + os.path.isfile(file_) + and os.path.normcase(file_) not in _normcased_files + ): + # We are skipping this file. Add it to the set. + will_skip.add(file_) + + will_remove = files | {os.path.join(folder, "*") for folder in folders} + + return will_remove, will_skip + + +class StashedUninstallPathSet: + """A set of file rename operations to stash files while + tentatively uninstalling them.""" + + def __init__(self) -> None: + # Mapping from source file root to [Adjacent]TempDirectory + # for files under that directory. + self._save_dirs: Dict[str, TempDirectory] = {} + # (old path, new path) tuples for each move that may need + # to be undone. + self._moves: List[Tuple[str, str]] = [] + + def _get_directory_stash(self, path: str) -> str: + """Stashes a directory. + + Directories are stashed adjacent to their original location if + possible, or else moved/copied into the user's temp dir.""" + + try: + save_dir: TempDirectory = AdjacentTempDirectory(path) + except OSError: + save_dir = TempDirectory(kind="uninstall") + self._save_dirs[os.path.normcase(path)] = save_dir + + return save_dir.path + + def _get_file_stash(self, path: str) -> str: + """Stashes a file. + + If no root has been provided, one will be created for the directory + in the user's temp directory.""" + path = os.path.normcase(path) + head, old_head = os.path.dirname(path), None + save_dir = None + + while head != old_head: + try: + save_dir = self._save_dirs[head] + break + except KeyError: + pass + head, old_head = os.path.dirname(head), head + else: + # Did not find any suitable root + head = os.path.dirname(path) + save_dir = TempDirectory(kind="uninstall") + self._save_dirs[head] = save_dir + + relpath = os.path.relpath(path, head) + if relpath and relpath != os.path.curdir: + return os.path.join(save_dir.path, relpath) + return save_dir.path + + def stash(self, path: str) -> str: + """Stashes the directory or file and returns its new location. + Handle symlinks as files to avoid modifying the symlink targets. + """ + path_is_dir = os.path.isdir(path) and not os.path.islink(path) + if path_is_dir: + new_path = self._get_directory_stash(path) + else: + new_path = self._get_file_stash(path) + + self._moves.append((path, new_path)) + if path_is_dir and os.path.isdir(new_path): + # If we're moving a directory, we need to + # remove the destination first or else it will be + # moved to inside the existing directory. + # We just created new_path ourselves, so it will + # be removable. + os.rmdir(new_path) + renames(path, new_path) + return new_path + + def commit(self) -> None: + """Commits the uninstall by removing stashed files.""" + for save_dir in self._save_dirs.values(): + save_dir.cleanup() + self._moves = [] + self._save_dirs = {} + + def rollback(self) -> None: + """Undoes the uninstall by moving stashed files back.""" + for p in self._moves: + logger.info("Moving to %s\n from %s", *p) + + for new_path, path in self._moves: + try: + logger.debug("Replacing %s from %s", new_path, path) + if os.path.isfile(new_path) or os.path.islink(new_path): + os.unlink(new_path) + elif os.path.isdir(new_path): + rmtree(new_path) + renames(path, new_path) + except OSError as ex: + logger.error("Failed to restore %s", new_path) + logger.debug("Exception: %s", ex) + + self.commit() + + @property + def can_rollback(self) -> bool: + return bool(self._moves) + + +class UninstallPathSet: + """A set of file paths to be removed in the uninstallation of a + requirement.""" + + def __init__(self, dist: BaseDistribution) -> None: + self._paths: Set[str] = set() + self._refuse: Set[str] = set() + self._pth: Dict[str, UninstallPthEntries] = {} + self._dist = dist + self._moved_paths = StashedUninstallPathSet() + # Create local cache of normalize_path results. Creating an UninstallPathSet + # can result in hundreds/thousands of redundant calls to normalize_path with + # the same args, which hurts performance. + self._normalize_path_cached = functools.lru_cache()(normalize_path) + + def _permitted(self, path: str) -> bool: + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + # aka is_local, but caching normalized sys.prefix + if not running_under_virtualenv(): + return True + return path.startswith(self._normalize_path_cached(sys.prefix)) + + def add(self, path: str) -> None: + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(self._normalize_path_cached(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self._paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == ".py": + self.add(cache_from_source(path)) + + def add_pth(self, pth_file: str, entry: str) -> None: + pth_file = self._normalize_path_cached(pth_file) + if self._permitted(pth_file): + if pth_file not in self._pth: + self._pth[pth_file] = UninstallPthEntries(pth_file) + self._pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def remove(self, auto_confirm: bool = False, verbose: bool = False) -> None: + """Remove paths in ``self._paths`` with confirmation (unless + ``auto_confirm`` is True).""" + + if not self._paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self._dist.raw_name, + ) + return + + dist_name_version = f"{self._dist.raw_name}-{self._dist.version}" + logger.info("Uninstalling %s:", dist_name_version) + + with indent_log(): + if auto_confirm or self._allowed_to_proceed(verbose): + moved = self._moved_paths + + for_rename = compress_for_rename(self._paths) + + for path in sorted(compact(for_rename)): + moved.stash(path) + logger.verbose("Removing file or directory %s", path) + + for pth in self._pth.values(): + pth.remove() + + logger.info("Successfully uninstalled %s", dist_name_version) + + def _allowed_to_proceed(self, verbose: bool) -> bool: + """Display which files would be deleted and prompt for confirmation""" + + def _display(msg: str, paths: Iterable[str]) -> None: + if not paths: + return + + logger.info(msg) + with indent_log(): + for path in sorted(compact(paths)): + logger.info(path) + + if not verbose: + will_remove, will_skip = compress_for_output_listing(self._paths) + else: + # In verbose mode, display all the files that are going to be + # deleted. + will_remove = set(self._paths) + will_skip = set() + + _display("Would remove:", will_remove) + _display("Would not remove (might be manually added):", will_skip) + _display("Would not remove (outside of prefix):", self._refuse) + if verbose: + _display("Will actually move:", compress_for_rename(self._paths)) + + return ask("Proceed (Y/n)? ", ("y", "n", "")) != "n" + + def rollback(self) -> None: + """Rollback the changes previously made by remove().""" + if not self._moved_paths.can_rollback: + logger.error( + "Can't roll back %s; was not uninstalled", + self._dist.raw_name, + ) + return + logger.info("Rolling back uninstall of %s", self._dist.raw_name) + self._moved_paths.rollback() + for pth in self._pth.values(): + pth.rollback() + + def commit(self) -> None: + """Remove temporary save dir: rollback will no longer be possible.""" + self._moved_paths.commit() + + @classmethod + def from_dist(cls, dist: BaseDistribution) -> "UninstallPathSet": + dist_location = dist.location + info_location = dist.info_location + if dist_location is None: + logger.info( + "Not uninstalling %s since it is not installed", + dist.canonical_name, + ) + return cls(dist) + + normalized_dist_location = normalize_path(dist_location) + if not dist.local: + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.canonical_name, + normalized_dist_location, + sys.prefix, + ) + return cls(dist) + + if normalized_dist_location in { + p + for p in {sysconfig.get_path("stdlib"), sysconfig.get_path("platstdlib")} + if p + }: + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.canonical_name, + normalized_dist_location, + ) + return cls(dist) + + paths_to_remove = cls(dist) + develop_egg_link = egg_link_path_from_location(dist.raw_name) + + # Distribution is installed with metadata in a "flat" .egg-info + # directory. This means it is not a modern .dist-info installation, an + # egg, or legacy editable. + setuptools_flat_installation = ( + dist.installed_with_setuptools_egg_info + and info_location is not None + and os.path.exists(info_location) + # If dist is editable and the location points to a ``.egg-info``, + # we are in fact in the legacy editable case. + and not info_location.endswith(f"{dist.setuptools_filename}.egg-info") + ) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if setuptools_flat_installation: + if info_location is not None: + paths_to_remove.add(info_location) + installed_files = dist.iter_declared_entries() + if installed_files is not None: + for installed_file in installed_files: + paths_to_remove.add(os.path.join(dist_location, installed_file)) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.is_file("top_level.txt"): + try: + namespace_packages = dist.read_text("namespace_packages.txt") + except FileNotFoundError: + namespaces = [] + else: + namespaces = namespace_packages.splitlines(keepends=False) + for top_level_pkg in [ + p + for p in dist.read_text("top_level.txt").splitlines() + if p and p not in namespaces + ]: + path = os.path.join(dist_location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(f"{path}.py") + paths_to_remove.add(f"{path}.pyc") + paths_to_remove.add(f"{path}.pyo") + + elif dist.installed_by_distutils: + raise UninstallationError( + "Cannot uninstall {!r}. It is a distutils installed project " + "and thus we cannot accurately determine which files belong " + "to it which would lead to only a partial uninstall.".format( + dist.raw_name, + ) + ) + + elif dist.installed_as_egg: + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist_location) + easy_install_egg = os.path.split(dist_location)[1] + easy_install_pth = os.path.join( + os.path.dirname(dist_location), + "easy-install.pth", + ) + paths_to_remove.add_pth(easy_install_pth, "./" + easy_install_egg) + + elif dist.installed_with_dist_info: + for path in uninstallation_paths(dist): + paths_to_remove.add(path) + + elif develop_egg_link: + # PEP 660 modern editable is handled in the ``.dist-info`` case + # above, so this only covers the setuptools-style editable. + with open(develop_egg_link) as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + normalized_link_pointer = paths_to_remove._normalize_path_cached( + link_pointer + ) + assert os.path.samefile( + normalized_link_pointer, normalized_dist_location + ), ( + f"Egg-link {develop_egg_link} (to {link_pointer}) does not match " + f"installed location of {dist.raw_name} (at {dist_location})" + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join( + os.path.dirname(develop_egg_link), "easy-install.pth" + ) + paths_to_remove.add_pth(easy_install_pth, dist_location) + + else: + logger.debug( + "Not sure how to uninstall: %s - Check: %s", + dist, + dist_location, + ) + + if dist.in_usersite: + bin_dir = get_bin_user() + else: + bin_dir = get_bin_prefix() + + # find distutils scripts= scripts + try: + for script in dist.iter_distutils_script_names(): + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, f"{script}.bat")) + except (FileNotFoundError, NotADirectoryError): + pass + + # find console_scripts and gui_scripts + def iter_scripts_to_remove( + dist: BaseDistribution, + bin_dir: str, + ) -> Generator[str, None, None]: + for entry_point in dist.iter_entry_points(): + if entry_point.group == "console_scripts": + yield from _script_names(bin_dir, entry_point.name, False) + elif entry_point.group == "gui_scripts": + yield from _script_names(bin_dir, entry_point.name, True) + + for s in iter_scripts_to_remove(dist, bin_dir): + paths_to_remove.add(s) + + return paths_to_remove + + +class UninstallPthEntries: + def __init__(self, pth_file: str) -> None: + self.file = pth_file + self.entries: Set[str] = set() + self._saved_lines: Optional[List[bytes]] = None + + def add(self, entry: str) -> None: + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + # os.path.splitdrive is used instead of os.path.isabs because isabs + # treats non-absolute paths with drive letter markings like c:foo\bar + # as absolute paths. It also does not recognize UNC paths if they don't + # have more than "\\sever\share". Valid examples: "\\server\share\" or + # "\\server\share\folder". + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace("\\", "/") + self.entries.add(entry) + + def remove(self) -> None: + logger.verbose("Removing pth entries from %s:", self.file) + + # If the file doesn't exist, log a warning and return + if not os.path.isfile(self.file): + logger.warning("Cannot remove entries from nonexistent file %s", self.file) + return + with open(self.file, "rb") as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b"\r\n" in line for line in lines): + endline = "\r\n" + else: + endline = "\n" + # handle missing trailing newline + if lines and not lines[-1].endswith(endline.encode("utf-8")): + lines[-1] = lines[-1] + endline.encode("utf-8") + for entry in self.entries: + try: + logger.verbose("Removing entry: %s", entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, "wb") as fh: + fh.writelines(lines) + + def rollback(self) -> bool: + if self._saved_lines is None: + logger.error("Cannot roll back changes to %s, none were made", self.file) + return False + logger.debug("Rolling %s back to previous state", self.file) + with open(self.file, "wb") as fh: + fh.writelines(self._saved_lines) + return True diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1061c6347bef75033bb72c950f2440263cf87819 GIT binary patch literal 218 zcmZ9GO^U)m5QWqJz#w=K7p9nTD}v%y5ibzhOc6_ys-~+G$VohdSJ6udo*zEgYpnP^*!LIXhx+BSrCg;*TLUb zddp($IXOqmnSsgg2zFV*5}vV~hdj4JE4M?N6K7>k?uKrK?aa$-VU083;DZhBGQQcf zz}>b8g>{SFVu|yRCGKPV8m+J~@>;c*1Y5T8ug6&ycQP=}FR9YT-z;@1ia2X=bMkhf zG#+n3e~?PZq0q=KeB}NHx=VqMmNx^Fbp*Q%SjZC=S_wyK4hc$7T4IB@?V%_qtR*gZ z=+92Koy0@dAJtmEnY$J%*a9W8AxKk~Lg+|BUzw%u4s`EDYEYI!YDl6#cK|YFJPCRr zi>`g726xJt{K*oUU8Xs1)p|VJ|KfXmV~?A~4WV!6WmaVg$xSJR#PT=_vTaEnlgH2~ ziq&j3-wlEtkwc(jmB&SJSBgKNt5vWiIzp#i75plSp15)C($7kzy@Dziw1-d(gDmX? z&{9V|t=9GP0h4dMQ&kBifu zv2!SHGLPl~d*iX=U;VqVI`&s5NHBV{A1Q3tYzm0{Kh=>7cuyWj)3W3V!c<8`QJPJ8 z5;wEd1C6SyQHe=zBxC~e{^}FW}J=?nXHk&NahThy=kyYoU9W|rfT0$2p+OS%XZH{SzZ~R`*wf%@}W)M zoy}@LsGpv==pQzSJiRs}=f?}p{rZ=0=%`X!h1_TzGoBumY0;B37*ky?BC~?6Y=3Aa zHmFa}QI)3Jmp^|8n^YxaY^9|+``>>Mr)`*Gfv35x(1)pbRle{1^=Zw1l&i?mxT_z~ z9NL`oXYArWyZC~wKV?@AR@R=l{-bZki);Ih3uEW&aqH^Xx%$es&RJu2_HCoi&%b79 Hs@U%Vgf%*e literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py new file mode 100644 index 0000000..42dade1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/base.py @@ -0,0 +1,20 @@ +from typing import Callable, List, Optional + +from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_set import RequirementSet + +InstallRequirementProvider = Callable[ + [str, Optional[InstallRequirement]], InstallRequirement +] + + +class BaseResolver: + def resolve( + self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + ) -> RequirementSet: + raise NotImplementedError() + + def get_installation_order( + self, req_set: RequirementSet + ) -> List[InstallRequirement]: + raise NotImplementedError() diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/__init__.py new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000000000000000000000000000000000000..fc18a03a001213f324a495c59a5c80c6f345e0d0 GIT binary patch literal 225 zcmZ8bL5c!F44hUM7Qu&jaKf%v5frb=;s=Iy3}dt>X*#ViH-E8j@GJTW!51vvJZ;a_ zf~2aFI@E12=w}J{Ylm8XwCf$uUb_5dO*S;EOBOoGJ=@Id3_Gb2)M{-ReuC_=j0$Vn zNTgC>DO|~7KANdg#Wiw-{Dgc0M;q`>+zF5KuWwL_IP$hU*a{Uqc_@e@2DT0Ys<>k$ nVQ^U4vVkH!DHcWUo7d;0mhI8uG<>8&CXKe@uWO76-K72jQG-Az literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..00a102f368e7a9c6188ea38b53819aae94daaf35 GIT binary patch literal 22460 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

^rCbS(kN&pL;~e~v?Jig3 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py new file mode 100644 index 0000000..5ddb848 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -0,0 +1,598 @@ +"""Dependency Resolution + +The dependency resolution in pip is performed as follows: + +for top-level requirements: + a. only one spec allowed per project, regardless of conflicts or not. + otherwise a "double requirement" exception is raised + b. they override sub-dependency requirements. +for sub-dependencies + a. "first found, wins" (where the order is breadth first) +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import logging +import sys +from collections import defaultdict +from itertools import chain +from typing import DefaultDict, Iterable, List, Optional, Set, Tuple + +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.requirements import Requirement + +from pip._internal.cache import WheelCache +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + HashError, + HashErrors, + InstallationError, + NoneMetadataError, + UnsupportedPythonVersion, +) +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.req_install import ( + InstallRequirement, + check_invalid_constraint_type, +) +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider +from pip._internal.utils import compatibility_tags +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.direct_url_helpers import direct_url_from_link +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import normalize_version_info +from pip._internal.utils.packaging import check_requires_python + +logger = logging.getLogger(__name__) + +DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] + + +def _check_dist_requires_python( + dist: BaseDistribution, + version_info: Tuple[int, int, int], + ignore_requires_python: bool = False, +) -> None: + """ + Check whether the given Python version is compatible with a distribution's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + + :raises UnsupportedPythonVersion: When the given Python version isn't + compatible. + """ + # This idiosyncratically converts the SpecifierSet to str and let + # check_requires_python then parse it again into SpecifierSet. But this + # is the legacy resolver so I'm just not going to bother refactoring. + try: + requires_python = str(dist.requires_python) + except FileNotFoundError as e: + raise NoneMetadataError(dist, str(e)) + try: + is_compatible = check_requires_python( + requires_python, + version_info=version_info, + ) + except specifiers.InvalidSpecifier as exc: + logger.warning( + "Package %r has an invalid Requires-Python: %s", dist.raw_name, exc + ) + return + + if is_compatible: + return + + version = ".".join(map(str, version_info)) + if ignore_requires_python: + logger.debug( + "Ignoring failed Requires-Python check for package %r: %s not in %r", + dist.raw_name, + version, + requires_python, + ) + return + + raise UnsupportedPythonVersion( + "Package {!r} requires a different Python: {} not in {!r}".format( + dist.raw_name, version, requires_python + ) + ) + + +class Resolver(BaseResolver): + """Resolves which packages need to be installed/uninstalled to perform \ + the requested operation without breaking the requirements of any package. + """ + + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer: RequirementPreparer, + finder: PackageFinder, + wheel_cache: Optional[WheelCache], + make_install_req: InstallRequirementProvider, + use_user_site: bool, + ignore_dependencies: bool, + ignore_installed: bool, + ignore_requires_python: bool, + force_reinstall: bool, + upgrade_strategy: str, + py_version_info: Optional[Tuple[int, ...]] = None, + ) -> None: + super().__init__() + assert upgrade_strategy in self._allowed_strategies + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + self._py_version_info = py_version_info + + self.preparer = preparer + self.finder = finder + self.wheel_cache = wheel_cache + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + self.ignore_dependencies = ignore_dependencies + self.ignore_installed = ignore_installed + self.ignore_requires_python = ignore_requires_python + self.use_user_site = use_user_site + self._make_install_req = make_install_req + + self._discovered_dependencies: DiscoveredDependencies = defaultdict(list) + + def resolve( + self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + ) -> RequirementSet: + """Resolve what operations need to be done + + As a side-effect of this method, the packages (and their dependencies) + are downloaded, unpacked and prepared for installation. This + preparation is done by ``pip.operations.prepare``. + + Once PyPI has static dependency metadata available, it would be + possible to move the preparation to become a step separated from + dependency resolution. + """ + requirement_set = RequirementSet(check_supported_wheels=check_supported_wheels) + for req in root_reqs: + if req.constraint: + check_invalid_constraint_type(req) + self._add_requirement_to_set(requirement_set, req) + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # _populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs: List[InstallRequirement] = [] + hash_errors = HashErrors() + for req in chain(requirement_set.all_requirements, discovered_reqs): + try: + discovered_reqs.extend(self._resolve_one(requirement_set, req)) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + return requirement_set + + def _add_requirement_to_set( + self, + requirement_set: RequirementSet, + install_req: InstallRequirement, + parent_req_name: Optional[str] = None, + extras_requested: Optional[Iterable[str]] = None, + ) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]]: + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :param extras_requested: an iterable of extras used to evaluate the + environment markers. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + # If the markers do not match, ignore this requirement. + if not install_req.match_markers(extras_requested): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + install_req.name, + install_req.markers, + ) + return [], None + + # If the wheel is not supported, raise an error. + # Should check this after filtering out based on environment markers to + # allow specifying different wheels based on the environment/OS, in a + # single requirements file. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + tags = compatibility_tags.get_supported() + if requirement_set.check_supported_wheels and not wheel.supported(tags): + raise InstallationError( + f"{wheel.filename} is not a supported wheel on this platform." + ) + + # This next bit is really a sanity check. + assert ( + not install_req.user_supplied or parent_req_name is None + ), "a user supplied req shouldn't have a parent" + + # Unnamed requirements are scanned again and the requirement won't be + # added as a dependency until after scanning. + if not install_req.name: + requirement_set.add_unnamed_requirement(install_req) + return [install_req], None + + try: + existing_req: Optional[ + InstallRequirement + ] = requirement_set.get_requirement(install_req.name) + except KeyError: + existing_req = None + + has_conflicting_requirement = ( + parent_req_name is None + and existing_req + and not existing_req.constraint + and existing_req.extras == install_req.extras + and existing_req.req + and install_req.req + and existing_req.req.specifier != install_req.req.specifier + ) + if has_conflicting_requirement: + raise InstallationError( + "Double requirement given: {} (already in {}, name={!r})".format( + install_req, existing_req, install_req.name + ) + ) + + # When no existing requirement exists, add the requirement as a + # dependency and it will be scanned again after. + if not existing_req: + requirement_set.add_named_requirement(install_req) + # We'd want to rescan this requirement later + return [install_req], install_req + + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + if install_req.constraint or not existing_req.constraint: + return [], existing_req + + does_not_satisfy_constraint = install_req.link and not ( + existing_req.link and install_req.link.path == existing_req.link.path + ) + if does_not_satisfy_constraint: + raise InstallationError( + f"Could not satisfy constraints for '{install_req.name}': " + "installation from path or url cannot be " + "constrained to a version" + ) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + # If we're now installing a user supplied requirement, + # mark the existing object as such. + if install_req.user_supplied: + existing_req.user_supplied = True + existing_req.extras = tuple( + sorted(set(existing_req.extras) | set(install_req.extras)) + ) + logger.debug( + "Setting %s extras to: %s", + existing_req, + existing_req.extras, + ) + # Return the existing requirement for addition to the parent and + # scanning again. + return [existing_req], existing_req + + def _is_upgrade_allowed(self, req: InstallRequirement) -> bool: + if self.upgrade_strategy == "to-satisfy-only": + return False + elif self.upgrade_strategy == "eager": + return True + else: + assert self.upgrade_strategy == "only-if-needed" + return req.user_supplied or req.constraint + + def _set_req_to_reinstall(self, req: InstallRequirement) -> None: + """ + Set a requirement to be installed. + """ + # Don't uninstall the conflict if doing a user install and the + # conflict is not a user install. + if not self.use_user_site or req.satisfied_by.in_usersite: + req.should_reinstall = True + req.satisfied_by = None + + def _check_skip_installed( + self, req_to_install: InstallRequirement + ) -> Optional[str]: + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + if self.ignore_installed: + return None + + req_to_install.check_if_exists(self.use_user_site) + if not req_to_install.satisfied_by: + return None + + if self.force_reinstall: + self._set_req_to_reinstall(req_to_install) + return None + + if not self._is_upgrade_allowed(req_to_install): + if self.upgrade_strategy == "only-if-needed": + return "already satisfied, skipping upgrade" + return "already satisfied" + + # Check for the possibility of an upgrade. For link-based + # requirements we have to pull the tree down and inspect to assess + # the version #, so it's handled way down. + if not req_to_install.link: + try: + self.finder.find_requirement(req_to_install, upgrade=True) + except BestVersionAlreadyInstalled: + # Then the best version is installed. + return "already up-to-date" + except DistributionNotFound: + # No distribution found, so we squash the error. It will + # be raised later when we re-try later to do the install. + # Why don't we just raise here? + pass + + self._set_req_to_reinstall(req_to_install) + return None + + def _find_requirement_link(self, req: InstallRequirement) -> Optional[Link]: + upgrade = self._is_upgrade_allowed(req) + best_candidate = self.finder.find_requirement(req, upgrade) + if not best_candidate: + return None + + # Log a warning per PEP 592 if necessary before returning. + link = best_candidate.link + if link.is_yanked: + reason = link.yanked_reason or "" + msg = ( + # Mark this as a unicode string to prevent + # "UnicodeEncodeError: 'ascii' codec can't encode character" + # in Python 2 when the reason contains non-ascii characters. + "The candidate selected for download or install is a " + f"yanked version: {best_candidate}\n" + f"Reason for being yanked: {reason}" + ) + logger.warning(msg) + + return link + + def _populate_link(self, req: InstallRequirement) -> None: + """Ensure that if a link can be found for this, that it is found. + + Note that req.link may still be None - if the requirement is already + installed and not needed to be upgraded based on the return value of + _is_upgrade_allowed(). + + If preparer.require_hashes is True, don't use the wheel cache, because + cached wheels, always built locally, have different hashes than the + files downloaded from the index server and thus throw false hash + mismatches. Furthermore, cached wheels at present have undeterministic + contents due to file modification times. + """ + if req.link is None: + req.link = self._find_requirement_link(req) + + if self.wheel_cache is None or self.preparer.require_hashes: + return + cache_entry = self.wheel_cache.get_cache_entry( + link=req.link, + package_name=req.name, + supported_tags=get_supported(), + ) + if cache_entry is not None: + logger.debug("Using cached wheel link: %s", cache_entry.link) + if req.link is req.original_link and cache_entry.persistent: + req.cached_wheel_source_link = req.link + if cache_entry.origin is not None: + req.download_info = cache_entry.origin + else: + # Legacy cache entry that does not have origin.json. + # download_info may miss the archive_info.hashes field. + req.download_info = direct_url_from_link( + req.link, link_is_in_wheel_cache=cache_entry.persistent + ) + req.link = cache_entry.link + + def _get_dist_for(self, req: InstallRequirement) -> BaseDistribution: + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + if req.editable: + return self.preparer.prepare_editable_requirement(req) + + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req.satisfied_by is None + skip_reason = self._check_skip_installed(req) + + if req.satisfied_by: + return self.preparer.prepare_installed_requirement(req, skip_reason) + + # We eagerly populate the link, since that's our "legacy" behavior. + self._populate_link(req) + dist = self.preparer.prepare_linked_requirement(req) + + # NOTE + # The following portion is for determining if a certain package is + # going to be re-installed/upgraded or not and reporting to the user. + # This should probably get cleaned up in a future refactor. + + # req.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req.check_if_exists(self.use_user_site) + + if req.satisfied_by: + should_modify = ( + self.upgrade_strategy != "to-satisfy-only" + or self.force_reinstall + or self.ignore_installed + or req.link.scheme == "file" + ) + if should_modify: + self._set_req_to_reinstall(req) + else: + logger.info( + "Requirement already satisfied (use --upgrade to upgrade): %s", + req, + ) + return dist + + def _resolve_one( + self, + requirement_set: RequirementSet, + req_to_install: InstallRequirement, + ) -> List[InstallRequirement]: + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # Parse and return dependencies + dist = self._get_dist_for(req_to_install) + # This will raise UnsupportedPythonVersion if the given Python + # version isn't compatible with the distribution's Requires-Python. + _check_dist_requires_python( + dist, + version_info=self._py_version_info, + ignore_requires_python=self.ignore_requires_python, + ) + + more_reqs: List[InstallRequirement] = [] + + def add_req(subreq: Requirement, extras_requested: Iterable[str]) -> None: + # This idiosyncratically converts the Requirement to str and let + # make_install_req then parse it again into Requirement. But this is + # the legacy resolver so I'm just not going to bother refactoring. + sub_install_req = self._make_install_req(str(subreq), req_to_install) + parent_req_name = req_to_install.name + to_scan_again, add_to_parent = self._add_requirement_to_set( + requirement_set, + sub_install_req, + parent_req_name=parent_req_name, + extras_requested=extras_requested, + ) + if parent_req_name and add_to_parent: + self._discovered_dependencies[parent_req_name].append(add_to_parent) + more_reqs.extend(to_scan_again) + + with indent_log(): + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not requirement_set.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + # 'unnamed' requirements can only come from being directly + # provided by the user. + assert req_to_install.user_supplied + self._add_requirement_to_set( + requirement_set, req_to_install, parent_req_name=None + ) + + if not self.ignore_dependencies: + if req_to_install.extras: + logger.debug( + "Installing extra requirements: %r", + ",".join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.iter_provided_extras()) + ) + for missing in missing_requested: + logger.warning( + "%s %s does not provide the extra '%s'", + dist.raw_name, + dist.version, + missing, + ) + + available_requested = sorted( + set(dist.iter_provided_extras()) & set(req_to_install.extras) + ) + for subreq in dist.iter_dependencies(available_requested): + add_req(subreq, extras_requested=available_requested) + + return more_reqs + + def get_installation_order( + self, req_set: RequirementSet + ) -> List[InstallRequirement]: + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs: Set[InstallRequirement] = set() + + def schedule(req: InstallRequirement) -> None: + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._discovered_dependencies[req.name]: + schedule(dep) + order.append(req) + + for install_req in req_set.requirements.values(): + schedule(install_req) + return order diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ce4e953f60ac66a268477186efabd6166bf7d0f GIT binary patch literal 229 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..7b71c38d478368f06825d3fdd2112347401c115b GIT binary patch literal 30419 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..dc71165db4ae573bded7b68c890313671c82c9a5 GIT binary patch literal 6229 zcmcIoTWl2989uYu*_+ol@cI%Mj1yQeyI|5Z2_`C+Bp9bAN@%OB5-sDMv-ZI3%;wCj zVbLO7ic~|8LevZHo7IaN>Q*98$y1f*_QeZC-cG4X)kbR6mnBZ6B&w?Z|C!xcV>@jg z(v|kiIdlH=&wtMU{kQY`SS(E7cmH=Db!;;sf5k!b7i@G^{so;IL?IcX2#QA&G6Fok zny28+cm;gt(|iSg#$O0z0)=2ESP(N}A(ROf!kKU(l8JB{zZNaTGBM}}l%UpDh-cyg zp%EIR!6`yRG~Nqe$$MIDrk(m(fO?&oOo#5F-b{xgQjxYJl@fxUv&yH0FZ(i`N`%HY zoDV2bnD6>8^D&t3_K@d^(sr3BaoVxyb#|ZGRQ4p><=+Z~e9KZb)l#V`S%##^=T)sN zS=u#2S$UU~GfWE@ z8%4@sm7%AkF-ubQ8AF?)W;jo++%zm7mgqT)$vNaZEENrl>Xs^NuyC@ZY6^a!U??RG z#b8E3GK~_;(P2jf%G7)rNWzM0T1=fR;di70wPX-nc0LH3oTiNC4Mt&;r5rGa4KWGC zf=5Bta}hKzjJG+T@C~*y79d^%4d7lR-On@|72#%^o@S?2+-YiRW$aA-!yiM?iFWQlqvwW}y-c zW{W3^ct)~jdtauO?LS#6Y7}@yvxjAqe%txrM}QFmFGEL~7_j}So;OT8z^GMXdJYMi zKOc0q!+)g$%{URj!Yp@wu|ah~sF0)NwIm_0dI-7Tx!|pMD#8q5!nKHpRJ<;oui~2( zu5GILek|M&1X2-BH}KnF#2*#UV&e+}xwhL!%EE8FXN9Ea{9<#XT*aqOJ%`A3cVV77 z1t^x_cXAq#sx}SLKlq822{j(Fb-Yp-KpnvE}Y%X}c zyB6uZu2&;NwP^Prx)VQmZZ1@dbT35)U|ehKxf-vDo0rADs@OOG^$*1D58|-!GZT?_ z>FH4bs;ikCpv(Qa$h-=#9As8V16Sm0i(&Q%(NW$=6;~ztH017IG;5}fq@`Li0fteP+Y{@;9-rETJ;)4*8Gwdcj>LMPYxk`} zZ1${bO=saOixjL`VFN($1TK0N8V=xkH;Hs#I#ElGEGM6?CZAr~dEhGnGfH*okZ{N= zY>eT5!~7R|R|mDHImXFTy(DOp|k89_*YMx6}f}ZFJ>4wQ7ViOz5VWQJR|O7S1iylngZAmO-1t zXy%k*!o}Ia^oE8{&p}p(2^?lflTHE!X%B=zW;xc6d6EH6fI(#AtRNpzXJie;dgyzQ zZOM5Ih?q*P6-QF@=wKkvg406+ot~wv86v%`12EY#Zq^UObr#&a&VrpSf&p&<`WJA` zThKTWsBd)yifj;p?nks}(JKjS;Obm+jLgF+7EbAO>mZ{=6exj`s_ zN`+)RijeUt9_oWS!%qXP3PbVIAQgEn;iDnNPs23AXGJ9d^*~e!(iqeevAjuXvgUxR~EOUM1LJyY}h`~hk(BAvTOgDu5vutZpYW8Tr%d0 zhn!5>Y~-sLcZ$rIQ58tZ-Gm0;A{Ltw%}v!1o=_HGuqm01vdaL!pJfV5KrVA|;9odw)HkhUrilQZLd%#vzh(!Tm+9Mf2n zgo0EO)IDb$Q?Wx+)Pj5svUDE)?2bl40Ph-EtKF{ZkewF6WdQ%Is+hUe9vY(r=GrTx z(AJ+0ztneL?=d9%v4sr>wx~l zk|q7N&!k$OZ37XUcs_`|?a;JRUnhQxSy--G+3fkm8t|o>GbLPx3}#8ajr0%A`L4xl zecSnCaQ~d|uDG?fZQq>l198g=P!X(mAem=z=sy&Wahv>*Ftc4<=qN(dG%g7@Jrn0+ zilOhYpkU+xy(!pT6NWX`^x9N;mNA1t+OiAyuszV2c%D)tC5dG5^dONgz^?k&@5-J0 zE-YF>DIv2KNqlf2eWz_?NgQFr@NQk1#wEW8Gv5c6uu!@%_Mb4$_}ZPG_QeV6MOrGyLl;3O%?E2^QpYrh1+}gf3T_<3I#nbHlol#Wv1!8DzGr%!xEz z2nX>=P!HD^st+y+^fEgLJ#*DYKsl3!p>3=jXXXYjL1oX9`;qu^Wbk%maKZZJ#h+fR zb@kkfcV2(|jq;M1;2WJtcH6*GmkTtT1*e^LZr-3D&1PSRNY=;Vp*+#HJn6SP8Bc zAeOVkFm^XQa1dScf8E~ltH=u@YrsT;SLrmELt6YQxoB5EApOt&s&dK6&vM`c90(Uj3EL`a* z21_{?IC#s{sGAvjR8Rrj?{RIg=W*Q8ygYPu=6DF#IXH)msk0+C9{lIxr~`>-7&=o# zqInydx>pc{2Od%It#lKi<4YE>ZJ8^69*7SSj>Jwv2 zBV*O%@umLb)lK6|r1LM{_F6((=&dGpSACo63DUc537&yAVd(m~C26GEv%gMY@Sga7 zZ}rLX73|jsyM&=yJW==Jb6aw0*JyRyK|Z?gkIku|iWNUh)rTJu5}Xd7Ejk#r=wR^4 SH-xm%yR>=7KM6efp8o^UvUN@X literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..b8dd81c0ecd08d2248a97dee1638b8c6f0ef3a73 GIT binary patch literal 10399 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 V-Ho0{2VS-C%}ay7;^@uD`X3duhFbst literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..a7473be8f6859d60148eb05337f7575ededf4fd7 GIT binary patch literal 4956 zcmd5=O>7(25#Hr4$)!k9)Q|rZ2YIfX*tBKRc3jI+TsyXew5nZMu~QUQTQ63-Pt?lG zUFPjlHdz|*K?fNq5cN{Kf2eYb3>S!ds(NaUM%#l4N072eP!&Z{7`>>Kdhw|5yu2gq@ot4VU5>96A>)}bCFUgExGYgy-<6f8)4KbGIK-?aC!jzu&IcFF4|CATJZb+X2d!c;3?P#k?6 zWZcr7Nn$yZlo7URFFQltne-TohDIFaCM61rF=(dz!$2e3(GBDONUy(o8iUfHE?Ua4 zOmH6JOe!WR+C|04kn2{FA6Q*p_djJI*u!VZtfmNr>N@4ss)Vxv*yyt4 z#Y}e6%G0c^*m=dwjxr17+1absxaH`oo&5&b)*8Ba?1F9UITLSKHZx7l>8zoTXA31~ z(lTGqoH&`ab%!1c-fp8!vN9-0nW-3A&&EXu9N6zpQ{2pg3k}9|B^kJNN0!T7qnlSR zQ=3KHb{fcC?$dL_zwdc=Ze&*cSUR{Sh1&bqT0A1OU*i9^ul-2Nthgk-whqjk8&esm z#o)!zt%VDts;1PMW+_wxT)B-?J(3r6>%!QbG|vAUkJ5`^(a6K z89=-fuJBWkTed$#+;^{o_K`|tD!F|PtPq(>?VP;_Cx0bevjUHCIX2>5zp^wu z^466B@~RE_D$y8#%g4Wk!O>wQh?|-|F+myJ5mNWIUZNEt=vWw0CJYEJ1LbfFf#(HJ z%84KnL0mUUx$_+eO+`D1&(wAxaHSIrjo^wH>{%yvBZikNT~RkDEH?tdC9gPc97otg zXOA}ocsEi|P$f4SBoQ~|Gs@7&lrf9BVU3O#bLkjE12S|i){U8y@^;RZ zKsel1c((vOb`ThCbUp;-J#07b?ev2VL_@i5jPfgux4Q{L4+_J(dzV|063f!Ts&sJv z`Uf}Ozwt~uwa8bcerQ)yUCXK7YN~hP`iD0j-l(R|&W2awsR!cn-qTh1i=SRebgXvl zUhX(v?Ku8;SGA*WHu|ZQd@p`4KKJ$c=m*L7lZ(T@8Ts|deDWE&^s#h#HTmYOu(J2S z^4??By~qB}NwFidUt8(^(sFmE+MRhU{i^MkZF9S42Uinqb7$^dnH~J3ZP!Yw^TBIB zKl-Di4_=;E79t-eA10q3>U&&yx@V}G8v22-n(BPI2eq6NR$$xf%bka+orj)vrmKnc zy}`A1c*jNu7i)VjaW640Az++4|RoG)_S|b-3!;&IA|8Hd(D$m8#wZJ zBfkp&^`{nSf^xE6O7T!ewQC}jPJSr`<;{fXmr`D76{S%ud7!+KfJf45V^NF2I0i*x zix#KxfF}WS39S|OBmLVx=fG`M9UfKagW>9r#hhJrBb{d4HLw@);1AQA` zO^A;vS;ORSDEE9WGN0*96w(f)0pE$oUtW^>1B4*cp<7br-bw|t-})JB%UCO}UwhL@ys9?&oL3^(1;S7FE5d3YMnF5)wm zU-}#%Z#99CUVyVY;m495SohMzmgB;xGX?+f3Oa}NhWQI zzply_{9f89A8rb`iR8;o5DzR4e^gqM-u@r>Y*!s+K{xT^YKPNesj?;DL<7$9{-&6M z)hoDdTZlQbFuWw4_RkF?e!7iipimN0HIFr1IgcwRA9&0s=RM|=I5o;I+(fof{-b`sefst{alC~!(jE` zk^+1w%aCz3i)DiEZuk&{uAi-K7DYC39DeQbZRc6hI{+_Zdew|)v8`5oA)WQ>qjT&M zNWc$U8;@sA;CcQtuIDrEp$rL~Pv8?LAoX>9~*n-tOM+d^xbp*C94x0Tk4P}@>b z+g5CiwS9pviScIBxfvy$naU{SoT9~iW^}SZa;a=aSLDf5PBB~4seB=yNzhD3_BmDa<65H#L${p{ea9*7Uk^c{W3ooRZhR;PF+0*?2OQmosuoQ%un* zzFH`}F*{>MD*Bs|DWpN8Z%Z>~uw1Zi*5;tFz$sjUm$`&b<`uu}ll@bIEZhSA%RVJA z<&y(Y4qD|PlpB-~+%-Z=!!6-~^{7Oc)?)uqMQy|y*9ha9pxg}YVJJu0yA%=L6@mMf zqCeJTwx4=)CYwoTw8~jD0_Uvh28p80lKf=aXT23KSjMGZYlCcoyT)JRl2Br)$Sv`c zOFR+bcFAW7s*;_eH!2R3q^ENumkK#$L`|u=RDR?PDNHMAO&xi?aG{`O((1_RLjGc5 z;>4cgs+zf&$7L9aUs3W`MzWa;BQr(qQX&7%_`c^x)QqO=nMtMJNL^Iak(tcQNHUYx z6p~M6M~I>pva?#IkY~j!3J!)fw^vq;&lJs25=LaSWKw^|gAH-JH3D?2jgZ~s9(VW6 z2|tRi!3~#2;jk03gR*283JaX(v|Zz-D_Ljte_*wr;|TH)q}Q)%gz=0golvD>4~f7| z=FEm<5?X=Gt=?qP#wZSIh3qExL~Ogee_?EXY(?xdLVctQYOAP4X{6LnLID<#n}(}9 zdC~noXb$LYOLN7L_kFVO1K}LEi?( zanJ}2{_l}4;4;*Zyc ziX#q|ETto6)TElyGAdYDIeDR|_j{4+>a-Ic;85CCn)q>14ZAu~g;cV-l#6^Vd#xW@=eEy1-X2z|&9WTtST zP{@)!P+a0Dcc=;_s2ypBJ=MFA-QvnVKG44yD|2vpD6HY79E7aY?v#)5rGC6Q#={;B zM$0~5V5pS;H3ye9zg96un)=HjKCo|*K4Kp}d*@;SAGZ_BL-)J!)k6=p zb-XZQk6Uf7tLcm={|KyvvYGrF!&1JWL0Po{W{vUuCcI(oImlk&c&Zk@3vlN;Ut#un z`)z;1F96KG(<;Ebe&WB%Q-crq2&V)1$QR#h@`{CxsH{G1JbF>dD{sz_F@3KWmmOdd zKbkE7yj92I&Ip7bYX}FjUTaN=)wZsB7AQ^2X3 zfg~=O**HZCx{_BF%?vU`45)(@mV6YDkOMHZl1{o!^=+F{h?`SA0vS`c#oI+gl#I|x zqwnMjFmmuvjDc5YLzld5|yT7Q227N zOV>iG!52>FH!9!iip0f?r2->t*fS`GydU=BfWp+?@1dALP^K6^amAU@a&vYUwCKIg zGhg3uRN|GGaX)lZk;ON;Ujcg2Z|m>fc;|)@dePYN;!5bnxf@SJX|dy@o_jqXZNImD zwRit=@BS6>fDt-S%V5+5RQygr;mbD>h#8J>r3aQ0-q=P>(1Tg$2`srMN`Yp#=R^?! z9ELF}!p)o9KZ-r2{i|Dtm$wezmw%dF%C3k<)Y&m{Fs502d7%J%u!^?I2T?fO*c(!NV zvWxgqk9o4e3$?!t-5o64e)s%BVm@JrJB-i{D#O&1tO~0*f;9Lb0hH62-sao7CsJaXr5Sb zQa|{T75M=Ttj;QEgFuolNLQc+SL*x{Z$@T_LX@nMQWZ58 zWSZL1-e#j}!$C8YF66Zogc8k$D+*B|%w+8#79J3=XZ03BheY4$DLXC~>NNCJ-+~Mw zQx|%upwYYUb$=!fmN|c8_;GZ?syCwBOY-9Wj}G5Eyf{?$^UWh`e&}F8$0!CAYRv+9Ok0$1>uZYKu&~YlHu{H(_(4vzO%uwffIcQ`r zHlU!?_00%%?$CTu|BN7hW?KOPD?#H%GmvFYU4RT-G=JcA-qtXOuHNEh-_3VNxEWQ9iK^cm+#CZ|da&`W^zb5ceFkB3FP zs~(VPD5=>(>2EkgHvW<32^ls!)m#DHf!x+ zaGgdw=SrTgo@2_Xt4F>CW2&qJKKsAx&Jn~dWE8S02MLX#{=Bp4>n-zU{v1je8N+NG zGEhp#u#AA8*{Bm(I*Hi?W~U&lA^}4w`rub5;rsZ2W02Kz*q(F_-#@?7`P^6X+JN3Z zcouQg2ZplcLK>pI8+yd02Jj?-V3vTtiYEIO_phJ^Lglba7pzlVz;+`JKwde?_KDUo z*5kDsm4FPpaVjX|0ZUL8lm>RZk_cI4I8;eADq*;bKnonF*v2l=L~F4>9IQCC&DJ

-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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9526cc0537c83920956df56b37ad318fed2a55fe GIT binary patch literal 12372 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py new file mode 100644 index 0000000..9c0ef5c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/base.py @@ -0,0 +1,141 @@ +from typing import FrozenSet, Iterable, Optional, Tuple, Union + +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName +from pip._vendor.packaging.version import LegacyVersion, Version + +from pip._internal.models.link import Link, links_equivalent +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.hashes import Hashes + +CandidateLookup = Tuple[Optional["Candidate"], Optional[InstallRequirement]] +CandidateVersion = Union[LegacyVersion, Version] + + +def format_name(project: NormalizedName, extras: FrozenSet[NormalizedName]) -> str: + if not extras: + return project + extras_expr = ",".join(sorted(extras)) + return f"{project}[{extras_expr}]" + + +class Constraint: + def __init__( + self, specifier: SpecifierSet, hashes: Hashes, links: FrozenSet[Link] + ) -> None: + self.specifier = specifier + self.hashes = hashes + self.links = links + + @classmethod + def empty(cls) -> "Constraint": + return Constraint(SpecifierSet(), Hashes(), frozenset()) + + @classmethod + def from_ireq(cls, ireq: InstallRequirement) -> "Constraint": + links = frozenset([ireq.link]) if ireq.link else frozenset() + return Constraint(ireq.specifier, ireq.hashes(trust_internet=False), links) + + def __bool__(self) -> bool: + return bool(self.specifier) or bool(self.hashes) or bool(self.links) + + def __and__(self, other: InstallRequirement) -> "Constraint": + if not isinstance(other, InstallRequirement): + return NotImplemented + specifier = self.specifier & other.specifier + hashes = self.hashes & other.hashes(trust_internet=False) + links = self.links + if other.link: + links = links.union([other.link]) + return Constraint(specifier, hashes, links) + + def is_satisfied_by(self, candidate: "Candidate") -> bool: + # Reject if there are any mismatched URL constraints on this package. + if self.links and not all(_match_link(link, candidate) for link in self.links): + return False + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) + + +class Requirement: + @property + def project_name(self) -> NormalizedName: + """The "project name" of a requirement. + + This is different from ``name`` if this requirement contains extras, + in which case ``name`` would contain the ``[...]`` part, while this + refers to the name of the project. + """ + raise NotImplementedError("Subclass should override") + + @property + def name(self) -> str: + """The name identifying this requirement in the resolver. + + This is different from ``project_name`` if this requirement contains + extras, where ``project_name`` would not contain the ``[...]`` part. + """ + raise NotImplementedError("Subclass should override") + + def is_satisfied_by(self, candidate: "Candidate") -> bool: + return False + + def get_candidate_lookup(self) -> CandidateLookup: + raise NotImplementedError("Subclass should override") + + def format_for_error(self) -> str: + raise NotImplementedError("Subclass should override") + + +def _match_link(link: Link, candidate: "Candidate") -> bool: + if candidate.source_link: + return links_equivalent(link, candidate.source_link) + return False + + +class Candidate: + @property + def project_name(self) -> NormalizedName: + """The "project name" of the candidate. + + This is different from ``name`` if this candidate contains extras, + in which case ``name`` would contain the ``[...]`` part, while this + refers to the name of the project. + """ + raise NotImplementedError("Override in subclass") + + @property + def name(self) -> str: + """The name identifying this candidate in the resolver. + + This is different from ``project_name`` if this candidate contains + extras, where ``project_name`` would not contain the ``[...]`` part. + """ + raise NotImplementedError("Override in subclass") + + @property + def version(self) -> CandidateVersion: + raise NotImplementedError("Override in subclass") + + @property + def is_installed(self) -> bool: + raise NotImplementedError("Override in subclass") + + @property + def is_editable(self) -> bool: + raise NotImplementedError("Override in subclass") + + @property + def source_link(self) -> Optional[Link]: + raise NotImplementedError("Override in subclass") + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + raise NotImplementedError("Override in subclass") + + def get_install_requirement(self) -> Optional[InstallRequirement]: + raise NotImplementedError("Override in subclass") + + def format_for_error(self) -> str: + raise NotImplementedError("Subclass should override") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py new file mode 100644 index 0000000..4125cda --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -0,0 +1,597 @@ +import logging +import sys +from typing import TYPE_CHECKING, Any, FrozenSet, Iterable, Optional, Tuple, Union, cast + +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.packaging.version import Version + +from pip._internal.exceptions import ( + HashError, + InstallationSubprocessError, + MetadataInconsistent, +) +from pip._internal.metadata import BaseDistribution +from pip._internal.models.link import Link, links_equivalent +from pip._internal.models.wheel import Wheel +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.direct_url_helpers import direct_url_from_link +from pip._internal.utils.misc import normalize_version_info + +from .base import Candidate, CandidateVersion, Requirement, format_name + +if TYPE_CHECKING: + from .factory import Factory + +logger = logging.getLogger(__name__) + +BaseCandidate = Union[ + "AlreadyInstalledCandidate", + "EditableCandidate", + "LinkCandidate", +] + +# Avoid conflicting with the PyPI package "Python". +REQUIRES_PYTHON_IDENTIFIER = cast(NormalizedName, "") + + +def as_base_candidate(candidate: Candidate) -> Optional[BaseCandidate]: + """The runtime version of BaseCandidate.""" + base_candidate_classes = ( + AlreadyInstalledCandidate, + EditableCandidate, + LinkCandidate, + ) + if isinstance(candidate, base_candidate_classes): + return candidate + return None + + +def make_install_req_from_link( + link: Link, template: InstallRequirement +) -> InstallRequirement: + assert not template.editable, "template is editable" + if template.req: + line = str(template.req) + else: + line = link.url + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + global_options=template.global_options, + hash_options=template.hash_options, + config_settings=template.config_settings, + ) + ireq.original_link = template.original_link + ireq.link = link + ireq.extras = template.extras + return ireq + + +def make_install_req_from_editable( + link: Link, template: InstallRequirement +) -> InstallRequirement: + assert template.editable, "template not editable" + ireq = install_req_from_editable( + link.url, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + permit_editable_wheels=template.permit_editable_wheels, + global_options=template.global_options, + hash_options=template.hash_options, + config_settings=template.config_settings, + ) + ireq.extras = template.extras + return ireq + + +def _make_install_req_from_dist( + dist: BaseDistribution, template: InstallRequirement +) -> InstallRequirement: + if template.req: + line = str(template.req) + elif template.link: + line = f"{dist.canonical_name} @ {template.link.url}" + else: + line = f"{dist.canonical_name}=={dist.version}" + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + global_options=template.global_options, + hash_options=template.hash_options, + config_settings=template.config_settings, + ) + ireq.satisfied_by = dist + return ireq + + +class _InstallRequirementBackedCandidate(Candidate): + """A candidate backed by an ``InstallRequirement``. + + This represents a package request with the target not being already + in the environment, and needs to be fetched and installed. The backing + ``InstallRequirement`` is responsible for most of the leg work; this + class exposes appropriate information to the resolver. + + :param link: The link passed to the ``InstallRequirement``. The backing + ``InstallRequirement`` will use this link to fetch the distribution. + :param source_link: The link this candidate "originates" from. This is + different from ``link`` when the link is found in the wheel cache. + ``link`` would point to the wheel cache, while this points to the + found remote link (e.g. from pypi.org). + """ + + dist: BaseDistribution + is_installed = False + + def __init__( + self, + link: Link, + source_link: Link, + ireq: InstallRequirement, + factory: "Factory", + name: Optional[NormalizedName] = None, + version: Optional[CandidateVersion] = None, + ) -> None: + self._link = link + self._source_link = source_link + self._factory = factory + self._ireq = ireq + self._name = name + self._version = version + self.dist = self._prepare() + + def __str__(self) -> str: + return f"{self.name} {self.version}" + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({str(self._link)!r})" + + def __hash__(self) -> int: + return hash((self.__class__, self._link)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, self.__class__): + return links_equivalent(self._link, other._link) + return False + + @property + def source_link(self) -> Optional[Link]: + return self._source_link + + @property + def project_name(self) -> NormalizedName: + """The normalised name of the project the candidate refers to""" + if self._name is None: + self._name = self.dist.canonical_name + return self._name + + @property + def name(self) -> str: + return self.project_name + + @property + def version(self) -> CandidateVersion: + if self._version is None: + self._version = self.dist.version + return self._version + + def format_for_error(self) -> str: + return "{} {} (from {})".format( + self.name, + self.version, + self._link.file_path if self._link.is_file else self._link, + ) + + def _prepare_distribution(self) -> BaseDistribution: + raise NotImplementedError("Override in subclass") + + def _check_metadata_consistency(self, dist: BaseDistribution) -> None: + """Check for consistency of project name and version of dist.""" + if self._name is not None and self._name != dist.canonical_name: + raise MetadataInconsistent( + self._ireq, + "name", + self._name, + dist.canonical_name, + ) + if self._version is not None and self._version != dist.version: + raise MetadataInconsistent( + self._ireq, + "version", + str(self._version), + str(dist.version), + ) + + def _prepare(self) -> BaseDistribution: + try: + dist = self._prepare_distribution() + except HashError as e: + # Provide HashError the underlying ireq that caused it. This + # provides context for the resulting error message to show the + # offending line to the user. + e.req = self._ireq + raise + except InstallationSubprocessError as exc: + # The output has been presented already, so don't duplicate it. + exc.context = "See above for output." + raise + + self._check_metadata_consistency(dist) + return dist + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + requires = self.dist.iter_dependencies() if with_requires else () + for r in requires: + yield from self._factory.make_requirements_from_spec(str(r), self._ireq) + yield self._factory.make_requires_python_requirement(self.dist.requires_python) + + def get_install_requirement(self) -> Optional[InstallRequirement]: + return self._ireq + + +class LinkCandidate(_InstallRequirementBackedCandidate): + is_editable = False + + def __init__( + self, + link: Link, + template: InstallRequirement, + factory: "Factory", + name: Optional[NormalizedName] = None, + version: Optional[CandidateVersion] = None, + ) -> None: + source_link = link + cache_entry = factory.get_wheel_cache_entry(source_link, name) + if cache_entry is not None: + logger.debug("Using cached wheel link: %s", cache_entry.link) + link = cache_entry.link + ireq = make_install_req_from_link(link, template) + assert ireq.link == link + if ireq.link.is_wheel and not ireq.link.is_file: + wheel = Wheel(ireq.link.filename) + wheel_name = canonicalize_name(wheel.name) + assert name == wheel_name, f"{name!r} != {wheel_name!r} for wheel" + # Version may not be present for PEP 508 direct URLs + if version is not None: + wheel_version = Version(wheel.version) + assert version == wheel_version, "{!r} != {!r} for wheel {}".format( + version, wheel_version, name + ) + + if cache_entry is not None: + assert ireq.link.is_wheel + assert ireq.link.is_file + if cache_entry.persistent and template.link is template.original_link: + ireq.cached_wheel_source_link = source_link + if cache_entry.origin is not None: + ireq.download_info = cache_entry.origin + else: + # Legacy cache entry that does not have origin.json. + # download_info may miss the archive_info.hashes field. + ireq.download_info = direct_url_from_link( + source_link, link_is_in_wheel_cache=cache_entry.persistent + ) + + super().__init__( + link=link, + source_link=source_link, + ireq=ireq, + factory=factory, + name=name, + version=version, + ) + + def _prepare_distribution(self) -> BaseDistribution: + preparer = self._factory.preparer + return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True) + + +class EditableCandidate(_InstallRequirementBackedCandidate): + is_editable = True + + def __init__( + self, + link: Link, + template: InstallRequirement, + factory: "Factory", + name: Optional[NormalizedName] = None, + version: Optional[CandidateVersion] = None, + ) -> None: + super().__init__( + link=link, + source_link=link, + ireq=make_install_req_from_editable(link, template), + factory=factory, + name=name, + version=version, + ) + + def _prepare_distribution(self) -> BaseDistribution: + return self._factory.preparer.prepare_editable_requirement(self._ireq) + + +class AlreadyInstalledCandidate(Candidate): + is_installed = True + source_link = None + + def __init__( + self, + dist: BaseDistribution, + template: InstallRequirement, + factory: "Factory", + ) -> None: + self.dist = dist + self._ireq = _make_install_req_from_dist(dist, template) + self._factory = factory + self._version = None + + # This is just logging some messages, so we can do it eagerly. + # The returned dist would be exactly the same as self.dist because we + # set satisfied_by in _make_install_req_from_dist. + # TODO: Supply reason based on force_reinstall and upgrade_strategy. + skip_reason = "already satisfied" + factory.preparer.prepare_installed_requirement(self._ireq, skip_reason) + + def __str__(self) -> str: + return str(self.dist) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.dist!r})" + + def __hash__(self) -> int: + return hash((self.__class__, self.name, self.version)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, self.__class__): + return self.name == other.name and self.version == other.version + return False + + @property + def project_name(self) -> NormalizedName: + return self.dist.canonical_name + + @property + def name(self) -> str: + return self.project_name + + @property + def version(self) -> CandidateVersion: + if self._version is None: + self._version = self.dist.version + return self._version + + @property + def is_editable(self) -> bool: + return self.dist.editable + + def format_for_error(self) -> str: + return f"{self.name} {self.version} (Installed)" + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + if not with_requires: + return + for r in self.dist.iter_dependencies(): + yield from self._factory.make_requirements_from_spec(str(r), self._ireq) + + def get_install_requirement(self) -> Optional[InstallRequirement]: + return None + + +class ExtrasCandidate(Candidate): + """A candidate that has 'extras', indicating additional dependencies. + + Requirements can be for a project with dependencies, something like + foo[extra]. The extras don't affect the project/version being installed + directly, but indicate that we need additional dependencies. We model that + by having an artificial ExtrasCandidate that wraps the "base" candidate. + + The ExtrasCandidate differs from the base in the following ways: + + 1. It has a unique name, of the form foo[extra]. This causes the resolver + to treat it as a separate node in the dependency graph. + 2. When we're getting the candidate's dependencies, + a) We specify that we want the extra dependencies as well. + b) We add a dependency on the base candidate. + See below for why this is needed. + 3. We return None for the underlying InstallRequirement, as the base + candidate will provide it, and we don't want to end up with duplicates. + + The dependency on the base candidate is needed so that the resolver can't + decide that it should recommend foo[extra1] version 1.0 and foo[extra2] + version 2.0. Having those candidates depend on foo=1.0 and foo=2.0 + respectively forces the resolver to recognise that this is a conflict. + """ + + def __init__( + self, + base: BaseCandidate, + extras: FrozenSet[str], + *, + comes_from: Optional[InstallRequirement] = None, + ) -> None: + """ + :param comes_from: the InstallRequirement that led to this candidate if it + differs from the base's InstallRequirement. This will often be the + case in the sense that this candidate's requirement has the extras + while the base's does not. Unlike the InstallRequirement backed + candidates, this requirement is used solely for reporting purposes, + it does not do any leg work. + """ + self.base = base + self.extras = frozenset(canonicalize_name(e) for e in extras) + # If any extras are requested in their non-normalized forms, keep track + # of their raw values. This is needed when we look up dependencies + # since PEP 685 has not been implemented for marker-matching, and using + # the non-normalized extra for lookup ensures the user can select a + # non-normalized extra in a package with its non-normalized form. + # TODO: Remove this attribute when packaging is upgraded to support the + # marker comparison logic specified in PEP 685. + self._unnormalized_extras = extras.difference(self.extras) + self._comes_from = comes_from if comes_from is not None else self.base._ireq + + def __str__(self) -> str: + name, rest = str(self.base).split(" ", 1) + return "{}[{}] {}".format(name, ",".join(self.extras), rest) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}(base={self.base!r}, extras={self.extras!r})" + + def __hash__(self) -> int: + return hash((self.base, self.extras)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, self.__class__): + return self.base == other.base and self.extras == other.extras + return False + + @property + def project_name(self) -> NormalizedName: + return self.base.project_name + + @property + def name(self) -> str: + """The normalised name of the project the candidate refers to""" + return format_name(self.base.project_name, self.extras) + + @property + def version(self) -> CandidateVersion: + return self.base.version + + def format_for_error(self) -> str: + return "{} [{}]".format( + self.base.format_for_error(), ", ".join(sorted(self.extras)) + ) + + @property + def is_installed(self) -> bool: + return self.base.is_installed + + @property + def is_editable(self) -> bool: + return self.base.is_editable + + @property + def source_link(self) -> Optional[Link]: + return self.base.source_link + + def _warn_invalid_extras( + self, + requested: FrozenSet[str], + valid: FrozenSet[str], + ) -> None: + """Emit warnings for invalid extras being requested. + + This emits a warning for each requested extra that is not in the + candidate's ``Provides-Extra`` list. + """ + invalid_extras_to_warn = frozenset( + extra + for extra in requested + if extra not in valid + # If an extra is requested in an unnormalized form, skip warning + # about the normalized form being missing. + and extra in self.extras + ) + if not invalid_extras_to_warn: + return + for extra in sorted(invalid_extras_to_warn): + logger.warning( + "%s %s does not provide the extra '%s'", + self.base.name, + self.version, + extra, + ) + + def _calculate_valid_requested_extras(self) -> FrozenSet[str]: + """Get a list of valid extras requested by this candidate. + + The user (or upstream dependant) may have specified extras that the + candidate doesn't support. Any unsupported extras are dropped, and each + cause a warning to be logged here. + """ + requested_extras = self.extras.union(self._unnormalized_extras) + valid_extras = frozenset( + extra + for extra in requested_extras + if self.base.dist.is_extra_provided(extra) + ) + self._warn_invalid_extras(requested_extras, valid_extras) + return valid_extras + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + factory = self.base._factory + + # Add a dependency on the exact base + # (See note 2b in the class docstring) + yield factory.make_requirement_from_candidate(self.base) + if not with_requires: + return + + valid_extras = self._calculate_valid_requested_extras() + for r in self.base.dist.iter_dependencies(valid_extras): + yield from factory.make_requirements_from_spec( + str(r), + self._comes_from, + valid_extras, + ) + + def get_install_requirement(self) -> Optional[InstallRequirement]: + # We don't return anything here, because we always + # depend on the base candidate, and we'll get the + # install requirement from that. + return None + + +class RequiresPythonCandidate(Candidate): + is_installed = False + source_link = None + + def __init__(self, py_version_info: Optional[Tuple[int, ...]]) -> None: + if py_version_info is not None: + version_info = normalize_version_info(py_version_info) + else: + version_info = sys.version_info[:3] + self._version = Version(".".join(str(c) for c in version_info)) + + # We don't need to implement __eq__() and __ne__() since there is always + # only one RequiresPythonCandidate in a resolution, i.e. the host Python. + # The built-in object.__eq__() and object.__ne__() do exactly what we want. + + def __str__(self) -> str: + return f"Python {self._version}" + + @property + def project_name(self) -> NormalizedName: + return REQUIRES_PYTHON_IDENTIFIER + + @property + def name(self) -> str: + return REQUIRES_PYTHON_IDENTIFIER + + @property + def version(self) -> CandidateVersion: + return self._version + + def format_for_error(self) -> str: + return f"Python {self.version}" + + def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]: + return () + + def get_install_requirement(self) -> Optional[InstallRequirement]: + return None diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py new file mode 100644 index 0000000..4adeb43 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -0,0 +1,812 @@ +import contextlib +import functools +import logging +from typing import ( + TYPE_CHECKING, + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Mapping, + NamedTuple, + Optional, + Sequence, + Set, + Tuple, + TypeVar, + cast, +) + +from pip._vendor.packaging.requirements import InvalidRequirement +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name +from pip._vendor.resolvelib import ResolutionImpossible + +from pip._internal.cache import CacheEntry, WheelCache +from pip._internal.exceptions import ( + DistributionNotFound, + InstallationError, + MetadataInconsistent, + UnsupportedPythonVersion, + UnsupportedWheel, +) +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import BaseDistribution, get_default_environment +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import ( + install_req_drop_extras, + install_req_from_link_and_ireq, +) +from pip._internal.req.req_install import ( + InstallRequirement, + check_invalid_constraint_type, +) +from pip._internal.resolution.base import InstallRequirementProvider +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.packaging import get_requirement +from pip._internal.utils.virtualenv import running_under_virtualenv + +from .base import Candidate, CandidateVersion, Constraint, Requirement +from .candidates import ( + AlreadyInstalledCandidate, + BaseCandidate, + EditableCandidate, + ExtrasCandidate, + LinkCandidate, + RequiresPythonCandidate, + as_base_candidate, +) +from .found_candidates import FoundCandidates, IndexCandidateInfo +from .requirements import ( + ExplicitRequirement, + RequiresPythonRequirement, + SpecifierRequirement, + SpecifierWithoutExtrasRequirement, + UnsatisfiableRequirement, +) + +if TYPE_CHECKING: + from typing import Protocol + + class ConflictCause(Protocol): + requirement: RequiresPythonRequirement + parent: Candidate + + +logger = logging.getLogger(__name__) + +C = TypeVar("C") +Cache = Dict[Link, C] + + +class CollectedRootRequirements(NamedTuple): + requirements: List[Requirement] + constraints: Dict[str, Constraint] + user_requested: Dict[str, int] + + +class Factory: + def __init__( + self, + finder: PackageFinder, + preparer: RequirementPreparer, + make_install_req: InstallRequirementProvider, + wheel_cache: Optional[WheelCache], + use_user_site: bool, + force_reinstall: bool, + ignore_installed: bool, + ignore_requires_python: bool, + py_version_info: Optional[Tuple[int, ...]] = None, + ) -> None: + self._finder = finder + self.preparer = preparer + self._wheel_cache = wheel_cache + self._python_candidate = RequiresPythonCandidate(py_version_info) + self._make_install_req_from_spec = make_install_req + self._use_user_site = use_user_site + self._force_reinstall = force_reinstall + self._ignore_requires_python = ignore_requires_python + + self._build_failures: Cache[InstallationError] = {} + self._link_candidate_cache: Cache[LinkCandidate] = {} + self._editable_candidate_cache: Cache[EditableCandidate] = {} + self._installed_candidate_cache: Dict[str, AlreadyInstalledCandidate] = {} + self._extras_candidate_cache: Dict[ + Tuple[int, FrozenSet[NormalizedName]], ExtrasCandidate + ] = {} + + if not ignore_installed: + env = get_default_environment() + self._installed_dists = { + dist.canonical_name: dist + for dist in env.iter_installed_distributions(local_only=False) + } + else: + self._installed_dists = {} + + @property + def force_reinstall(self) -> bool: + return self._force_reinstall + + def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None: + if not link.is_wheel: + return + wheel = Wheel(link.filename) + if wheel.supported(self._finder.target_python.get_unsorted_tags()): + return + msg = f"{link.filename} is not a supported wheel on this platform." + raise UnsupportedWheel(msg) + + def _make_extras_candidate( + self, + base: BaseCandidate, + extras: FrozenSet[str], + *, + comes_from: Optional[InstallRequirement] = None, + ) -> ExtrasCandidate: + cache_key = (id(base), frozenset(canonicalize_name(e) for e in extras)) + try: + candidate = self._extras_candidate_cache[cache_key] + except KeyError: + candidate = ExtrasCandidate(base, extras, comes_from=comes_from) + self._extras_candidate_cache[cache_key] = candidate + return candidate + + def _make_candidate_from_dist( + self, + dist: BaseDistribution, + extras: FrozenSet[str], + template: InstallRequirement, + ) -> Candidate: + try: + base = self._installed_candidate_cache[dist.canonical_name] + except KeyError: + base = AlreadyInstalledCandidate(dist, template, factory=self) + self._installed_candidate_cache[dist.canonical_name] = base + if not extras: + return base + return self._make_extras_candidate(base, extras, comes_from=template) + + def _make_candidate_from_link( + self, + link: Link, + extras: FrozenSet[str], + template: InstallRequirement, + name: Optional[NormalizedName], + version: Optional[CandidateVersion], + ) -> Optional[Candidate]: + base: Optional[BaseCandidate] = self._make_base_candidate_from_link( + link, template, name, version + ) + if not extras or base is None: + return base + return self._make_extras_candidate(base, extras, comes_from=template) + + def _make_base_candidate_from_link( + self, + link: Link, + template: InstallRequirement, + name: Optional[NormalizedName], + version: Optional[CandidateVersion], + ) -> Optional[BaseCandidate]: + # TODO: Check already installed candidate, and use it if the link and + # editable flag match. + + if link in self._build_failures: + # We already tried this candidate before, and it does not build. + # Don't bother trying again. + return None + + if template.editable: + if link not in self._editable_candidate_cache: + try: + self._editable_candidate_cache[link] = EditableCandidate( + link, + template, + factory=self, + name=name, + version=version, + ) + except MetadataInconsistent as e: + logger.info( + "Discarding [blue underline]%s[/]: [yellow]%s[reset]", + link, + e, + extra={"markup": True}, + ) + self._build_failures[link] = e + return None + + return self._editable_candidate_cache[link] + else: + if link not in self._link_candidate_cache: + try: + self._link_candidate_cache[link] = LinkCandidate( + link, + template, + factory=self, + name=name, + version=version, + ) + except MetadataInconsistent as e: + logger.info( + "Discarding [blue underline]%s[/]: [yellow]%s[reset]", + link, + e, + extra={"markup": True}, + ) + self._build_failures[link] = e + return None + return self._link_candidate_cache[link] + + def _iter_found_candidates( + self, + ireqs: Sequence[InstallRequirement], + specifier: SpecifierSet, + hashes: Hashes, + prefers_installed: bool, + incompatible_ids: Set[int], + ) -> Iterable[Candidate]: + if not ireqs: + return () + + # The InstallRequirement implementation requires us to give it a + # "template". Here we just choose the first requirement to represent + # all of them. + # Hopefully the Project model can correct this mismatch in the future. + template = ireqs[0] + assert template.req, "Candidates found on index must be PEP 508" + name = canonicalize_name(template.req.name) + + extras: FrozenSet[str] = frozenset() + for ireq in ireqs: + assert ireq.req, "Candidates found on index must be PEP 508" + specifier &= ireq.req.specifier + hashes &= ireq.hashes(trust_internet=False) + extras |= frozenset(ireq.extras) + + def _get_installed_candidate() -> Optional[Candidate]: + """Get the candidate for the currently-installed version.""" + # If --force-reinstall is set, we want the version from the index + # instead, so we "pretend" there is nothing installed. + if self._force_reinstall: + return None + try: + installed_dist = self._installed_dists[name] + except KeyError: + return None + # Don't use the installed distribution if its version does not fit + # the current dependency graph. + if not specifier.contains(installed_dist.version, prereleases=True): + return None + candidate = self._make_candidate_from_dist( + dist=installed_dist, + extras=extras, + template=template, + ) + # The candidate is a known incompatibility. Don't use it. + if id(candidate) in incompatible_ids: + return None + return candidate + + def iter_index_candidate_infos() -> Iterator[IndexCandidateInfo]: + result = self._finder.find_best_candidate( + project_name=name, + specifier=specifier, + hashes=hashes, + ) + icans = list(result.iter_applicable()) + + # PEP 592: Yanked releases are ignored unless the specifier + # explicitly pins a version (via '==' or '===') that can be + # solely satisfied by a yanked release. + all_yanked = all(ican.link.is_yanked for ican in icans) + + def is_pinned(specifier: SpecifierSet) -> bool: + for sp in specifier: + if sp.operator == "===": + return True + if sp.operator != "==": + continue + if sp.version.endswith(".*"): + continue + return True + return False + + pinned = is_pinned(specifier) + + # PackageFinder returns earlier versions first, so we reverse. + for ican in reversed(icans): + if not (all_yanked and pinned) and ican.link.is_yanked: + continue + func = functools.partial( + self._make_candidate_from_link, + link=ican.link, + extras=extras, + template=template, + name=name, + version=ican.version, + ) + yield ican.version, func + + return FoundCandidates( + iter_index_candidate_infos, + _get_installed_candidate(), + prefers_installed, + incompatible_ids, + ) + + def _iter_explicit_candidates_from_base( + self, + base_requirements: Iterable[Requirement], + extras: FrozenSet[str], + ) -> Iterator[Candidate]: + """Produce explicit candidates from the base given an extra-ed package. + + :param base_requirements: Requirements known to the resolver. The + requirements are guaranteed to not have extras. + :param extras: The extras to inject into the explicit requirements' + candidates. + """ + for req in base_requirements: + lookup_cand, _ = req.get_candidate_lookup() + if lookup_cand is None: # Not explicit. + continue + # We've stripped extras from the identifier, and should always + # get a BaseCandidate here, unless there's a bug elsewhere. + base_cand = as_base_candidate(lookup_cand) + assert base_cand is not None, "no extras here" + yield self._make_extras_candidate(base_cand, extras) + + def _iter_candidates_from_constraints( + self, + identifier: str, + constraint: Constraint, + template: InstallRequirement, + ) -> Iterator[Candidate]: + """Produce explicit candidates from constraints. + + This creates "fake" InstallRequirement objects that are basically clones + of what "should" be the template, but with original_link set to link. + """ + for link in constraint.links: + self._fail_if_link_is_unsupported_wheel(link) + candidate = self._make_base_candidate_from_link( + link, + template=install_req_from_link_and_ireq(link, template), + name=canonicalize_name(identifier), + version=None, + ) + if candidate: + yield candidate + + def find_candidates( + self, + identifier: str, + requirements: Mapping[str, Iterable[Requirement]], + incompatibilities: Mapping[str, Iterator[Candidate]], + constraint: Constraint, + prefers_installed: bool, + ) -> Iterable[Candidate]: + # Collect basic lookup information from the requirements. + explicit_candidates: Set[Candidate] = set() + ireqs: List[InstallRequirement] = [] + for req in requirements[identifier]: + cand, ireq = req.get_candidate_lookup() + if cand is not None: + explicit_candidates.add(cand) + if ireq is not None: + ireqs.append(ireq) + + # If the current identifier contains extras, add requires and explicit + # candidates from entries from extra-less identifier. + with contextlib.suppress(InvalidRequirement): + parsed_requirement = get_requirement(identifier) + if parsed_requirement.name != identifier: + explicit_candidates.update( + self._iter_explicit_candidates_from_base( + requirements.get(parsed_requirement.name, ()), + frozenset(parsed_requirement.extras), + ), + ) + for req in requirements.get(parsed_requirement.name, []): + _, ireq = req.get_candidate_lookup() + if ireq is not None: + ireqs.append(ireq) + + # Add explicit candidates from constraints. We only do this if there are + # known ireqs, which represent requirements not already explicit. If + # there are no ireqs, we're constraining already-explicit requirements, + # which is handled later when we return the explicit candidates. + if ireqs: + try: + explicit_candidates.update( + self._iter_candidates_from_constraints( + identifier, + constraint, + template=ireqs[0], + ), + ) + except UnsupportedWheel: + # If we're constrained to install a wheel incompatible with the + # target architecture, no candidates will ever be valid. + return () + + # Since we cache all the candidates, incompatibility identification + # can be made quicker by comparing only the id() values. + incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())} + + # If none of the requirements want an explicit candidate, we can ask + # the finder for candidates. + if not explicit_candidates: + return self._iter_found_candidates( + ireqs, + constraint.specifier, + constraint.hashes, + prefers_installed, + incompat_ids, + ) + + return ( + c + for c in explicit_candidates + if id(c) not in incompat_ids + and constraint.is_satisfied_by(c) + and all(req.is_satisfied_by(c) for req in requirements[identifier]) + ) + + def _make_requirements_from_install_req( + self, ireq: InstallRequirement, requested_extras: Iterable[str] + ) -> Iterator[Requirement]: + """ + Returns requirement objects associated with the given InstallRequirement. In + most cases this will be a single object but the following special cases exist: + - the InstallRequirement has markers that do not apply -> result is empty + - the InstallRequirement has both a constraint (or link) and extras + -> result is split in two requirement objects: one with the constraint + (or link) and one with the extra. This allows centralized constraint + handling for the base, resulting in fewer candidate rejections. + """ + if not ireq.match_markers(requested_extras): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + ireq.name, + ireq.markers, + ) + elif not ireq.link: + if ireq.extras and ireq.req is not None and ireq.req.specifier: + yield SpecifierWithoutExtrasRequirement(ireq) + yield SpecifierRequirement(ireq) + else: + self._fail_if_link_is_unsupported_wheel(ireq.link) + # Always make the link candidate for the base requirement to make it + # available to `find_candidates` for explicit candidate lookup for any + # set of extras. + # The extras are required separately via a second requirement. + cand = self._make_base_candidate_from_link( + ireq.link, + template=install_req_drop_extras(ireq) if ireq.extras else ireq, + name=canonicalize_name(ireq.name) if ireq.name else None, + version=None, + ) + if cand is None: + # There's no way we can satisfy a URL requirement if the underlying + # candidate fails to build. An unnamed URL must be user-supplied, so + # we fail eagerly. If the URL is named, an unsatisfiable requirement + # can make the resolver do the right thing, either backtrack (and + # maybe find some other requirement that's buildable) or raise a + # ResolutionImpossible eventually. + if not ireq.name: + raise self._build_failures[ireq.link] + yield UnsatisfiableRequirement(canonicalize_name(ireq.name)) + else: + # require the base from the link + yield self.make_requirement_from_candidate(cand) + if ireq.extras: + # require the extras on top of the base candidate + yield self.make_requirement_from_candidate( + self._make_extras_candidate(cand, frozenset(ireq.extras)) + ) + + def collect_root_requirements( + self, root_ireqs: List[InstallRequirement] + ) -> CollectedRootRequirements: + collected = CollectedRootRequirements([], {}, {}) + for i, ireq in enumerate(root_ireqs): + if ireq.constraint: + # Ensure we only accept valid constraints + problem = check_invalid_constraint_type(ireq) + if problem: + raise InstallationError(problem) + if not ireq.match_markers(): + continue + assert ireq.name, "Constraint must be named" + name = canonicalize_name(ireq.name) + if name in collected.constraints: + collected.constraints[name] &= ireq + else: + collected.constraints[name] = Constraint.from_ireq(ireq) + else: + reqs = list( + self._make_requirements_from_install_req( + ireq, + requested_extras=(), + ) + ) + if not reqs: + continue + template = reqs[0] + if ireq.user_supplied and template.name not in collected.user_requested: + collected.user_requested[template.name] = i + collected.requirements.extend(reqs) + # Put requirements with extras at the end of the root requires. This does not + # affect resolvelib's picking preference but it does affect its initial criteria + # population: by putting extras at the end we enable the candidate finder to + # present resolvelib with a smaller set of candidates to resolvelib, already + # taking into account any non-transient constraints on the associated base. This + # means resolvelib will have fewer candidates to visit and reject. + # Python's list sort is stable, meaning relative order is kept for objects with + # the same key. + collected.requirements.sort(key=lambda r: r.name != r.project_name) + return collected + + def make_requirement_from_candidate( + self, candidate: Candidate + ) -> ExplicitRequirement: + return ExplicitRequirement(candidate) + + def make_requirements_from_spec( + self, + specifier: str, + comes_from: Optional[InstallRequirement], + requested_extras: Iterable[str] = (), + ) -> Iterator[Requirement]: + """ + Returns requirement objects associated with the given specifier. In most cases + this will be a single object but the following special cases exist: + - the specifier has markers that do not apply -> result is empty + - the specifier has both a constraint and extras -> result is split + in two requirement objects: one with the constraint and one with the + extra. This allows centralized constraint handling for the base, + resulting in fewer candidate rejections. + """ + ireq = self._make_install_req_from_spec(specifier, comes_from) + return self._make_requirements_from_install_req(ireq, requested_extras) + + def make_requires_python_requirement( + self, + specifier: SpecifierSet, + ) -> Optional[Requirement]: + if self._ignore_requires_python: + return None + # Don't bother creating a dependency for an empty Requires-Python. + if not str(specifier): + return None + return RequiresPythonRequirement(specifier, self._python_candidate) + + def get_wheel_cache_entry( + self, link: Link, name: Optional[str] + ) -> Optional[CacheEntry]: + """Look up the link in the wheel cache. + + If ``preparer.require_hashes`` is True, don't use the wheel cache, + because cached wheels, always built locally, have different hashes + than the files downloaded from the index server and thus throw false + hash mismatches. Furthermore, cached wheels at present have + nondeterministic contents due to file modification times. + """ + if self._wheel_cache is None: + return None + return self._wheel_cache.get_cache_entry( + link=link, + package_name=name, + supported_tags=get_supported(), + ) + + def get_dist_to_uninstall(self, candidate: Candidate) -> Optional[BaseDistribution]: + # TODO: Are there more cases this needs to return True? Editable? + dist = self._installed_dists.get(candidate.project_name) + if dist is None: # Not installed, no uninstallation required. + return None + + # We're installing into global site. The current installation must + # be uninstalled, no matter it's in global or user site, because the + # user site installation has precedence over global. + if not self._use_user_site: + return dist + + # We're installing into user site. Remove the user site installation. + if dist.in_usersite: + return dist + + # We're installing into user site, but the installed incompatible + # package is in global site. We can't uninstall that, and would let + # the new user installation to "shadow" it. But shadowing won't work + # in virtual environments, so we error out. + if running_under_virtualenv() and dist.in_site_packages: + message = ( + f"Will not install to the user site because it will lack " + f"sys.path precedence to {dist.raw_name} in {dist.location}" + ) + raise InstallationError(message) + return None + + def _report_requires_python_error( + self, causes: Sequence["ConflictCause"] + ) -> UnsupportedPythonVersion: + assert causes, "Requires-Python error reported with no cause" + + version = self._python_candidate.version + + if len(causes) == 1: + specifier = str(causes[0].requirement.specifier) + message = ( + f"Package {causes[0].parent.name!r} requires a different " + f"Python: {version} not in {specifier!r}" + ) + return UnsupportedPythonVersion(message) + + message = f"Packages require a different Python. {version} not in:" + for cause in causes: + package = cause.parent.format_for_error() + specifier = str(cause.requirement.specifier) + message += f"\n{specifier!r} (required by {package})" + return UnsupportedPythonVersion(message) + + def _report_single_requirement_conflict( + self, req: Requirement, parent: Optional[Candidate] + ) -> DistributionNotFound: + if parent is None: + req_disp = str(req) + else: + req_disp = f"{req} (from {parent.name})" + + cands = self._finder.find_all_candidates(req.project_name) + skipped_by_requires_python = self._finder.requires_python_skipped_reasons() + + versions_set: Set[CandidateVersion] = set() + yanked_versions_set: Set[CandidateVersion] = set() + for c in cands: + is_yanked = c.link.is_yanked if c.link else False + if is_yanked: + yanked_versions_set.add(c.version) + else: + versions_set.add(c.version) + + versions = [str(v) for v in sorted(versions_set)] + yanked_versions = [str(v) for v in sorted(yanked_versions_set)] + + if yanked_versions: + # Saying "version X is yanked" isn't entirely accurate. + # https://github.com/pypa/pip/issues/11745#issuecomment-1402805842 + logger.critical( + "Ignored the following yanked versions: %s", + ", ".join(yanked_versions) or "none", + ) + if skipped_by_requires_python: + logger.critical( + "Ignored the following versions that require a different python " + "version: %s", + "; ".join(skipped_by_requires_python) or "none", + ) + logger.critical( + "Could not find a version that satisfies the requirement %s " + "(from versions: %s)", + req_disp, + ", ".join(versions) or "none", + ) + if str(req) == "requirements.txt": + logger.info( + "HINT: You are attempting to install a package literally " + 'named "requirements.txt" (which cannot exist). Consider ' + "using the '-r' flag to install the packages listed in " + "requirements.txt" + ) + + return DistributionNotFound(f"No matching distribution found for {req}") + + def get_installation_error( + self, + e: "ResolutionImpossible[Requirement, Candidate]", + constraints: Dict[str, Constraint], + ) -> InstallationError: + assert e.causes, "Installation error reported with no cause" + + # If one of the things we can't solve is "we need Python X.Y", + # that is what we report. + requires_python_causes = [ + cause + for cause in e.causes + if isinstance(cause.requirement, RequiresPythonRequirement) + and not cause.requirement.is_satisfied_by(self._python_candidate) + ] + if requires_python_causes: + # The comprehension above makes sure all Requirement instances are + # RequiresPythonRequirement, so let's cast for convenience. + return self._report_requires_python_error( + cast("Sequence[ConflictCause]", requires_python_causes), + ) + + # Otherwise, we have a set of causes which can't all be satisfied + # at once. + + # The simplest case is when we have *one* cause that can't be + # satisfied. We just report that case. + if len(e.causes) == 1: + req, parent = e.causes[0] + if req.name not in constraints: + return self._report_single_requirement_conflict(req, parent) + + # OK, we now have a list of requirements that can't all be + # satisfied at once. + + # A couple of formatting helpers + def text_join(parts: List[str]) -> str: + if len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def describe_trigger(parent: Candidate) -> str: + ireq = parent.get_install_requirement() + if not ireq or not ireq.comes_from: + return f"{parent.name}=={parent.version}" + if isinstance(ireq.comes_from, InstallRequirement): + return str(ireq.comes_from.name) + return str(ireq.comes_from) + + triggers = set() + for req, parent in e.causes: + if parent is None: + # This is a root requirement, so we can report it directly + trigger = req.format_for_error() + else: + trigger = describe_trigger(parent) + triggers.add(trigger) + + if triggers: + info = text_join(sorted(triggers)) + else: + info = "the requested packages" + + msg = ( + f"Cannot install {info} because these package versions " + "have conflicting dependencies." + ) + logger.critical(msg) + msg = "\nThe conflict is caused by:" + + relevant_constraints = set() + for req, parent in e.causes: + if req.name in constraints: + relevant_constraints.add(req.name) + msg = msg + "\n " + if parent: + msg = msg + f"{parent.name} {parent.version} depends on " + else: + msg = msg + "The user requested " + msg = msg + req.format_for_error() + for key in relevant_constraints: + spec = constraints[key].specifier + msg += f"\n The user requested (constraint) {key}{spec}" + + msg = ( + msg + + "\n\n" + + "To fix this you could try to:\n" + + "1. loosen the range of package versions you've specified\n" + + "2. remove package versions to allow pip attempt to solve " + + "the dependency conflict\n" + ) + + logger.info(msg) + + return DistributionNotFound( + "ResolutionImpossible: for help visit " + "https://pip.pypa.io/en/latest/topics/dependency-resolution/" + "#dealing-with-dependency-conflicts" + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py new file mode 100644 index 0000000..8663097 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py @@ -0,0 +1,155 @@ +"""Utilities to lazily create and visit candidates found. + +Creating and visiting a candidate is a *very* costly operation. It involves +fetching, extracting, potentially building modules from source, and verifying +distribution metadata. It is therefore crucial for performance to keep +everything here lazy all the way down, so we only touch candidates that we +absolutely need, and not "download the world" when we only need one version of +something. +""" + +import functools +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Set, Tuple + +from pip._vendor.packaging.version import _BaseVersion + +from .base import Candidate + +IndexCandidateInfo = Tuple[_BaseVersion, Callable[[], Optional[Candidate]]] + +if TYPE_CHECKING: + SequenceCandidate = Sequence[Candidate] +else: + # For compatibility: Python before 3.9 does not support using [] on the + # Sequence class. + # + # >>> from collections.abc import Sequence + # >>> Sequence[str] + # Traceback (most recent call last): + # File "", line 1, in + # TypeError: 'ABCMeta' object is not subscriptable + # + # TODO: Remove this block after dropping Python 3.8 support. + SequenceCandidate = Sequence + + +def _iter_built(infos: Iterator[IndexCandidateInfo]) -> Iterator[Candidate]: + """Iterator for ``FoundCandidates``. + + This iterator is used when the package is not already installed. Candidates + from index come later in their normal ordering. + """ + versions_found: Set[_BaseVersion] = set() + for version, func in infos: + if version in versions_found: + continue + candidate = func() + if candidate is None: + continue + yield candidate + versions_found.add(version) + + +def _iter_built_with_prepended( + installed: Candidate, infos: Iterator[IndexCandidateInfo] +) -> Iterator[Candidate]: + """Iterator for ``FoundCandidates``. + + This iterator is used when the resolver prefers the already-installed + candidate and NOT to upgrade. The installed candidate is therefore + always yielded first, and candidates from index come later in their + normal ordering, except skipped when the version is already installed. + """ + yield installed + versions_found: Set[_BaseVersion] = {installed.version} + for version, func in infos: + if version in versions_found: + continue + candidate = func() + if candidate is None: + continue + yield candidate + versions_found.add(version) + + +def _iter_built_with_inserted( + installed: Candidate, infos: Iterator[IndexCandidateInfo] +) -> Iterator[Candidate]: + """Iterator for ``FoundCandidates``. + + This iterator is used when the resolver prefers to upgrade an + already-installed package. Candidates from index are returned in their + normal ordering, except replaced when the version is already installed. + + The implementation iterates through and yields other candidates, inserting + the installed candidate exactly once before we start yielding older or + equivalent candidates, or after all other candidates if they are all newer. + """ + versions_found: Set[_BaseVersion] = set() + for version, func in infos: + if version in versions_found: + continue + # If the installed candidate is better, yield it first. + if installed.version >= version: + yield installed + versions_found.add(installed.version) + candidate = func() + if candidate is None: + continue + yield candidate + versions_found.add(version) + + # If the installed candidate is older than all other candidates. + if installed.version not in versions_found: + yield installed + + +class FoundCandidates(SequenceCandidate): + """A lazy sequence to provide candidates to the resolver. + + The intended usage is to return this from `find_matches()` so the resolver + can iterate through the sequence multiple times, but only access the index + page when remote packages are actually needed. This improve performances + when suitable candidates are already installed on disk. + """ + + def __init__( + self, + get_infos: Callable[[], Iterator[IndexCandidateInfo]], + installed: Optional[Candidate], + prefers_installed: bool, + incompatible_ids: Set[int], + ): + self._get_infos = get_infos + self._installed = installed + self._prefers_installed = prefers_installed + self._incompatible_ids = incompatible_ids + + def __getitem__(self, index: Any) -> Any: + # Implemented to satisfy the ABC check. This is not needed by the + # resolver, and should not be used by the provider either (for + # performance reasons). + raise NotImplementedError("don't do this") + + def __iter__(self) -> Iterator[Candidate]: + infos = self._get_infos() + if not self._installed: + iterator = _iter_built(infos) + elif self._prefers_installed: + iterator = _iter_built_with_prepended(self._installed, infos) + else: + iterator = _iter_built_with_inserted(self._installed, infos) + return (c for c in iterator if id(c) not in self._incompatible_ids) + + def __len__(self) -> int: + # Implemented to satisfy the ABC check. This is not needed by the + # resolver, and should not be used by the provider either (for + # performance reasons). + raise NotImplementedError("don't do this") + + @functools.lru_cache(maxsize=1) + def __bool__(self) -> bool: + if self._prefers_installed and self._installed: + return True + return any(self) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py new file mode 100644 index 0000000..315fb9c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -0,0 +1,255 @@ +import collections +import math +from typing import ( + TYPE_CHECKING, + Dict, + Iterable, + Iterator, + Mapping, + Sequence, + TypeVar, + Union, +) + +from pip._vendor.resolvelib.providers import AbstractProvider + +from .base import Candidate, Constraint, Requirement +from .candidates import REQUIRES_PYTHON_IDENTIFIER +from .factory import Factory + +if TYPE_CHECKING: + from pip._vendor.resolvelib.providers import Preference + from pip._vendor.resolvelib.resolvers import RequirementInformation + + PreferenceInformation = RequirementInformation[Requirement, Candidate] + + _ProviderBase = AbstractProvider[Requirement, Candidate, str] +else: + _ProviderBase = AbstractProvider + +# Notes on the relationship between the provider, the factory, and the +# candidate and requirement classes. +# +# The provider is a direct implementation of the resolvelib class. Its role +# is to deliver the API that resolvelib expects. +# +# Rather than work with completely abstract "requirement" and "candidate" +# concepts as resolvelib does, pip has concrete classes implementing these two +# ideas. The API of Requirement and Candidate objects are defined in the base +# classes, but essentially map fairly directly to the equivalent provider +# methods. In particular, `find_matches` and `is_satisfied_by` are +# requirement methods, and `get_dependencies` is a candidate method. +# +# The factory is the interface to pip's internal mechanisms. It is stateless, +# and is created by the resolver and held as a property of the provider. It is +# responsible for creating Requirement and Candidate objects, and provides +# services to those objects (access to pip's finder and preparer). + + +D = TypeVar("D") +V = TypeVar("V") + + +def _get_with_identifier( + mapping: Mapping[str, V], + identifier: str, + default: D, +) -> Union[D, V]: + """Get item from a package name lookup mapping with a resolver identifier. + + This extra logic is needed when the target mapping is keyed by package + name, which cannot be directly looked up with an identifier (which may + contain requested extras). Additional logic is added to also look up a value + by "cleaning up" the extras from the identifier. + """ + if identifier in mapping: + return mapping[identifier] + # HACK: Theoretically we should check whether this identifier is a valid + # "NAME[EXTRAS]" format, and parse out the name part with packaging or + # some regular expression. But since pip's resolver only spits out three + # kinds of identifiers: normalized PEP 503 names, normalized names plus + # extras, and Requires-Python, we can cheat a bit here. + name, open_bracket, _ = identifier.partition("[") + if open_bracket and name in mapping: + return mapping[name] + return default + + +class PipProvider(_ProviderBase): + """Pip's provider implementation for resolvelib. + + :params constraints: A mapping of constraints specified by the user. Keys + are canonicalized project names. + :params ignore_dependencies: Whether the user specified ``--no-deps``. + :params upgrade_strategy: The user-specified upgrade strategy. + :params user_requested: A set of canonicalized package names that the user + supplied for pip to install/upgrade. + """ + + def __init__( + self, + factory: Factory, + constraints: Dict[str, Constraint], + ignore_dependencies: bool, + upgrade_strategy: str, + user_requested: Dict[str, int], + ) -> None: + self._factory = factory + self._constraints = constraints + self._ignore_dependencies = ignore_dependencies + self._upgrade_strategy = upgrade_strategy + self._user_requested = user_requested + self._known_depths: Dict[str, float] = collections.defaultdict(lambda: math.inf) + + def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str: + return requirement_or_candidate.name + + def get_preference( + self, + identifier: str, + resolutions: Mapping[str, Candidate], + candidates: Mapping[str, Iterator[Candidate]], + information: Mapping[str, Iterable["PreferenceInformation"]], + backtrack_causes: Sequence["PreferenceInformation"], + ) -> "Preference": + """Produce a sort key for given requirement based on preference. + + The lower the return value is, the more preferred this group of + arguments is. + + Currently pip considers the following in order: + + * Prefer if any of the known requirements is "direct", e.g. points to an + explicit URL. + * If equal, prefer if any requirement is "pinned", i.e. contains + operator ``===`` or ``==``. + * If equal, calculate an approximate "depth" and resolve requirements + closer to the user-specified requirements first. If the depth cannot + by determined (eg: due to no matching parents), it is considered + infinite. + * Order user-specified requirements by the order they are specified. + * If equal, prefers "non-free" requirements, i.e. contains at least one + operator, such as ``>=`` or ``<``. + * If equal, order alphabetically for consistency (helps debuggability). + """ + try: + next(iter(information[identifier])) + except StopIteration: + # There is no information for this identifier, so there's no known + # candidates. + has_information = False + else: + has_information = True + + if has_information: + lookups = (r.get_candidate_lookup() for r, _ in information[identifier]) + candidate, ireqs = zip(*lookups) + else: + candidate, ireqs = None, () + + operators = [ + specifier.operator + for specifier_set in (ireq.specifier for ireq in ireqs if ireq) + for specifier in specifier_set + ] + + direct = candidate is not None + pinned = any(op[:2] == "==" for op in operators) + unfree = bool(operators) + + try: + requested_order: Union[int, float] = self._user_requested[identifier] + except KeyError: + requested_order = math.inf + if has_information: + parent_depths = ( + self._known_depths[parent.name] if parent is not None else 0.0 + for _, parent in information[identifier] + ) + inferred_depth = min(d for d in parent_depths) + 1.0 + else: + inferred_depth = math.inf + else: + inferred_depth = 1.0 + self._known_depths[identifier] = inferred_depth + + requested_order = self._user_requested.get(identifier, math.inf) + + # Requires-Python has only one candidate and the check is basically + # free, so we always do it first to avoid needless work if it fails. + requires_python = identifier == REQUIRES_PYTHON_IDENTIFIER + + # Prefer the causes of backtracking on the assumption that the problem + # resolving the dependency tree is related to the failures that caused + # the backtracking + backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes) + + return ( + not requires_python, + not direct, + not pinned, + not backtrack_cause, + inferred_depth, + requested_order, + not unfree, + identifier, + ) + + def find_matches( + self, + identifier: str, + requirements: Mapping[str, Iterator[Requirement]], + incompatibilities: Mapping[str, Iterator[Candidate]], + ) -> Iterable[Candidate]: + def _eligible_for_upgrade(identifier: str) -> bool: + """Are upgrades allowed for this project? + + This checks the upgrade strategy, and whether the project was one + that the user specified in the command line, in order to decide + whether we should upgrade if there's a newer version available. + + (Note that we don't need access to the `--upgrade` flag, because + an upgrade strategy of "to-satisfy-only" means that `--upgrade` + was not specified). + """ + if self._upgrade_strategy == "eager": + return True + elif self._upgrade_strategy == "only-if-needed": + user_order = _get_with_identifier( + self._user_requested, + identifier, + default=None, + ) + return user_order is not None + return False + + constraint = _get_with_identifier( + self._constraints, + identifier, + default=Constraint.empty(), + ) + return self._factory.find_candidates( + identifier=identifier, + requirements=requirements, + constraint=constraint, + prefers_installed=(not _eligible_for_upgrade(identifier)), + incompatibilities=incompatibilities, + ) + + def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> bool: + return requirement.is_satisfied_by(candidate) + + def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]: + with_requires = not self._ignore_dependencies + return [r for r in candidate.iter_dependencies(with_requires) if r is not None] + + @staticmethod + def is_backtrack_cause( + identifier: str, backtrack_causes: Sequence["PreferenceInformation"] + ) -> bool: + for backtrack_cause in backtrack_causes: + if identifier == backtrack_cause.requirement.name: + return True + if backtrack_cause.parent and identifier == backtrack_cause.parent.name: + return True + return False diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py new file mode 100644 index 0000000..12adeff --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/reporter.py @@ -0,0 +1,80 @@ +from collections import defaultdict +from logging import getLogger +from typing import Any, DefaultDict + +from pip._vendor.resolvelib.reporters import BaseReporter + +from .base import Candidate, Requirement + +logger = getLogger(__name__) + + +class PipReporter(BaseReporter): + def __init__(self) -> None: + self.reject_count_by_package: DefaultDict[str, int] = defaultdict(int) + + self._messages_at_reject_count = { + 1: ( + "pip is looking at multiple versions of {package_name} to " + "determine which version is compatible with other " + "requirements. This could take a while." + ), + 8: ( + "pip is still looking at multiple versions of {package_name} to " + "determine which version is compatible with other " + "requirements. This could take a while." + ), + 13: ( + "This is taking longer than usual. You might need to provide " + "the dependency resolver with stricter constraints to reduce " + "runtime. See https://pip.pypa.io/warnings/backtracking for " + "guidance. If you want to abort this run, press Ctrl + C." + ), + } + + def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None: + self.reject_count_by_package[candidate.name] += 1 + + count = self.reject_count_by_package[candidate.name] + if count not in self._messages_at_reject_count: + return + + message = self._messages_at_reject_count[count] + logger.info("INFO: %s", message.format(package_name=candidate.name)) + + msg = "Will try a different candidate, due to conflict:" + for req_info in criterion.information: + req, parent = req_info.requirement, req_info.parent + # Inspired by Factory.get_installation_error + msg += "\n " + if parent: + msg += f"{parent.name} {parent.version} depends on " + else: + msg += "The user requested " + msg += req.format_for_error() + logger.debug(msg) + + +class PipDebuggingReporter(BaseReporter): + """A reporter that does an info log for every event it sees.""" + + def starting(self) -> None: + logger.info("Reporter.starting()") + + def starting_round(self, index: int) -> None: + logger.info("Reporter.starting_round(%r)", index) + + def ending_round(self, index: int, state: Any) -> None: + logger.info("Reporter.ending_round(%r, state)", index) + + def ending(self, state: Any) -> None: + logger.info("Reporter.ending(%r)", state) + + def adding_requirement(self, requirement: Requirement, parent: Candidate) -> None: + logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent) + + def rejecting_candidate(self, criterion: Any, candidate: Candidate) -> None: + logger.info("Reporter.rejecting_candidate(%r, %r)", criterion, candidate) + + def pinning(self, candidate: Candidate) -> None: + logger.info("Reporter.pinning(%r)", candidate) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py new file mode 100644 index 0000000..4af4a9f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -0,0 +1,166 @@ +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import NormalizedName, canonicalize_name + +from pip._internal.req.constructors import install_req_drop_extras +from pip._internal.req.req_install import InstallRequirement + +from .base import Candidate, CandidateLookup, Requirement, format_name + + +class ExplicitRequirement(Requirement): + def __init__(self, candidate: Candidate) -> None: + self.candidate = candidate + + def __str__(self) -> str: + return str(self.candidate) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.candidate!r})" + + @property + def project_name(self) -> NormalizedName: + # No need to canonicalize - the candidate did this + return self.candidate.project_name + + @property + def name(self) -> str: + # No need to canonicalize - the candidate did this + return self.candidate.name + + def format_for_error(self) -> str: + return self.candidate.format_for_error() + + def get_candidate_lookup(self) -> CandidateLookup: + return self.candidate, None + + def is_satisfied_by(self, candidate: Candidate) -> bool: + return candidate == self.candidate + + +class SpecifierRequirement(Requirement): + def __init__(self, ireq: InstallRequirement) -> None: + assert ireq.link is None, "This is a link, not a specifier" + self._ireq = ireq + self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras) + + def __str__(self) -> str: + return str(self._ireq.req) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({str(self._ireq.req)!r})" + + @property + def project_name(self) -> NormalizedName: + assert self._ireq.req, "Specifier-backed ireq is always PEP 508" + return canonicalize_name(self._ireq.req.name) + + @property + def name(self) -> str: + return format_name(self.project_name, self._extras) + + def format_for_error(self) -> str: + # Convert comma-separated specifiers into "A, B, ..., F and G" + # This makes the specifier a bit more "human readable", without + # risking a change in meaning. (Hopefully! Not all edge cases have + # been checked) + parts = [s.strip() for s in str(self).split(",")] + if len(parts) == 0: + return "" + elif len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def get_candidate_lookup(self) -> CandidateLookup: + return None, self._ireq + + def is_satisfied_by(self, candidate: Candidate) -> bool: + assert candidate.name == self.name, ( + f"Internal issue: Candidate is not for this requirement " + f"{candidate.name} vs {self.name}" + ) + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + assert self._ireq.req, "Specifier-backed ireq is always PEP 508" + spec = self._ireq.req.specifier + return spec.contains(candidate.version, prereleases=True) + + +class SpecifierWithoutExtrasRequirement(SpecifierRequirement): + """ + Requirement backed by an install requirement on a base package. + Trims extras from its install requirement if there are any. + """ + + def __init__(self, ireq: InstallRequirement) -> None: + assert ireq.link is None, "This is a link, not a specifier" + self._ireq = install_req_drop_extras(ireq) + self._extras = frozenset(canonicalize_name(e) for e in self._ireq.extras) + + +class RequiresPythonRequirement(Requirement): + """A requirement representing Requires-Python metadata.""" + + def __init__(self, specifier: SpecifierSet, match: Candidate) -> None: + self.specifier = specifier + self._candidate = match + + def __str__(self) -> str: + return f"Python {self.specifier}" + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({str(self.specifier)!r})" + + @property + def project_name(self) -> NormalizedName: + return self._candidate.project_name + + @property + def name(self) -> str: + return self._candidate.name + + def format_for_error(self) -> str: + return str(self) + + def get_candidate_lookup(self) -> CandidateLookup: + if self.specifier.contains(self._candidate.version, prereleases=True): + return self._candidate, None + return None, None + + def is_satisfied_by(self, candidate: Candidate) -> bool: + assert candidate.name == self._candidate.name, "Not Python candidate" + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) + + +class UnsatisfiableRequirement(Requirement): + """A requirement that cannot be satisfied.""" + + def __init__(self, name: NormalizedName) -> None: + self._name = name + + def __str__(self) -> str: + return f"{self._name} (unavailable)" + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({str(self._name)!r})" + + @property + def project_name(self) -> NormalizedName: + return self._name + + @property + def name(self) -> str: + return self._name + + def format_for_error(self) -> str: + return str(self) + + def get_candidate_lookup(self) -> CandidateLookup: + return None, None + + def is_satisfied_by(self, candidate: Candidate) -> bool: + return False diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py new file mode 100644 index 0000000..c12beef --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -0,0 +1,317 @@ +import contextlib +import functools +import logging +import os +from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible +from pip._vendor.resolvelib import Resolver as RLResolver +from pip._vendor.resolvelib.structs import DirectedGraph + +from pip._internal.cache import WheelCache +from pip._internal.index.package_finder import PackageFinder +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import install_req_extend_extras +from pip._internal.req.req_install import InstallRequirement +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider +from pip._internal.resolution.resolvelib.provider import PipProvider +from pip._internal.resolution.resolvelib.reporter import ( + PipDebuggingReporter, + PipReporter, +) +from pip._internal.utils.packaging import get_requirement + +from .base import Candidate, Requirement +from .factory import Factory + +if TYPE_CHECKING: + from pip._vendor.resolvelib.resolvers import Result as RLResult + + Result = RLResult[Requirement, Candidate, str] + + +logger = logging.getLogger(__name__) + + +class Resolver(BaseResolver): + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer: RequirementPreparer, + finder: PackageFinder, + wheel_cache: Optional[WheelCache], + make_install_req: InstallRequirementProvider, + use_user_site: bool, + ignore_dependencies: bool, + ignore_installed: bool, + ignore_requires_python: bool, + force_reinstall: bool, + upgrade_strategy: str, + py_version_info: Optional[Tuple[int, ...]] = None, + ): + super().__init__() + assert upgrade_strategy in self._allowed_strategies + + self.factory = Factory( + finder=finder, + preparer=preparer, + make_install_req=make_install_req, + wheel_cache=wheel_cache, + use_user_site=use_user_site, + force_reinstall=force_reinstall, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + py_version_info=py_version_info, + ) + self.ignore_dependencies = ignore_dependencies + self.upgrade_strategy = upgrade_strategy + self._result: Optional[Result] = None + + def resolve( + self, root_reqs: List[InstallRequirement], check_supported_wheels: bool + ) -> RequirementSet: + collected = self.factory.collect_root_requirements(root_reqs) + provider = PipProvider( + factory=self.factory, + constraints=collected.constraints, + ignore_dependencies=self.ignore_dependencies, + upgrade_strategy=self.upgrade_strategy, + user_requested=collected.user_requested, + ) + if "PIP_RESOLVER_DEBUG" in os.environ: + reporter: BaseReporter = PipDebuggingReporter() + else: + reporter = PipReporter() + resolver: RLResolver[Requirement, Candidate, str] = RLResolver( + provider, + reporter, + ) + + try: + limit_how_complex_resolution_can_be = 200000 + result = self._result = resolver.resolve( + collected.requirements, max_rounds=limit_how_complex_resolution_can_be + ) + + except ResolutionImpossible as e: + error = self.factory.get_installation_error( + cast("ResolutionImpossible[Requirement, Candidate]", e), + collected.constraints, + ) + raise error from e + + req_set = RequirementSet(check_supported_wheels=check_supported_wheels) + # process candidates with extras last to ensure their base equivalent is + # already in the req_set if appropriate. + # Python's sort is stable so using a binary key function keeps relative order + # within both subsets. + for candidate in sorted( + result.mapping.values(), key=lambda c: c.name != c.project_name + ): + ireq = candidate.get_install_requirement() + if ireq is None: + if candidate.name != candidate.project_name: + # extend existing req's extras + with contextlib.suppress(KeyError): + req = req_set.get_requirement(candidate.project_name) + req_set.add_named_requirement( + install_req_extend_extras( + req, get_requirement(candidate.name).extras + ) + ) + continue + + # Check if there is already an installation under the same name, + # and set a flag for later stages to uninstall it, if needed. + installed_dist = self.factory.get_dist_to_uninstall(candidate) + if installed_dist is None: + # There is no existing installation -- nothing to uninstall. + ireq.should_reinstall = False + elif self.factory.force_reinstall: + # The --force-reinstall flag is set -- reinstall. + ireq.should_reinstall = True + elif installed_dist.version != candidate.version: + # The installation is different in version -- reinstall. + ireq.should_reinstall = True + elif candidate.is_editable or installed_dist.editable: + # The incoming distribution is editable, or different in + # editable-ness to installation -- reinstall. + ireq.should_reinstall = True + elif candidate.source_link and candidate.source_link.is_file: + # The incoming distribution is under file:// + if candidate.source_link.is_wheel: + # is a local wheel -- do nothing. + logger.info( + "%s is already installed with the same version as the " + "provided wheel. Use --force-reinstall to force an " + "installation of the wheel.", + ireq.name, + ) + continue + + # is a local sdist or path -- reinstall + ireq.should_reinstall = True + else: + continue + + link = candidate.source_link + if link and link.is_yanked: + # The reason can contain non-ASCII characters, Unicode + # is required for Python 2. + msg = ( + "The candidate selected for download or install is a " + "yanked version: {name!r} candidate (version {version} " + "at {link})\nReason for being yanked: {reason}" + ).format( + name=candidate.name, + version=candidate.version, + link=link, + reason=link.yanked_reason or "", + ) + logger.warning(msg) + + req_set.add_named_requirement(ireq) + + reqs = req_set.all_requirements + self.factory.preparer.prepare_linked_requirements_more(reqs) + for req in reqs: + req.prepared = True + req.needs_more_preparation = False + return req_set + + def get_installation_order( + self, req_set: RequirementSet + ) -> List[InstallRequirement]: + """Get order for installation of requirements in RequirementSet. + + The returned list contains a requirement before another that depends on + it. This helps ensure that the environment is kept consistent as they + get installed one-by-one. + + The current implementation creates a topological ordering of the + dependency graph, giving more weight to packages with less + or no dependencies, while breaking any cycles in the graph at + arbitrary points. We make no guarantees about where the cycle + would be broken, other than it *would* be broken. + """ + assert self._result is not None, "must call resolve() first" + + if not req_set.requirements: + # Nothing is left to install, so we do not need an order. + return [] + + graph = self._result.graph + weights = get_topological_weights(graph, set(req_set.requirements.keys())) + + sorted_items = sorted( + req_set.requirements.items(), + key=functools.partial(_req_set_item_sorter, weights=weights), + reverse=True, + ) + return [ireq for _, ireq in sorted_items] + + +def get_topological_weights( + graph: "DirectedGraph[Optional[str]]", requirement_keys: Set[str] +) -> Dict[Optional[str], int]: + """Assign weights to each node based on how "deep" they are. + + This implementation may change at any point in the future without prior + notice. + + We first simplify the dependency graph by pruning any leaves and giving them + the highest weight: a package without any dependencies should be installed + first. This is done again and again in the same way, giving ever less weight + to the newly found leaves. The loop stops when no leaves are left: all + remaining packages have at least one dependency left in the graph. + + Then we continue with the remaining graph, by taking the length for the + longest path to any node from root, ignoring any paths that contain a single + node twice (i.e. cycles). This is done through a depth-first search through + the graph, while keeping track of the path to the node. + + Cycles in the graph result would result in node being revisited while also + being on its own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. + + We are only interested in the weights of packages that are in the + requirement_keys. + """ + path: Set[Optional[str]] = set() + weights: Dict[Optional[str], int] = {} + + def visit(node: Optional[str]) -> None: + if node in path: + # We hit a cycle, so we'll break it here. + return + + # Time to visit the children! + path.add(node) + for child in graph.iter_children(node): + visit(child) + path.remove(node) + + if node not in requirement_keys: + return + + last_known_parent_count = weights.get(node, 0) + weights[node] = max(last_known_parent_count, len(path)) + + # Simplify the graph, pruning leaves that have no dependencies. + # This is needed for large graphs (say over 200 packages) because the + # `visit` function is exponentially slower then, taking minutes. + # See https://github.com/pypa/pip/issues/10557 + # We will loop until we explicitly break the loop. + while True: + leaves = set() + for key in graph: + if key is None: + continue + for _child in graph.iter_children(key): + # This means we have at least one child + break + else: + # No child. + leaves.add(key) + if not leaves: + # We are done simplifying. + break + # Calculate the weight for the leaves. + weight = len(graph) - 1 + for leaf in leaves: + if leaf not in requirement_keys: + continue + weights[leaf] = weight + # Remove the leaves from the graph, making it simpler. + for leaf in leaves: + graph.remove(leaf) + + # Visit the remaining graph. + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + + # Sanity check: all requirement keys should be in the weights, + # and no other keys should be in the weights. + difference = set(weights.keys()).difference(requirement_keys) + assert not difference, difference + + return weights + + +def _req_set_item_sorter( + item: Tuple[str, InstallRequirement], + weights: Dict[Optional[str], int], +) -> Tuple[int, str]: + """Key function used to sort install requirements for installation. + + Based on the "weight" mapping calculated in ``get_installation_order()``. + The canonical package name is returned as the second member as a tie- + breaker to ensure the result is predictable, which is useful in tests. + """ + name = canonicalize_name(item[0]) + return weights[name], name diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py b/.venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py new file mode 100644 index 0000000..0f64ae0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/self_outdated_check.py @@ -0,0 +1,248 @@ +import datetime +import functools +import hashlib +import json +import logging +import optparse +import os.path +import sys +from dataclasses import dataclass +from typing import Any, Callable, Dict, Optional + +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.rich.console import Group +from pip._vendor.rich.markup import escape +from pip._vendor.rich.text import Text + +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.metadata import get_default_environment +from pip._internal.metadata.base import DistributionVersion +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.network.session import PipSession +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.entrypoints import ( + get_best_invocation_for_this_pip, + get_best_invocation_for_this_python, +) +from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace +from pip._internal.utils.misc import ensure_dir + +_WEEK = datetime.timedelta(days=7) + +logger = logging.getLogger(__name__) + + +def _get_statefile_name(key: str) -> str: + key_bytes = key.encode() + name = hashlib.sha224(key_bytes).hexdigest() + return name + + +def _convert_date(isodate: str) -> datetime.datetime: + """Convert an ISO format string to a date. + + Handles the format 2020-01-22T14:24:01Z (trailing Z) + which is not supported by older versions of fromisoformat. + """ + return datetime.datetime.fromisoformat(isodate.replace("Z", "+00:00")) + + +class SelfCheckState: + def __init__(self, cache_dir: str) -> None: + self._state: Dict[str, Any] = {} + self._statefile_path = None + + # Try to load the existing state + if cache_dir: + self._statefile_path = os.path.join( + cache_dir, "selfcheck", _get_statefile_name(self.key) + ) + try: + with open(self._statefile_path, encoding="utf-8") as statefile: + self._state = json.load(statefile) + except (OSError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + @property + def key(self) -> str: + return sys.prefix + + def get(self, current_time: datetime.datetime) -> Optional[str]: + """Check if we have a not-outdated version loaded already.""" + if not self._state: + return None + + if "last_check" not in self._state: + return None + + if "pypi_version" not in self._state: + return None + + # Determine if we need to refresh the state + last_check = _convert_date(self._state["last_check"]) + time_since_last_check = current_time - last_check + if time_since_last_check > _WEEK: + return None + + return self._state["pypi_version"] + + def set(self, pypi_version: str, current_time: datetime.datetime) -> None: + # If we do not have a path to cache in, don't bother saving. + if not self._statefile_path: + return + + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self._statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self._statefile_path)) + + state = { + # Include the key so it's easy to tell which pip wrote the + # file. + "key": self.key, + "last_check": current_time.isoformat(), + "pypi_version": pypi_version, + } + + text = json.dumps(state, sort_keys=True, separators=(",", ":")) + + with adjacent_tmp_file(self._statefile_path) as f: + f.write(text.encode()) + + try: + # Since we have a prefix-specific state file, we can just + # overwrite whatever is there, no need to check. + replace(f.name, self._statefile_path) + except OSError: + # Best effort. + pass + + +@dataclass +class UpgradePrompt: + old: str + new: str + + def __rich__(self) -> Group: + if WINDOWS: + pip_cmd = f"{get_best_invocation_for_this_python()} -m pip" + else: + pip_cmd = get_best_invocation_for_this_pip() + + notice = "[bold][[reset][blue]notice[reset][bold]][reset]" + return Group( + Text(), + Text.from_markup( + f"{notice} A new release of pip is available: " + f"[red]{self.old}[reset] -> [green]{self.new}[reset]" + ), + Text.from_markup( + f"{notice} To update, run: " + f"[green]{escape(pip_cmd)} install --upgrade pip" + ), + ) + + +def was_installed_by_pip(pkg: str) -> bool: + """Checks whether pkg was installed by pip + + This is used not to display the upgrade message when pip is in fact + installed by system package manager, such as dnf on Fedora. + """ + dist = get_default_environment().get_distribution(pkg) + return dist is not None and "pip" == dist.installer + + +def _get_current_remote_pip_version( + session: PipSession, options: optparse.Values +) -> Optional[str]: + # Lets use PackageFinder to see what the latest pip version is + link_collector = LinkCollector.create( + session, + options=options, + suppress_no_index=True, + ) + + # Pass allow_yanked=False so we don't suggest upgrading to a + # yanked version. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=False, # Explicitly set to False + ) + + finder = PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + best_candidate = finder.find_best_candidate("pip").best_candidate + if best_candidate is None: + return None + + return str(best_candidate.version) + + +def _self_version_check_logic( + *, + state: SelfCheckState, + current_time: datetime.datetime, + local_version: DistributionVersion, + get_remote_version: Callable[[], Optional[str]], +) -> Optional[UpgradePrompt]: + remote_version_str = state.get(current_time) + if remote_version_str is None: + remote_version_str = get_remote_version() + if remote_version_str is None: + logger.debug("No remote pip version found") + return None + state.set(remote_version_str, current_time) + + remote_version = parse_version(remote_version_str) + logger.debug("Remote version of pip: %s", remote_version) + logger.debug("Local version of pip: %s", local_version) + + pip_installed_by_pip = was_installed_by_pip("pip") + logger.debug("Was pip installed by pip? %s", pip_installed_by_pip) + if not pip_installed_by_pip: + return None # Only suggest upgrade if pip is installed by pip. + + local_version_is_older = ( + local_version < remote_version + and local_version.base_version != remote_version.base_version + ) + if local_version_is_older: + return UpgradePrompt(old=str(local_version), new=remote_version_str) + + return None + + +def pip_self_version_check(session: PipSession, options: optparse.Values) -> None: + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_dist = get_default_environment().get_distribution("pip") + if not installed_dist: + return + + try: + upgrade_prompt = _self_version_check_logic( + state=SelfCheckState(cache_dir=options.cache_dir), + current_time=datetime.datetime.now(datetime.timezone.utc), + local_version=installed_dist.version, + get_remote_version=functools.partial( + _get_current_remote_pip_version, session, options + ), + ) + if upgrade_prompt is not None: + logger.warning("%s", upgrade_prompt, extra={"rich": True}) + except Exception: + logger.warning("There was an error checking the latest version of pip.") + logger.debug("See below for error", exc_info=True) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000..e69de29 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 new file mode 100644 index 0000000000000000000000000000000000000000..37ad74f7b0980941d726e648b1258f6205d48bcb GIT binary patch literal 213 zcmZ9GK?=e!5JelSAVLq~q8-$&2#Q-pyg-RHw4-ejGSfm&;u*Y(UPAB$(w!^QtsnmU zH=kL|Gm0jG*XuFBKHvTLS2y&{Eog(O*xv&;@LSj4d>IN;DIiIe)NpX-qB15pnv-G7 z2zEy2X1j^kM40PJX@;axx`7f;U|TMXUL>Mx literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..8873a44ac27d2c5daba381bc6cb6119b3ab2d997 GIT binary patch literal 4554 zcmcgvOKcm*8Qvu+TUuIvM0FH9a59#RN>mvVO^Y7lRt_yv7GsJONGh&urSy_JB3EAS zE_RldDL9D{hH_RhR3l)iXI1$O zoDCSkx0SO&uC5MtnYN!s=xya@=k@FWA6SL_FJ652O|?B|4D?YC8oMBO$k>g$)(k`j z^^XS>Wwv2geABT7D|=3(TJxE6k$Y^#bLwnW_wup$VrS-_I1;y4e8jT z+tfgb4GO4V@z=<)1c@$$Q!#bOV}?^{)Uh$0LWQlE7Vw_*Yn+W0IvisWQUdI-33#5? zx=TGbz%#yQ%5aNqQ<+(y|l zMJ>t=yNv80!gUUDyOw<3XHs)SoQKpN5ZYG+B* zbl8r>Y6AwnrmfLbcRL9!Lnf#*`Jkee-@N#s*3U0@6O+^gwi zD#^wY1-y?%*_m{4F1J`@pvWh(#U+-TWr^$(JDtuZqb&8qg?y?|V7a`Oo?pnMQUczLX$U0G7nG8C3nHlmF|$XP;z$M_7rLnHc6`Be zDBwjr@02NmMSR||t4{Ld3kd{o)uvhzkD=IJj9X?o?lzI(_IG06eladgpTFShl?!^6 zi#Ym2yw@M%4d1jxywooHCE0e|W^1TqJ9gR9?F$|?#AEo0L%3a0?uYmOF+6omTz}*J zH$JRvj85GdnYt62+QOo;1DRczkjN}!a9#0)gka8`rLxalk2>#_`+wD{iM1bw|{p zcy%K1b+{s_yp~p!*VW7FD!J(k*LJJ$UK~;`<7>qT9#)=ICc81XT`srvtV8P8(4fBi z;$`(G>UC9B-aQ>snt^d;yW}2IypXCaVK2?VnsO<)q^zmG4z8(@kX{0?66IGM96#Ij z7N;-Yv>P}w;b^Qg-_uPjR1qGP3&KJ$^f_Br-c|4-mL!)v>Q2hi%9fYA)wNS>b`)6( zf$qFCp;yWO@A7(FcP-m71&-$?#x@6pXbaCr3o=(9Iw zZse~uufO^Jn;WB(x4!$^ft$zgj(+c2Xmdc>H`1p5GpIGZy`w!ChMdnn9aO$?^r4~- zkGA$dy&1&wUxE&gZ;d5hP=E1!;z;0j=m3VdpWaRPBSVR)f!p5<(m0~hc#6i6aBFv| zgipj$sWl`&=&9*?)T3m#Y3&urIt!76itATuvY)mFJzm9W2QH^RB-@LubP|G(6j{$C zOpgY?kFUOl#iu=L9aOf&k8p#%s`?;sL=A0CE9!y2DElAoQq^y54l050J@wFC_0WUB zJ~gFo1(m?zO*z~=rUZuXsR!?>2d`B&)WZ(~s`|1dcy?0`9}ezO$G4t)P92f@{{ujO B**X9K literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..27c782b502f0e145cfce0e47980678333d9427c8 GIT binary patch literal 1884 zcmZux&2Jk;6rcUD*I7F?ABC1uDI>UKrDV5IDd_=_nzR(8)ggPp$E7?kdV?t4iVx2R7gGYH^8N#prRELNO0*bl_Meb#CyA5H>vU@zxO=z_RYNC z{NDRzeB34=fB&q9{~3h*gMSJs4V}RvI9r5~CSe-WBdw`{H6p!ZG>wkgG&RD^W=SWP z2rJzotjx9LT9M?Ly$AVGnijKog;ltrQR$hxwjVNhe(6G)mg- zFmBuS1-=-@T+&vym929@TTvh-T}%X3OPt=sXfE6pE*28WCtcTdZqg1{O9^vrJB&l+ z4--y0v;&#w0;jDYiZ}xg(r_BYeVRZrK|2ZSMR1~83RD(UT*hs4bvZxDevIWb96dG8 z$Nki71yVh(!%_>^3#km9!6Z0agcCrIGGM*C?YP5Z2<@d=ZCT4A`T+2r@}~jND#N%Ppu)OYp)!7go0bI;`Js#ZID0eCq7eO z-v0ddwzc!}gZj*l`n5ju-O6r#=0Rm<03pP&(u(iLL5KT(YWsf9Ebwc-|7kCXhC7z; zv!n&fhEzgetB4mteNV(o=)Zu

c$frBxiEflCd$v}Zy?b_#fZ36mx!=mnPi!9- z^Cb9vYT|*S3T{gSRY(~eJazB^s4b#Kd(Yle6$61RWAt;fPVNoAxi@40zV_N2K#EQL ztZ%01LQhd1%btJ+OL~GH0(schz}lRIoH?dIE@b7W)-Yc>Hflsi6R*L(#N%m>)M<=I zbqY67ZIFMf^{>abS07EB*_}9Z|M zqJHfFdPgCpkm#8(t-wknF#L?Q&1<|JO5o%XxM1HFJq;kxtkq$%VUJv2N_r8Fu_Fc= zjyM=(2^|48=}cu`g)Vn#7*l8kchm~54=&6~KMN7Z;GzwuBwh!b9+SoJo5$Ikzep`$ z>@tp>1`-S7#Yt2n-lnyK!&gw)#O9H2fZ8DYHBzcQGEeQAr?%hy&2;`WtB=f+yXML5 z6FbiD=2Vu)(KE(2oT|X)%WqRw{Vp&oZx(!o?C`XFIUCP;x17M`j13gM!xwv6c_;72 z_r#m9i~N!2LG2lurajS1nmIT|wEAD<^j?Kj$2Y9~vZc*#UfODW)z~NC?j6^)bDQ&f R1kBwl12p@#rA=pd{vXI8%Od~) literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fd5b619fdeb4d9a8636c3e72ff94f0431a0087a GIT binary patch literal 2428 zcma)8O>7fK6y9C0z4mS#yHKE{&@O~Ptp+b8NV%j{TTptT0h;uZLsy&KiM`HxcQvz) zlgMe43W;!n8$qgsdjr8C#~eAL_7amwq1#ldRC?&m$h{Qx&^NPo5~XS3NuD=9Z{C~v zzV~MRV>&H!Aiw?~*oz5{`;87dBDFiMZ=rLCv$%22;w{19$9blOaRGG9iIv51k*8~- z6E7#mCFsXd(n_G@q-aUk#qpHY#b%O7X0r(^bv-_={A*T**~l`1M3g)ValwNE9@4Wy zEw*b`tj>0xGMRxn0xp!&8SMVK%M%F7TEskR%ylI^)Fga^MERk0l)wX>%} zi~@KrV8f~yjx*b@P9fymZh?6Y5DAFt8-(=BhHI$^P8={h)xw?+HsIBm3=mdfv)XnB z$5hRt;TDjUm;az#jO6%Gylj&IbeHcK0UW(dZyZW+b=N4PFcDqVPw;(y)x2>LCS`!UBzArnE}zh@Fu*#Hi`DR=N7z6pPv1M5WC=(0TV3`$V_XF zJ)!xt5X>FOe{fzSfb?14FsCTS8qlNZln?A0j#de5hiL8NEUH zrIgw`!*Mm?VVe>x)Pzyaip|Gr93!)(8}p0vx5qZ5o@beZOG-1r9qC=T@{`oP)pP9LnY(B1 z_22EUf3qsB_k6sRd9I{y4c;8wRE{^4s zgk1s6D<(Cr#&=j6<$}&L%athBdj!f+fc%Pe@{92jCM*M2ZFd-Fz&sZZV`P?u$ z_l|k~6?DZJU9lQZeb@NXK4lirj9^mg?(}war-ZCTfuDnEpiCOU^jkJO2KZBw!&JHA z1a?#^K;*#rIZ;D_nM>jxSjKPDCaN&{`03jfmSMxF3R%g;iSO6}euFxdSp6WsO)_MO zjp38Go98gQ#{t28u=RcT5gVF4q-WpS%${y!PuHufeNQfIWUsDeuB|1nJx?FnO!qa? zkP@qho}Ai9Us)LLBun+F^v<*F;iZ|S4AffJfm=g2hrSc;$#>;-$d~DQv9VKBIeskH z9ZI@xd8V$jOR)QgiC~s~JTZM#Lj_*^ydHhzF?}5(WMU-1?4b|E3D0w)q-SYNZ}b2v zj+#>TUM7d4e*_=lei+geC6t(EjOY0mLV_1tS&o-~=Hy?vBfoR!nhBo&aQR}B169A+ zqUuFb;k7zUfm%7m)Z^Y39lbav@>lrf(I!Xr{R@#^9cZy}^FWH%mdz#ys&3sM_}>1L IcG=nf0oi9PHUIzs literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..67649fc82c7229a92ae9221e9a3d1f23cec3579b GIT binary patch literal 2231 zcmaJ?Uu+ab7@ys}KYM$36z{67MdBg|oY31!<&UKyN(!-Bd$ot0y^w6Sw|jSc-P^s) z%yE>oScwTVA!!kl)~M0wiv$y1eUv8?eQ7C4r(%4>v&T+T}B>0lN2~LvTo+{ z1}3s;uPqIm%0uI{=-BdEV3>|gWX;y)ilx!KgUga`5{k{yDrKzXpX{!|gy2RB230E> zEvi*hcf*A0mN}|c#tOs@X8H&FQkk?Hzy_^iJGb6G76t*n)n1t9P>2NqS!fcC^E(mc zo1(iA7C1D5Y+ntX6Gl*t1xc{@vM|YUXo5?lgl`Zt@ly%Tjr6D0p^?F>qEj{$ zqLH#@D?`{R897RnVQ18#W{xQDI(EV7JKp&wA!flYgCmtB;Gb0hTB&$0?M}XWKp`eI zIxAXkOe+{f0W%cUw5fq@%~F^FM9Dd2@MN+w?gon*QK?066l~Z6A9)3)DOBhE{^(sX zey=6|?@IW&`-t;Le-$Hj0qDbj0?2>pUbt<+*G3{Be$f|1mqKyj6D}^eJ{F_gTDk%g zAwPUx1U`k+IYb}v9(!NsZmdNDk5MM;jZ(9Ong>89*2Sz8SjSjfKE^CDKW`Z4p-7uI zO>*mQ+N9_vxMpdT*dsTLNq(I+bKHjUSR1m3Pp`Ln>jqKSmf}yJO`zueWXHO@TK`XS zb4$JZ+nRb4=yRm=U-C`#i(Ep`5)c$ghB53_kj#R(hsaQBNy$6!a||Ne4wdtcWjQsO zjF&Cb9wR*-muVI$t&ko?DVVfa9Zf=xK~h#UF99o6%TiwNKDZN#J6DDLb!-(M?Cv_; z)z#g=AF{NZp+nCwiJY(6Iad9wX*5z*)~kk0okuMs2gU&Z&HfKq@9<1`+be+mjjltA zPSw(71ImL|HOm^83y!SSwDGmq^kB^-fRRdj#o8lhiiYhyT88_z=Idk!%Q?;7L*-FJ z#)byYU@vv0lscF?nHm^KWzH*4XdIO@re%4KYV;!JLhu=GLXt$pD>{u*?D7tAeNM%& zT`{E&_oW60Mlj0`*Pka;qi%puuiN=^mO~88(##E*ggM^zc@n|37bJuRm(T02m^Uqh z=@C{*U@vC*i`mATw!wm2g=q>sXh&kp6+GK}Cn&E(TV^uTnb}ic9Qf+!XGgz}-Q0eC z`{K4kOVPuZ`IY#yb4RWonLmE5cR9X$F~0kBEr&W5Lml%+mO{Ix0`-8zwa>P$A|UmScCK}n)De(*vdYN) zH#pSNH5CS5hPTY@pWeSL?O2p{eEss0wD-1@`7V9)%=I%ryua9a{C4N*AMsDMTeXGZ M)3-kSDCimbA6C2{djJ3c literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..874b57f14e8d54ecf925f677e251c3df6a0e5906 GIT binary patch literal 5579 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..764c1f7310283d97842fe363f766b1a85a1de090 GIT binary patch literal 702 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..7e7351cef49a836488f1ff4c2ef85ab0c3f01d9a GIT binary patch literal 3581 zcma)8T}&g_6~5yc+vD*V+hF{|pWT2rG26u^Y!xZWHpymF3DQ8D-L{caX=FSD9%9e9 zcLq|xUZE&z*_Q^|2Utbb5-CzrEf3@=uYIiA7aXYU9kJ3%tyJ~R2%dQAxnqy*MPAV> z`<`>}J?GqW?>*;y^AC@QL(rc6-6sEzN9bQq4SW^hhksQ<5hF`-?r24>XtsR`h;mPy1K-g#W+9!31Tnb46WNl}tXt z81A=mHYcx0)5?NsxbDbU%Id$sdBX{;Ip_RkRwE4PF+Y`+kAb?289gWHs<4dnFr-(q zn!$;tDw#4EB~y?L2bT0@tYmFu*Jg77rQv_J2eUhqX|W+c=KjTXij2jLBbN;6CMG7Nlt4}HPHro#`IK~P=yH{Gm%d@dZiM2rN#$8pL;M@<)alox;OCk$d@Ar z1K&lzjaDaTDw8v3v@Oh?BAbhQ;68UCF0eJTGq*kW)knv!Nz=;C{q6e|Hoobux%)qV z^TC@{cl3LAw8r;W`JoCww3~h;JQS+Kla=AgfAX*X+Z)*(f0TTf+<)!ZJ9WrT9eSrO znQ{87&XMn(BQ@4vWy2LVyc_-I_3HK875GJNAG7Z~ar%ggwtM%{hYvs8laGc|l~Ag} zric&0Lne3z7lI5;2tl53qN@GY3 zX7(Nd5=oTx%zR!-*f1$8xDUDnjfp-cFJ#7Lg2FN2HL_<64?}N+IvyG#U9jxnQ6d9> zEehQRI&lY1_|PtQ#E+hM0=wd|XY|}o^Nu>A?G8+2W(_6}sAL35FUT4Mn(#?ZlJY_} zlg&xlX7vS0gjo2K@!1^@3-vmh5Gx(qE!lb^%E)w-RO9C_8npwOV3ye~YJ=x*$RccG;&ajDoWT zEcrHt5NO`wU4omT_KL+>h>gcP_D0>%R43miX0vpz-cv+n7NiC&%{DE&N-pd#xxPRq z4h*$dEba^RXS&Q@=@p42vz6F=#=6O?JxkbUA==4FDbB+rc0uLr4^{`ssih+ z%)sRE7t}3C2y%%DpW%Rjg|DYZnGY^Xx-bt_6AHROvODf_X6w%}JQi$Yl|%X|zT)2y|8htQG{4%lIZp z5+hmy3daVjI}pqNe*M$+lVGG8OjUxZgTQx#-wqxHZ*97tcn1#Ifz9<=JXMXqTZzA0 z<9$^=Ug6_V%CB)1_N7hw&=cMBpR$O5>6Ax-YlnRNurFQR{j~{Kn?=i8oyH^_}>`wLr8Q7^?)vY6GLS(I1^Vncki{hXzKfk*P{#>fFov zxH@88T)h`@Jyq9m#Wj5FikkX?sxMLTCBVv`BDKhHQ^Uy+4F)uLh1*2OZ?NNJUUnjmy>`LK;hA@!~C84~HLWIhimrbhVrbrahsARslf`Wyb|ms^oe z!`G6D;%2=!n0ZyroA3P>WcqK0#RdBYIJumcevEIyUIaZf9lAPAQPfj3_#brd2;F;% z#-F0pQ}lznBS3`?=ZRvIK+v!Bu_ft z61z*W6zD(?eUN|vssN)3kQPew$URCEp8{z@Dm= zDuUNbvMu%7k%K-jJLpN65>BfWICT~#1XE2Blnr#b-!R58`0xD?x-FsxV~Zvz4mHZd6m{r4e4Eypa$H9_blLHnj4BQT z1=!OJ?unv%m*_&9I*o?khOlT8s>Z0q#j?2|m8Y7xAL>ZjSgMu;le{U)OrDo8slxOY zX{8yIaaKVanCFUJr7cFwP7rWKApt1zz$-hxx6)s;z{(Dec#77*xI}QNRzT}|eOSHlTMfV`K+If1R<*6EPDjIN6N8>v9dGuC7)EkO(U()AZpyiqq zRGB;2V9ROamoWlzUqmXN5APRhN;TwF+pdE3=0c&&>vp5vaO|N?+mo`%r2XvW=bq2^ zw!Jd;^LJjJDLOMN&&<3~xP9@lF|(2$1RKC-3Kqs7sCh$-Knf)$ojz7}jn|ni9a(n* z`z^s2Sy@T@24CRHD@*%L9#r|OKbm=2O0OE!AwG6t84zpx-U4_9+Ye?7m#^4}@RFjtD+bOxpP7C3`{S%7Ype0h?jM>Z1ciO+LW+b6bb z4^sBW7yp!cX=mib+V_5S{qxk>ZakJvbV)3p*n<`10K5~RK)CMhLbo5@0OLN??-*(T z^+gM&9cr(V+w+9nF*^oGuY(j*XjQkP<}U(`9c~@76T9F4${p)#)?-QzwT=~eD~*`YxIO;2NM{kHA4Xi>fz{tp)eJ_N~9fR2`Y|*Brs&U zI|qV%2RcZ6iky0S`__Z;!s`6)_~h2D&0Aj(D^AzaJ5OBvg2d8v?akeZ(_5|0)>db; zvwi*Eod**yt^Ig6cYLF@-r9I~{oVI#k8=;H#wNbsF%}U{hngC|CQ6e*R5F?rt(J2t73sulUtwu5V`wROX5YH; zZ@%s;W*+710CM||MGx=@{aDBBL*G)_Xa|d9ku-cRVlof4P7J^sK|lUA>oF~dS|=Vw zLPI}(6!e`5_<%Tg`m1Ia7uK`ElcTCIlw&`Pwix>dZu#VJUJg`eKMcB6Fj`rFqo zp=Uki;9j5?Udi|D1$?Gg@V}YmgGOfiM%qmNe^&OnH}pB$*aepd=dmP0`Gbx1-Hfgo z`CgHH4eH|bW$M7@r)RY}0VS6=eU~mU%3T-guVfJO7DgANp=0a>6sVzh7XaH=^fx2Z za*z;D;yt3qUE1<|pDs3~qP;tf&UnQWpnwcQ+TmIiQCUXdQ4AAsw_t%Qka`3jGl4X& zX--U_bPBsk?C?Ga42Y*9jAI6+ID@i3`BVL;1pf% z3&TUAGogbYCQBv9_e&)xF-r8ZpsN6e?hiy0w5|=0Q!m!J+w|EFL>`8alVu&cZp<)@ zKa;fnW&T3W{gb@-A2Vi{-7JYexnrG!Yr%-Qn(St?M&kW)m%wA&y*K-NZx6-p*^F`Q g-gKA1Hp^55?}8Cwppm^weJx5Vbr11ppc;4*&oF literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..235a0167e0f859a6485470ed91ca7037771b4a79 GIT binary patch literal 2176 zcma)7&2JM&6rcUf#&+U><2p$pY)C0_P;3$t1PuihlJr9m5Fb)yK-StbcGj$S-JLOz z$Wha(Qk|+2i8#bkd&r>&Dth49zoEU@SSZng)Ji>Zvj`{DLuc067*#ni^3MCcuX%6Y zzPJ9ftIG%I`uh)FUlRfNhcVt#cEI*o4uExV13&;d10-9NOL8vHCwZ3&Nr6({5DQXL zD#%Hh0|?~1y~ElR3R#|NdfE~XLDALh_xLu2lB?-$OFRT+SM#37w<+v!HGOS~hoItW z20Xq^VP}@lbtJoygk%&%Jz2h=D%pefLEjlld4Tx*2W<}zLH}b~CAN|w7(hYTMZHDX z{hgTXUErd@mrOMm&j)@~(FBJ{6D3wTd?9`&n$oR>{&K4a@qg_aiTy0{aB}z=} zYAH03Bc8u}=^AGD&sb15b2mTw`1UP4I&yRD*1g#6V^K$h8qpltMxd=_K-g*SYHJ`j0P-E4yiLoz z=bPafO)Qn}@C_{_| z6(T}`O6HM@%vRg^d@5bDnqj0hdJZ)=tc+%$SPqe*jk1XPLeF@F+F=w`4gC&Mts*i< zqN-)8R~E>;WhUmfS(Ex+()I&3+g>u>6%IR+hBLZ88x7d3-y9z%noaOMoyGNZsV65lN0y0TFb{Cm<g|Q953p{cuF@3 zvQ5p1mxylQxR*+-xZwD;60uUPTG)d$8TRSKO}Z_E*9sUIdv@MSd2l7k*NzzFKH_Gc;NcjXpZn2%UHuIMI}-OH%>+!t1^@Uv;w4GqiDd zW4zXLfsv-4 z#%TsXpy$EV%G3*`ds7*%E5jQz4Q2GHGW%O*Gd5k1O*dk*+X5euoAi$IHoY^5h8>x; zMF!h!G#uVWc7y?$EEH*@+w22p(?3AqD74#CH?Gf~ayoa=_(>|rwTSHmQz^|fEusXyv zX-D3f+1Z(Ie)E0b{Jzh>Zf}nwC|~?a)c(2!r!LZ7W$igzxOfv zMEw8etD*lOeZZI5Y(cs-7f5%IC_SWx)kt1U$3#R>0XOIPJ<>s94@K|oG`fYd;v76z zx)EAHYPc}$&ug|-^bj=1%aVArSAy9KDBYoUs4XN!f~4y$_`)AGO1g8B+WKKI-2?ru zoT#>aD5ZNF{dVXlDneEiTnY0i8T$(tI-L|9>72%FCvw5EHPcY^q~J(JMKhocUp_m1 z`oiTICtzvTOF?$fHAyB{i6bd=jyWMp>=HGy4S-x83N)S<--6CEviX=>3XNm?@4PJp zx4(IPRqziiAUo`TZ>nJiB}L^qY&lL0n<_eV#Usz1{=k8o@f*U7T71tbqr&Qq2i*+Y*!vg zhdD)nQ|4k77oTP@8^98E6axavFe~M9a7+%jf^DrxK*%=Kc#hZ}yrNkK?B202nOrBN ztSL5uO*DorFs--0Ma{CNf*4#R*}P(CtmrCnUZDoZ;cW{@U686q3805@7po0|4Gfy6 zrbS)pmab%pDVvn5KwD%doL?CE_F z%zpXg`_pHqCyshbVtAHtOqx+PKOicet#DY}1z479mhDTG@g;uvc?}#Sr)WAXfOF?c z;J*ZeMj4oeTNyk0!C+q;Jj^H>}V=Ar`h?^teiEADXU^BDVUe?JR)`Y@R1{) z^QmPGGysBf+SP`laLG^!)hjMW?m=>to1|=NYQr4VoYleh6|;o10Np!C!+8L&oyQ|% zMUHC(zA>AC%K}bXGF%&RPxdUMSLA3?asuFqPKepCD|LeNR0FT$w=5}8&eDwERs3>3 zl{bqd#S~UljMPPH7QnVF^}ac4+FF*S&YQ-ZdHU4YNyfA}qX!E6p2{+$+gOpu9nr2%K`50jRci*sncXOTJ zs|Gd@o!!_dFtGm z%=F3gXJ&v{^88u@vE;RE-U*Sb5LTHJVM-B1>%G025jkc=1d^i47_Jc zwgN=z#2a#&LyY?qPXVCNPObe&7xy4O@@d46{u{^Of zv3zdn+-KeQ2ks5j#*VM{oLD@+89`k=%Y93IYw)`&%&3+qDrz{rJbRjXsdRIf!EKtaco%wI6ykQENR`laA3Y zIPxnT%UQzZ*xx~CJIC&n(gELYu}Zw_i^$$&-^2?}|m6Zbx}=qx|296Pbm} z#{HFYnxI3x+6z@O-~?a+J-`RvhKg}D`)*42@nK$_<)3DtT10=2w%?L($cy9avG}d| z8}n@HZ3IqZrB84Ts{C^xT@0IHI=V!fQz zHJSfsa8nIu;MI?ABEVB8xYHgzwKXy>5RfySU{NWUkgOCnASQ~r`0fm3t>!%8SKB5A z&(0VS6E)RR)}}nrK%!$)Cji-on@PAg)>Rh#Z3()(;ifuYb8r01$gv_U)yWBZ0@m^% z%AP^B5fB97De8QRTArfzrzrXdwC5Qb`8|?BJHhCaP|NkJ-U@xu7T*Yn+TeJKL4Hos~NwAKacj03(~&;ZGq5N literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..05cbfe0df3528d222b631e6ae0bd2d1465e3f465 GIT binary patch literal 7476 zcmcgxYit`=cD_Rnhfj&39@fJTjcnNxZA-E(JATxU+LCQ~qqS38t`l}sV#FCyBas~L zosnq@RH|f+MoNTA-c8psb^$Yhfyi}=`cHwhD9}1CilFTe$#f89qO>XMKJt&T4AeFH zqvs4cBqg&O6kBuz-sidRbH4N4tG}zLa1r>v`CE%@sU_r}F=Kv44P;jTh#}-A5sAn| zNFWz80mjH%^075wHS%nLh1e3YMY#aSpsY1wk2(SlBh5yfQCGkfbqCy0Prwtc2vkHX z1C>#4z-!doB2`gez!$9!R7Yz9HAWd1sg2eJ>Ws8KvLRX@sE4#eY7m`L;}9#luCswA zi8D&vk_$>bl3Qd%X2{wIlON(l&vo`*eocXm79#n?3J9yk%6qUb{2*5&dd=L&Rrjp9 z^1vpj*(5O|M{@bn(3B2rE}4rD=CY+EUk&+YBVS|8w?b;jw__`GDL)FN#ivKTe2 zSO+!R@-+>xz73FXEh||MHQP%h8z8?U$@m+;#fhHxGkRr6iD}X`Eh@x>utcGx`n(X8 z!~rQ9SExXfXXJ?F=XC4ISWJQbb7Vu7n_K!6EsYB*Ivpu%rd!f;Jo+lQHieQfO4rf~q_& z=`2?09P~6sW1;*(7#qw&hzGFx4~E)lMj-|xmO`?S5Sh_-LewhFW)^9jnG!Ac@-0** zX)TtII82FRJmM~<%~Xn*hpbwDK;8j`pI}lX#f%;#g#Pq<`l~Q|DaOVAXuoDR%SS5B zR53QyRb+8>)aN9c&#W!>GGR$sauSzFNq=C*>%SB!neB%4wS<`abcw8yEVoanrR%o4WnNrPGvx`f@@j zB&n*-UI|`!Rkw>Wje-9At+@^yaVlMvl`$M9ucj($9?$bHu67M8QK?H6R4|^d*Qhcg zg*3J6qB5vxa!Bp^p%M!#y{9@(s;V50MWvY5)p=ElUG0jADgdz?B24-Zdr71 z`Nmr_*|+SfoNIsNYFf#QoR~f_zyGnDf8zDcj!%!jpPK*iBQKxjtiG0&=3NWDndZH7 zeOZn)@aerbCKjt&pVT+rOx{SQYwysH5+5cq^<9fqUEgeOxwG%1gC8FJ=){L7?(ffR zJ2WRw^<_5`w|A~R<7&!oAs*lJXI|3K{FE@hmM=H*Su4aZRMd%2S$7Xl9`eqf-PVV@ z7zlN4G$GKix&{tV33tJ7_3r@=UL_28BCP-yqD^G9ypaPyAatzzMhb>jtSBn%_q25u z+nA|hThSs~f61npJIo(22grm)D?o_mDM%L}Xu|3wDeKRzHyH-v5w9sxj7_6wh?e(1 zup&J8bsw~@Syu#Ev$Y9X3~>^X#Xr-y8IokT{XdOu8u&J{m#9p_f@aUaUy{uk{vTV> z_=W9okQWUPzzaOuK}SR$m3U2G)ARJk{4nk59Lx!g}jI@2PHm1WlfV}m=k4n)ab?VWV{A_<=rL^ zU4c2OysTk+F+W;PN%@2viC|Z8K~;18f(gfQ6`e0TSsVy^rAovY3r9%^Nnw8~_-2Tb)zKh9EwFm-t;2jes^_fXYL+ zK%hGY1yw@mqe$u~x=7tN6d6;8AySjEkj~-6V~U^6wc3Uf%n&+gbu6N(*eOngwqS)% z5J!X%U|tX=1^zxF(LGR(W~%-v1piK+15nix*QPAtT->s^cEwXUdv^Nl?1kwI^Sv2Q z%L)KbVmk4Kw|U9ioMtoL?aSV}@0=inr{nPz3=dts?>Atz&zWL^jH-8rV+_U{Z8nG$1f1ceyDr){Gxfeyj z|59IpnZI+=-P6iEsPOePSRXXAr+CYQZQYRmwB8BjpYjKK>TD0|tWff>!Pe7af4JEW z+jAAW70$Bt~ms4L20ceDxwU1xUk|##fHzj1<_7DMi3j z-@`%SQ_p@+>t6|hz-fTh7(9cCZA~!~zusJ!nbO&mVGudSMq7&@K4M-pa?3G7;30+F zNC8d@MJ0?yD?n*+M=lY+Wjx%2o|Kn|06f5008td0L?l4l7Y4yhbKr4V1q)Zy&Ky-} z_w!1Of9=AhbFc5_H7Y}$fhdSWsOTUXzeU zZWGL%ZiYbL5Y!}93!1YCf?yChkl&IcC0tyfU|A}9wSv99)Pnv7RQJPA9fJUj%|pPV z!qef|=yY_x_i<&*im&Eo{f+vY%{Q9UwHe=zDQ?;An;UrV__D_bwl>8+bFOLfd1VW9 z;&8p2csnuI_Q>9dCYqd1&e#5u-cH;~WV~&Qj^RtAi9{LRj}nF1 z*h=P7jt1Dw-2lQ+oOOY17uTI)i<01i?JUs>EM9b)b?-31)saFQpUKr{GFsuj0A*~> zktZFdgZI16>+LzqF?2NNouU#r1$B4`j7FkrFf^uW0u_ULt1uW6rJ?Y!JTe-I#+3Mv zsj7`#OsatQ7Qx}|fE&IEOb5Qz^9gOZlN6HFSu z?}hVd82s|JI6Xe@FK@}6Mr347kjT-ZKDor;Te> z?jq%VO`=g5?m9}$D4-i*l74%REbN3dJ&VC12y_;4Qnw6=6i-Vfm+4$5$Y8$f^kb0q_T|L-%lIv--JZyCK?6Ey;bzs=VK&ab+ zvf|Y*RJgH#IGU~jnhTIX&T{*>0 z33GKT)r~hhZ*->X7VMeoJyZ5&N9AnAbVUZ%b>(s0vCkdHvNjM0`}b5&{*WGpPS>0% z+6MksCn5Oth%#l8ObM+lwL<6{zhC`YfO>!4c%Oo6QsLlrPF4-j#3w9#+q}ydu&AO9 z^SKL?6L;Bo_@qNndL4dhI|P&DiQS2e>)p5BerryAY~Q%-ty=R)lX3HlEdTPh6kr3v zxAaM*_;^r+3DW~!B006SP$bR9RH%e=DVb_j{*|^k<6)3^AEodOa+0jLH+%pOK|lMw z91VPVYKT0>iQR`pjs*TM@C=A8;@N}+@hrsx!ok&Y!d7x_EOfi>D|>F1l+Co>2`| zIhw$(q`Aw>4V%Hvoc^i4m4>F<_FMMbo?D*uz`~(S!~Usr%buza&dtl2+V&++`*iPj zHjvC(p?%gtJXMpgzI|(IPIs73Fvcs0&S}XwkR^(1GafxCo&j}W zfZAZZhUm5MD%%-^$6HaModA_WNY;|njrGXlDL&=vO@@&9blYh>-FAkQD9kAL=G;(D z04P()ur+wihNo+GP*EbrgRb#jqE`l~1P|OH31a+@3cMN^S>tw><4K6Nx)sL7_ZkHT zL;Rpaxo`MkGUK!>qQu!5e8ezp(#Pv1V z{xxa)M{@iNa{S*&?-J?#7t;MT*^}iO7~gz%mOzx=zlu@z6=JE%TI!e`^F3Jt(E?OM zbpP-tum6?y*}&ql-&*Rv_!Ns)t)}RA`$%>DytcIS$R~R)pevn9SC)Ov@Z}1JV5H^tSX^dgs!nj#bQOD;XF9nn0B9Ud1Tu;Fzj;7y+Vm gU0Pi@ogQCm*|&=MY&FAlq^&64v0#0Mk+H}B4R<$TCIA2c literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..509fda9c299ac43828ec48d690e9e2f8f1c2cfe4 GIT binary patch literal 1182 zcmZ8f&u<$=6rTNQdsD9yqg1VW*(N=(NZA!>RSC5PrNvbvL8uZJqP=X_yJLIG?(Ax2 zoYt!XK5zhWFTW)Zmb#LUJmX-1mwz4_kvX5P%3U+Z-P z@bT;y8T}>$@TXY38tU9Rc+8y#;1-C02O=qwV=0k6SrWbyD~al<+*ds<(mWl?uoCHA z`83ag)u?ir+oytxp^TmI7LM_C!9xS;xv&c*)&zPdB9oumfc35# zEq<+f=5Wbw<|nVC7_wmoErh!%Ndksa>=-Y22C$_RIfP{Ut2dyrDedmQZynWqPh}gH#;`A;gMRQ~O+KtUmTSx9@%cLe03hx8u zKM`2g%YoZZ6X?=_CINPDlJpJ?8FjbQPRdY7-H%h;OFz82wm~WC;RIslI(rcBxiRXv z*^u>9eBOC?-KD&{YgrI}9`qn}GnBbL!VD4|#O{Ehn7X1?@l&Xi4T}b%{`?ZY2#_US z&PhJ$+Z;yVxzQY7_|Dm1niyyPFwRblvrmoH(Y0yC_}#Q76>HjfYr3#_z_nn@ZWI;Y zkJ8Zh3!R^UaF2)%6sLF@&g(LqvJ59dG(3E$#%DKLt&jeHAEJpQg)&k!d_TZAW#xCK zzE5->A{7Bu0j5x@oivTh!hZs!DdGWS;M_Yu ze(+@F(wp*2)0Hc4GFPU{s}J#H`NHEbC(Es$>yzbMPe5~~YtqvG>I`u6@Z3Mblr{ec Duw_2W literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8dd98da08416195f9888cee2532a2a726fc7f06e GIT binary patch literal 2360 zcmb7_&2JM&6u@V8z4qD;BoI3R0?n4MWP#Xx94aU+HIP!i3?hI^)YjUp*YPIy?sjHO zFmj}{s#GfVKvk&(sS-g#Diu=yfZlR#F9stKmP$=kF1@)V2b5m=X6;RJ$fYCg+c$6K z&CJgG?R&qjSrb9f=6?}PwGE*^dB<;orn2CGatmcq78lTP@6o`>)gC$@{hLjVUo*}S?%UG5F;@hWH!IjTB%(N}7 z;7S)uPQh@4#9$?2>3o9U1W-hH77`$CA%^{d2TkP~rV)%7pW`*(K7>?!{on;}MXHPz z%n@C=Y7h!LDwGk^8r`GVL`mD0p_nf?EsMwd z5hA!Ogc0MOUK`_Eu4q~DK{JGtsDh@zqk_i5D<(nL593F$+a#V8D#8%jhL}e+pUwCu zd5x|0wVb#e5fP&+s4PsNYvL6&fz`nE_s$cusFAW_7J)K;%NplSv2%@BuZVCH1DhtA zp-#BcglQevpLRQs4P{QGFJ;c0JbEm1dGNx}$#ZE{c12pIuIv;vma|FG6>RE;d`u+j z6inu>O)+3@q{NItLTuuO&J8t`$YrfiY8X|~?HIL68IG!dCKqSiP?JE)!=We&iNc$N ze8GFv05OFY+mIanvhrEw=ES4W8*}ZQbwQS5zx8anbLjS=>dv=oJ%_9EhPh}@HM(uC zvwJ?i<4Jt?!}#u6eBi6d4SeIRx{TU7r_wJMLugIU_b2WRSEGAhAVG@#0c%O{UZP#F z(|6q|5&hH&Vpfc$Hi@$vF~}Btc#`q(Spwx23PX~Cw1Rlh$Cd$sp=Qep%UIwrs^Hbo z0SM68G623OLKPvi44MEvADu~&wfnOwBJ1$=2Tg$Kx{5f~>0AGMzk;Y(5k2gdk$)X9 zfKv8-Umpeh-g#du6h`3Z{eS%FUM5KpzIED_G^(4X8gYXSyBj@ObZo*J5Cx7MKXV3t zeSl}wU@ZWVIQYA=p3(Cf$vAF^7?iK}eivxXWVTe!LujE6@qYOkgk>vM4xaft4ehp#@)q|oGdnU*09cW|ko#^f8%< z>Umf&r%Uoyu^d;kCd literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa2d2aa738e3268cbb71ce003e52dee4b965c56a GIT binary patch literal 7572 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/logging.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f06596be6011691488c1796db4d3803ed86c210 GIT binary patch literal 13575 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 K-PfvDj{E}|Xv5F| literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..c63a49c27475adb2b411a7cbcf2d871a9b023ccc GIT binary patch literal 4568 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 B+q(b& literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..bfe8c0550f1278b4b1174d90100c17ddf81c6820 GIT binary patch literal 8736 zcmbU`ZEPDycC*~&_wqv|B~jGZS|4_3%VZpX#OFAUWl4_Z56QKaJA{tGqq!?-Wpb&T zU0Sw69fTlvm4X~>;nGS*uZXWGP#x?;??->QK!N&-qCK=|AX-XAZ+tih4bb*i>Bwnv z?w`IHaz%-j-Mfy&`F!){&Ac~n-h2DcE|&vAdh|~w`RjIs{*5$@)KIRh{fa?o9*Ia~ zQYcm`nHWRcrkIJAY>b7{l;YCnm^p2US<=>+HEoO8()O4=?T9(j&X_aJ$9RU!XH%}U zJLaZ!F6BvkW8QR4tcJGDDPP(j^V7N|6-d{{YSVSGI@-3T>eInkkU^3eTL6y@)@z70 zK-(_yQb-C8n+Vb57i$#DeNy8vOL{~H)Hu=kJ{N1EV|FP-`k|N3g7FZH^Dy50obmDu zfN}v;iyc+}i7lcVD5=Kmky=Hs3RR6%6I@_&W(``M8R4Q>PCF!<)O~SOsot9Nix1LFikITx4ZW|oeE^c- zsDv5aBB{y5xD+<&-tz#XNVs2t*~5vXr1P0^Nr|haZh&p8b`T>uH(Ln9{if9#M4*mb5HZ zl4O5IGDF1*zqRi}HjhTo6q7@}=;D5arc4^hbt&acn#ri&F4wRx$BdErRVin>U7p9F z4?6*d;IB%XU|!`=jyaFQ%;a{50I3H{2~83bLQf{0PAFnG5HDmdjY&x@LIJ{@?vPcP zoRb14$fd~nTVT(UBI*`k1C(JF`(aU?PnM9x=@GCbXQ3aKd3q%}nn_DhHKD>P(X%*1 zR#2noGM6%%oK&M{GRjD%_t@@kRh37Sw4`X!$Q4Ps5>3gMqT|=J(TuV;vgehk3a7k# zJdwPd7?ITIxI7+>%Y?lk0@17{r_^YLRgv*)I-gElmf~f)a5JF>e(Ficrcu#`wzW^6 zE_nQ3BCEM`hJ9i|eBF)YPxh@ig#sU3n-+EzN2VCu7-_m4@@Zf5FcFP>em1p zO0F{y8XWw{*4JKv4Ad1fws92tND@u&BO(B|OHE|364=8|M(ODv&){@I6Uy`paH3gq zHmWe3!Gb8OssvjtI*1}!ylTjJ7=9A^(?SIKCxnJ{`<~<-1W;{ zze+A2JGX2*_n3tVzg5Z6yw~mO_5kfVH`Os^i5>}Up)BRgegkOU3J@8Z8AOk4` zDe_!DID7ChrNm$V#0(3pA(pok5o@;K769ACTXM;%bcs+QeCRY_rT%$(`NHV*Jifs#m?7+RYk>f&`OkLRdX6SPrK8SAnrE2Kll0!~bH30Vb8 zVj`(YA`#MbS&Oh6cxzFmB&f%*i5}N89M%0;x*{b~o7jkV0kKEnr*=U`ukI;kkoi~V z;Jv*+*?H^yot-P8{WCq+PkwOn?%q6qFs^OvF`9 z%w#p_nGkhXMv0fTbw-pX+scakLK{xVs?^pcv`r+iLQ2p~87#G3)B~XZa3Y?$ykVlM zyGp&FwPc)8bXQhUB`t0=t>tG&cf$)3A5O?ANsN~$qQt~AN(%Iqp+ZG?#*)+?0`iGO zk!wepeh=6WOy{P7;Zt1BLQ6}I!P_(dDWx%TL#oo0Q(I^HRu_AUSL!xkuvJZmCSWiq zLQ{N2jg0Zn!Wv2~$6pS?OjpjuqpJ+&7?iV$OqQ8)n^4XvnsN@~jbo7JHt>vjjasD~ zBX3MqGFF-LAd-KzBB(VSiNZh#P~CPP#4Y#84Hf6kQii0mEicG(t~ssTCi z1K!VbHDlYK#iyE1qW2gWBYLSSy=ZKVT*<0r@PmPK+j_OJ-BnYdY0R1mtd9V$)KH0hoRIdJaz}-M`H&kZ50C#-_DpxmE3p*rFb5#P(0Up}cV>#{$GZg~f zhSzx;0(;py_C^&FXo~jAYD&#>U>p^gTnOiLtmwQ9cLV=O>ozdv9M7S}rJ8bng%O!M z{B5GGAx$;rY&g9^TOKR*Z_Gtku2FQ07MZ!@G3J3jTsIoWw$R3+SL-uyXkgftQbh|H zy8s{W_rvR};LVw?GAft>;<^+*i!Ij(+JhAX^o%+-(0sz6sZxry&pkQQ?egmce6Br@ z0An4+$ih8@wsy+EBG%{FKQaC3y5|F{!+1N=+6;&egj=V4aO(9NsOd09E2U`8`QZI2 z^NB(4fuTO-`@V02nLtpkp&Q(!L$oFc8;p}f zf;=o}I4glkrAk8o!0|yr%LrgRQ1h$e=Il@fFlQu;1@Pa>cmNYMDxd%dFRexdVGwLI zJOLI>314sDvA5oY?p|p)kxhX|0l1HYoh+3MHbL0R#5n{@2-MF7uUyCs3&i&VZ&)bV zPvED3*#hK&%aXv-IxUH0QX1EY|K0^NgoG%{#Nj4Oz8u*^!9Yd%qFvZsC8OJiXPtXmHK?=eMLgg?cXGw_Q zrLeK+W?PN&WSE>%mr%0c4A)F08y1v|Mi?|AiCqFQ=fEO{0~ZZsC7w`2Zv%JBXGh^n z5M6=glT2o@3g=1Yg3Vspr!bmO z*AapLoR$@c0|41{<#o)Z`3aD)^#F(fhVyt<6J%9@aEX{r8v7fV^qtEp3ZcmGS36bR zdkLI%HI-0Du>{^~QVLtZxzj{h(JiIj(h|fMN8o1aEHDz{3nT)gPPWrXjL=E80QNG- zq?80Nf71#>PZ@^}4kMkSb0SEPZl>oRW_ABYa~jJuSftyft1@vCMG_FAn~8%INa~~s z=;biT9(Z(SD*ux^FI>u`L?I2~21QF;9L}av#_ldeFCG#uTuNo7i=8SobW1vcFK5Rm zgPZ4ok3m5@)k*sWS%F(9YZrBnI2Dr}J*9|=(AKH80Z&AMZi8MVvTe96K$E59cuY3; zD_0UJSp)%xRS$uPZSX%{4{yiBYSuYf8P4d|G7oi*Xb#v9!4wqZEC1Ia3+%Qc1ZA{D--JDDT4Z005;+)I z!#hZalW;Gq01}|W0l^Mgqm0rW6@N{)49kiP(nWj+e3WEPk`ZH8w~|8!anT*$CMQOv zDcppK3yFyxN<|J6TNA%bGGbL?60*U>HpKf#Ml3*l0J4(fw&55YpqIsXiXNYjXl__3 zzG2)6<3_L%6Qf4wh(gji5@rNJznat?pn&2f86=@Kaw6px3njo{tlOYQ1yr{|vs7~v z6mgK3IF%Q=Z?y@QAP>s(5{;MW9-{O%kcqP}jhGMW&*32i&-wteH7!}&3$5FhY)u7g zV9DB42!>`{j|F7&e-U|OHPV}p^v<{+);7(*GxyG{q z^LlhPy6kA2?s;Gh6l~7xj#)Y3f|@~5o2ze;YhD_)_Q}xA%1FWEV9s9|VzVSL{H(!1T!n&N_$)UVrz4cb9CTnRiKj@`K4GTf@v`!NwDMR&Bw&Ex2L} zJ#zaB!KRO$H=V1&u6(d-IT)Ggec-Pzcxwy(`uW%9Ui*x77{v&s>0eSqz5NGcw@ZRhFv;7~QdQ_sP z;0??>=bWqFZF%puPrdDh8vlI#T>V05xu#>*0>>K&uKL^a{`O^m=d5jpojLy~*!Z#g zrh7T~5}cjAbLPYYKD2O<#4}lFKC<~%Z4G%_!$SLtt)=k$z}5t2ERSj%=Hqkm`?W)h zfxm41S?ki#p{4N9^uPnF?}4Xo#yvCfz}ZmX{nrO(2Nqualy84jt{+(8+a6eJ3(nxe zoA;gV4+GIc$IFFK%g521(Ff7)o3VS^6DwEip6Pq+K;D*z-fgSi9eMALMQ%~Ld;DJS z-6NlRPtx9S-Wx6ijy?>8RvIIV>T=`W<-oo|WBaY4rO?i=tXz%ni38QNt$Is<5dd7+ zd%tn_tzFB3$R@ZVkNm-zQw3LOVfenQ<00R?%6I1Z&Vu(q!P`Vm-ZSR`ad~1lyWGV9 z;(gO+igx5~To}FY+76t82r=n{FrvSCsrB&Pi%W;kEHw`-`v#Zz!Gg19`ph>^E}IZv zOJdD#I2pRc_B_A6;N4F}u?aQ}xE;vu{+}Ymmmxolya6JK)toP~$lLf-?SyOlcL%z+ z4cO5??DY=pL!a2~1AEv{e4&9Z_LD9%sqYI9HnX}F3?UUBv9KvJuql>E0y;FT^h2op zAq1AlXC%=onku27N{pDOX_2jjk*Y?x4I}H3;|h$n?!;{Puw)jQVcQHEW<<;TF8Clq zK2F#se*<3#uE-+!Fi}ycaSY}K7@O3-B>EPNE}MB zFpx;l{8&~_iO0Yuf$iyBGgRSW|OEZ*+3{iQ`AxSSjXi*f=i zTrj&K>I_dPta9nv^YA`HeaRLaD7`_%;we2k5R*bl!UrRfvmybnwPDEqh?(Eu49;@5 z%Z}lGG{tJ`vvO#b%P|oCVec@v%Q1ZVsS-YOR)SjBz*X9VRAvI)y0;;+$Up?Xgh;Im3i?t&l%77H71HTbc?HH*vL<$yGmu8@W6Nv z;|9Oh!}BGe|0Z}y@Zy_k1XtG`TG+AdYAYhE-F<81A>T+YN^?gcP+w@-RS33z<+Qlm zU$Zd!H34}{vZpFJyC2o;VE@AWvK2N(zATVf7$ze0o z$bI7rh{hz$^j~DgA3z3_LI}U5t+4-FkF7jBL#~>S+GpP`vQYoRRr^tL_F~Ztb$Aqm z!k=63uy@Wc+VaAleElm$0uOrI;r_}_YE4vy(13r~etZCvNWlQ4q2u9pd>R_~EXjzY zi{FAww`kYMcTh}ZfbItq9$<3$N?WEIrCTN!U9X2PW|262SQ9fiLM+`#$!4WClOCd@ z#`m;HG9^dK*Gt7D#7m_>4{nA6Gp1zmsni718#fQrk8+W+F-on^l9gJ8r399i)g=A? zgMDNS zt?=OA*uUUk7+Yw~`@?zn&S_`S;%0&i=7r>~LyPY&wH(R^Un?T$y60RYMX`-%EDO@D z-bMdU`vIWiP`(8Kpz|KPMv7t!$DC!Jg&@86xO?Y+XrzQfO!{9O^FYJ6PDW zzp#5xVP~Y!87{Q#cw8%)7+=Bep3!df%wAjAwdCo{+qV~4(*3BWwrD05c%mG>8||~r zMJsKB!Emr>r&R}nNAuuf&re^^hxQbmw2w!DriC|e^(^{toya%s&if-p7aeeuffKjR z-#nA|?<{&~pBK67mg=_WT|0_3w8MvLn-_-W28w>#3?N@3u@#l%xL(-vcm@ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..f6edcc913f6c9ee20abe99a45e5badd23a655bfb GIT binary patch literal 12080 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 Ee}R+UAOHXW literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e91820fb5c102fceee3aef7b95d443f00a485fc GIT binary patch literal 11126 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 MZbtVOO~dnl07mS@#{d8T literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..35fe2a19ebe2c2ce0f60943ab2c472893de302b3 GIT binary patch literal 2423 zcmah~O>7%Q6rSC+*FU?lou5QC8F3aYlZRz;%69&?6EO%4g0Rs;cuh>N8J3FX9_wKq+IfRR1#XWqPd z^X7YRe`sn7BN)ry^Ty92LccP>8~z5fih;R>G^BAR8m?1rm~&-*Sa7M&rM&6Sh{Ga> zG(q#3flP2X2)thpX`(Lbp?%=zx=#yy>KhJgL020BIRvuQuwrr;WONL%6y=`dRrah5uiy)?(|Eb$_a^rSIuD}5Z_^DJ_fzfMQc^y+lRal}FY6ExO zF!dhgy~`In73E4wudhrPrm2kSN=DUmMI9rynWMm1nvzoUy6fv^&ZfFnH_xh=l1^8> z?spx=Lk@2f9EBjApk?C>JJ1P$E0+=B$pJHtA9fPm34oZk4a*5&{bo)l)RCE)rDpVQ zSc>zmqdHRA%@hU8%(KL-MybsZgj+!qgFhLBsf54h49PO@XM8nZ^iE-4Hmb+2*gn2 zihNOknJe(vU*I+0jSeVvLGu@RtMvthu&9XzehU19K0+n$AT7XRy2E>MZi$HHc-u&` z7u_}rd_iEIJs(A3)48V$JUK--z*-PID=m0q1R{+W1kG)@TG);J0LT*SYjlTqOnRrs zy`Xu7Ww4aBrdFgyK=QMFTC^1vg@OQ(k4<%iw?cjc=ylvP=+q&LS#(of@7p;gsalF{ znUf0Z(~Z&qY!L<8m+i_7lSJvTY^tntaVKQjN!3i*09iD}kBm&cZrRoeSLvEVVf7P5 z#k#_FkX$xvV^%h{rO=c?l&q=J>rfs&$_hi1+oRXHgxj!J7&cZIET8~FEJnU3F%XxW zFrlpACJdUwyV$PXFgZaMC~NJQVbcj;Rn46KCdM|#dznIp^=_}M9KqBrMUFNl~65+BK5jYG^W@}{qjSwq1HG$$rUAVyt_9Ic2k&0IeQ}|^tJ>c6_~3l| zYgOOA(%DjenwBqBxL>5^^3}?RKkj;Exw&OVd>)D|H}9z&`C|W!_*-P}V&ttkGB^HI z9#|FlXc%0E!r)Tg!2(#2j#Q;1_wtW@_lxt=tEIj_WfY426x;nwj+T9&_g9)f8(5TM z<>s05WwOD3m0#F%tO`H*n7df+n(nW(Rpmp=(U$Vr^10~`rhUs&wDh*y7vfErVHHE+ z)w2wpt}6jWas{TEz;WCX3N4|gCDgKnc0EUJzoWLAC~zGWp@x9m3qEMM-?Ylu gnjGMw$kSvj6}9 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..7926c6e3eaf4236b805f688bd6718bc049d24b20 GIT binary patch literal 4498 zcmbVPTWk~A89p-}-*YjqMAxJ9Z6o3BAwMha2{4JDmcNDBO2Op3hq zBt5*9k`lC{=FR((zPvx_7m$bu%0=8oYb^;r7M!@w{DW2+>21O!`Q=;Pi zjg)Mv$UcypE3zNt2*`foBfIv&0_=hD0rf*rqo$1>k9u?dCkjKIvWp#FqDa`N^uqjXoTXi zkuiMg_-ULr@&!$mbqi0crd5vMgq}WeAvUZmF=tdt;xUoim>+%Th4=1o2*|=UK@RIzip5soCC99@qtx7J5e1ROOvlRt!0+cC z*TqhwMPWh6F>~%Hu4Z%1J38Ri?MsV_#QxmX1OSr`bOKRfMikHuxR`+Eq*>1mG$V8% zs|xa+$n>!OIcWwty)p0nZ?6+sq4?Jfe;g!Ogy>~K7f(-RI1D*Z2`FVzfEkEg02C}! ztN|EfV+vZGPzRc>b5y1Hs-cr(%qLU}rW7>=>xPBF$(4khP7_KoSRPNQ8gT+(K2+Ot z-dnV$dd|iKJ3#cbp#T(IjRkGdpte+yt*kBO3{`hRBM54-C8I!eTVku*AqpsH@e|qp zYvUuPX_ydD&Cc>ExKfJkY70}8c|FEXOzlYl`lYrE;wzXKhIGP1Gf;8ZY1_A)xtCei z?r9HuAN8;FuP&5Y2bQjtMV~kN#)q1Af0|gm^sMnz841DI6Jetzx_su|ncts%-qN++ z(p73X`mF~#zGc1Nu83|oAZc|&Uz1Hu%~$Ro`Rc_hKN%iLjo%ob82LCgetBXfH8ynd$3vG! z##7fvE{*)$_Hv?Rdo!j{EC9s`vWR!!Kw^#A4Q@zTe1OK?*w>AebAhdt<3}*2mkqAO zekkUEc6V*8F=R*n`b?o9;A~MX|BtjYkTG${`eqzKZPo26?=DZ%Z#Dz0V^b ztVce0(picO-u7;UBkSP<5B5B2Uuj?cS?SQ}CvBxe7rt(3EBj%5Ie;2=y_>FB0%E7# zjQ+pr3MMve^@tq97}HhDG;}2t;3kiLc*fp!km~eUj@GDL=>mgd6i`g$ZJMMO*3C* zY;6lW<)EVw;I82F%q?L3zh9x zRAA3}V*BMu>SRQ=tsO}5+?Gn2>_?XEbM6b-uYq-^@`jRvnf8I2yYuhv?_`6*56l^sH5B(`roNGo~m;3JZEf3xse0cIv-%8)3!Ii-;PknXni*u!pp;F6- zOIKcY9N`@UD+3#W=tkhkrq|QyTa)k`2!f`&eRulqp1*T`ZU324xNlABdp9bogmnTo zt(W7>P(89*`_1N3DHvCc0QcKCg5R9t{6{XAk@2KqXhhbJRSjO_xR@~|huiB$-HZ|+ zjY$r-Wr*SsT&A?0EH_#s@HMrYXJF*7;GsvL0`&$^p#7Quz{}>&Qghc@pzF22`eukgmrQC6B(IjSq?aG4N_E~cU*2R#;WQ4&E0{^+<04}%@ z^Dc0sPS)dRzbt_=nO*EnV!GHu)uZgAhr&-443f#O$Zy9EzT=8-0|VfLMU9*{2cee{ z4EiUifcFT(3)J)i1^IvY1q%HO9eNeqf4l8@aQ}L6|NXP2VCRx=qwDyC$w#@B+=eu` z=@GldHPlfKd4(b2{=_C?Z8_o-x>q|l5j5r9qHsxg057a9hZ=A+p<^Hg6c6E3YK~p{ug73;31P#~c{{Y2#UAh1O literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d43fe007ef91a2376e5dceb0678853e1feae12c GIT binary patch literal 5944 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 Qg6aZCvzxAmq(*n-KW^RaR{#J2 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py new file mode 100644 index 0000000..e06947c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/_jaraco_text.py @@ -0,0 +1,109 @@ +"""Functions brought over from jaraco.text. + +These functions are not supposed to be used within `pip._internal`. These are +helper functions brought over from `jaraco.text` to enable vendoring newer +copies of `pkg_resources` without having to vendor `jaraco.text` and its entire +dependency cone; something that our vendoring setup is not currently capable of +handling. + +License reproduced from original source below: + +Copyright Jason R. Coombs + +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. +""" + +import functools +import itertools + + +def _nonblank(str): + return str and not str.startswith("#") + + +@functools.singledispatch +def yield_lines(iterable): + r""" + Yield valid lines of a string or iterable. + + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return filter(_nonblank, map(str.strip, text.splitlines())) + + +def drop_comment(line): + """ + Drop comments. + + >>> drop_comment('foo # bar') + 'foo' + + A hash without a space may be in a URL. + + >>> drop_comment('http://example.com/foo#bar') + 'http://example.com/foo#bar' + """ + return line.partition(" #")[0] + + +def join_continuation(lines): + r""" + Join lines continued by a trailing backslash. + + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar', 'baz'])) + ['foobar', 'baz'] + >>> list(join_continuation(['foo \\', 'bar \\', 'baz'])) + ['foobarbaz'] + + Not sure why, but... + The character preceeding the backslash is also elided. + + >>> list(join_continuation(['goo\\', 'dly'])) + ['godly'] + + A terrible idea, but... + If no line is available to continue, suppress the lines. + + >>> list(join_continuation(['foo', 'bar\\', 'baz\\'])) + ['foo'] + """ + lines = iter(lines) + for item in lines: + while item.endswith("\\"): + try: + item = item[:-2].strip() + next(lines) + except StopIteration: + return + yield item diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py new file mode 100644 index 0000000..92c4c6a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/_log.py @@ -0,0 +1,38 @@ +"""Customize logging + +Defines custom logger class for the `logger.verbose(...)` method. + +init_logging() must be called before any other modules that call logging.getLogger. +""" + +import logging +from typing import Any, cast + +# custom log level for `--verbose` output +# between DEBUG and INFO +VERBOSE = 15 + + +class VerboseLogger(logging.Logger): + """Custom Logger, defining a verbose log-level + + VERBOSE is between INFO and DEBUG. + """ + + def verbose(self, msg: str, *args: Any, **kwargs: Any) -> None: + return self.log(VERBOSE, msg, *args, **kwargs) + + +def getLogger(name: str) -> VerboseLogger: + """logging.getLogger, but ensures our VerboseLogger class is returned""" + return cast(VerboseLogger, logging.getLogger(name)) + + +def init_logging() -> None: + """Register our VerboseLogger and VERBOSE log level. + + Should be called before any calls to getLogger(), + i.e. in pip._internal.__init__ + """ + logging.setLoggerClass(VerboseLogger) + logging.addLevelName(VERBOSE, "VERBOSE") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py new file mode 100644 index 0000000..16933bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/appdirs.py @@ -0,0 +1,52 @@ +""" +This code wraps the vendored appdirs module to so the return values are +compatible for the current pip code base. + +The intention is to rewrite current usages gradually, keeping the tests pass, +and eventually drop this after all usages are changed. +""" + +import os +import sys +from typing import List + +from pip._vendor import platformdirs as _appdirs + + +def user_cache_dir(appname: str) -> str: + return _appdirs.user_cache_dir(appname, appauthor=False) + + +def _macos_user_config_dir(appname: str, roaming: bool = True) -> str: + # Use ~/Application Support/pip, if the directory exists. + path = _appdirs.user_data_dir(appname, appauthor=False, roaming=roaming) + if os.path.isdir(path): + return path + + # Use a Linux-like ~/.config/pip, by default. + linux_like_path = "~/.config/" + if appname: + linux_like_path = os.path.join(linux_like_path, appname) + + return os.path.expanduser(linux_like_path) + + +def user_config_dir(appname: str, roaming: bool = True) -> str: + if sys.platform == "darwin": + return _macos_user_config_dir(appname, roaming) + + return _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) + + +# for the discussion regarding site_config_dir locations +# see +def site_config_dirs(appname: str) -> List[str]: + if sys.platform == "darwin": + return [_appdirs.site_data_dir(appname, appauthor=False, multipath=True)] + + dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True) + if sys.platform == "win32": + return [dirval] + + # Unix-y system. Look in /etc as well. + return dirval.split(os.pathsep) + ["/etc"] diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000..3f4d300 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,63 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" + +import logging +import os +import sys + +__all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"] + + +logger = logging.getLogger(__name__) + + +def has_tls() -> bool: + try: + import _ssl # noqa: F401 # ignore unused + + return True + except ImportError: + pass + + from pip._vendor.urllib3.util import IS_PYOPENSSL + + return IS_PYOPENSSL + + +def get_path_uid(path: str) -> int: + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, "O_NOFOLLOW"): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError(f"{path} is a symlink; Will not return uid for symlinks") + return file_uid + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = {"python", "wsgiref", "argparse"} + + +# windows detection, covers cpython and ironpython +WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py new file mode 100644 index 0000000..b6ed9a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/compatibility_tags.py @@ -0,0 +1,165 @@ +"""Generate and work with PEP 425 Compatibility Tags. +""" + +import re +from typing import List, Optional, Tuple + +from pip._vendor.packaging.tags import ( + PythonVersion, + Tag, + compatible_tags, + cpython_tags, + generic_tags, + interpreter_name, + interpreter_version, + mac_platforms, +) + +_osx_arch_pat = re.compile(r"(.+)_(\d+)_(\d+)_(.+)") + + +def version_info_to_nodot(version_info: Tuple[int, ...]) -> str: + # Only use up to the first two numbers. + return "".join(map(str, version_info[:2])) + + +def _mac_platforms(arch: str) -> List[str]: + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + mac_version = (int(major), int(minor)) + arches = [ + # Since we have always only checked that the platform starts + # with "macosx", for backwards-compatibility we extract the + # actual prefix provided by the user in case they provided + # something like "macosxcustom_". It may be good to remove + # this as undocumented or deprecate it in the future. + "{}_{}".format(name, arch[len("macosx_") :]) + for arch in mac_platforms(mac_version, actual_arch) + ] + else: + # arch pattern didn't match (?!) + arches = [arch] + return arches + + +def _custom_manylinux_platforms(arch: str) -> List[str]: + arches = [arch] + arch_prefix, arch_sep, arch_suffix = arch.partition("_") + if arch_prefix == "manylinux2014": + # manylinux1/manylinux2010 wheels run on most manylinux2014 systems + # with the exception of wheels depending on ncurses. PEP 599 states + # manylinux1/manylinux2010 wheels should be considered + # manylinux2014 wheels: + # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels + if arch_suffix in {"i686", "x86_64"}: + arches.append("manylinux2010" + arch_sep + arch_suffix) + arches.append("manylinux1" + arch_sep + arch_suffix) + elif arch_prefix == "manylinux2010": + # manylinux1 wheels run on most manylinux2010 systems with the + # exception of wheels depending on ncurses. PEP 571 states + # manylinux1 wheels should be considered manylinux2010 wheels: + # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels + arches.append("manylinux1" + arch_sep + arch_suffix) + return arches + + +def _get_custom_platforms(arch: str) -> List[str]: + arch_prefix, arch_sep, arch_suffix = arch.partition("_") + if arch.startswith("macosx"): + arches = _mac_platforms(arch) + elif arch_prefix in ["manylinux2014", "manylinux2010"]: + arches = _custom_manylinux_platforms(arch) + else: + arches = [arch] + return arches + + +def _expand_allowed_platforms(platforms: Optional[List[str]]) -> Optional[List[str]]: + if not platforms: + return None + + seen = set() + result = [] + + for p in platforms: + if p in seen: + continue + additions = [c for c in _get_custom_platforms(p) if c not in seen] + seen.update(additions) + result.extend(additions) + + return result + + +def _get_python_version(version: str) -> PythonVersion: + if len(version) > 1: + return int(version[0]), int(version[1:]) + else: + return (int(version[0]),) + + +def _get_custom_interpreter( + implementation: Optional[str] = None, version: Optional[str] = None +) -> str: + if implementation is None: + implementation = interpreter_name() + if version is None: + version = interpreter_version() + return f"{implementation}{version}" + + +def get_supported( + version: Optional[str] = None, + platforms: Optional[List[str]] = None, + impl: Optional[str] = None, + abis: Optional[List[str]] = None, +) -> List[Tag]: + """Return a list of supported tags for each version specified in + `versions`. + + :param version: a string version, of the form "33" or "32", + or None. The version will be assumed to support our ABI. + :param platform: specify a list of platforms you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abis: specify a list of abis you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported: List[Tag] = [] + + python_version: Optional[PythonVersion] = None + if version is not None: + python_version = _get_python_version(version) + + interpreter = _get_custom_interpreter(impl, version) + + platforms = _expand_allowed_platforms(platforms) + + is_cpython = (impl or interpreter_name()) == "cp" + if is_cpython: + supported.extend( + cpython_tags( + python_version=python_version, + abis=abis, + platforms=platforms, + ) + ) + else: + supported.extend( + generic_tags( + interpreter=interpreter, + abis=abis, + platforms=platforms, + ) + ) + supported.extend( + compatible_tags( + python_version=python_version, + interpreter=interpreter, + platforms=platforms, + ) + ) + + return supported diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py new file mode 100644 index 0000000..8668b3b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/datetime.py @@ -0,0 +1,11 @@ +"""For when pip wants to check the date or time. +""" + +import datetime + + +def today_is_later_than(year: int, month: int, day: int) -> bool: + today = datetime.date.today() + given = datetime.date(year, month, day) + + return today > given diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000..72bd6f2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,120 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" + +import logging +import warnings +from typing import Any, Optional, TextIO, Type, Union + +from pip._vendor.packaging.version import parse + +from pip import __version__ as current_version # NOTE: tests patch this name. + +DEPRECATION_MSG_PREFIX = "DEPRECATION: " + + +class PipDeprecationWarning(Warning): + pass + + +_original_showwarning: Any = None + + +# Warnings <-> Logging Integration +def _showwarning( + message: Union[Warning, str], + category: Type[Warning], + filename: str, + lineno: int, + file: Optional[TextIO] = None, + line: Optional[str] = None, +) -> None: + if file is not None: + if _original_showwarning is not None: + _original_showwarning(message, category, filename, lineno, file, line) + elif issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip._internal.deprecations") + logger.warning(message) + else: + _original_showwarning(message, category, filename, lineno, file, line) + + +def install_warning_logger() -> None: + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _original_showwarning + + if _original_showwarning is None: + _original_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + + +def deprecated( + *, + reason: str, + replacement: Optional[str], + gone_in: Optional[str], + feature_flag: Optional[str] = None, + issue: Optional[int] = None, +) -> None: + """Helper to deprecate existing functionality. + + reason: + Textual reason shown to the user about why this functionality has + been deprecated. Should be a complete sentence. + replacement: + Textual suggestion shown to the user about what alternative + functionality they can use. + gone_in: + The version of pip does this functionality should get removed in. + Raises an error if pip's current version is greater than or equal to + this. + feature_flag: + Command-line flag of the form --use-feature={feature_flag} for testing + upcoming functionality. + issue: + Issue number on the tracker that would serve as a useful place for + users to find related discussion and provide feedback. + """ + + # Determine whether or not the feature is already gone in this version. + is_gone = gone_in is not None and parse(current_version) >= parse(gone_in) + + message_parts = [ + (reason, f"{DEPRECATION_MSG_PREFIX}{{}}"), + ( + gone_in, + "pip {} will enforce this behaviour change." + if not is_gone + else "Since pip {}, this is no longer supported.", + ), + ( + replacement, + "A possible replacement is {}.", + ), + ( + feature_flag, + "You can use the flag --use-feature={} to test the upcoming behaviour." + if not is_gone + else None, + ), + ( + issue, + "Discussion can be found at https://github.com/pypa/pip/issues/{}", + ), + ] + + message = " ".join( + format_str.format(value) + for value, format_str in message_parts + if format_str is not None and value is not None + ) + + # Raise as an error if this behaviour is deprecated. + if is_gone: + raise PipDeprecationWarning(message) + + warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py new file mode 100644 index 0000000..0e8e5e1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/direct_url_helpers.py @@ -0,0 +1,87 @@ +from typing import Optional + +from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo +from pip._internal.models.link import Link +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import vcs + + +def direct_url_as_pep440_direct_reference(direct_url: DirectUrl, name: str) -> str: + """Convert a DirectUrl to a pip requirement string.""" + direct_url.validate() # if invalid, this is a pip bug + requirement = name + " @ " + fragments = [] + if isinstance(direct_url.info, VcsInfo): + requirement += "{}+{}@{}".format( + direct_url.info.vcs, direct_url.url, direct_url.info.commit_id + ) + elif isinstance(direct_url.info, ArchiveInfo): + requirement += direct_url.url + if direct_url.info.hash: + fragments.append(direct_url.info.hash) + else: + assert isinstance(direct_url.info, DirInfo) + requirement += direct_url.url + if direct_url.subdirectory: + fragments.append("subdirectory=" + direct_url.subdirectory) + if fragments: + requirement += "#" + "&".join(fragments) + return requirement + + +def direct_url_for_editable(source_dir: str) -> DirectUrl: + return DirectUrl( + url=path_to_url(source_dir), + info=DirInfo(editable=True), + ) + + +def direct_url_from_link( + link: Link, source_dir: Optional[str] = None, link_is_in_wheel_cache: bool = False +) -> DirectUrl: + if link.is_vcs: + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend + url, requested_revision, _ = vcs_backend.get_url_rev_and_auth( + link.url_without_fragment + ) + # For VCS links, we need to find out and add commit_id. + if link_is_in_wheel_cache: + # If the requested VCS link corresponds to a cached + # wheel, it means the requested revision was an + # immutable commit hash, otherwise it would not have + # been cached. In that case we don't have a source_dir + # with the VCS checkout. + assert requested_revision + commit_id = requested_revision + else: + # If the wheel was not in cache, it means we have + # had to checkout from VCS to build and we have a source_dir + # which we can inspect to find out the commit id. + assert source_dir + commit_id = vcs_backend.get_revision(source_dir) + return DirectUrl( + url=url, + info=VcsInfo( + vcs=vcs_backend.name, + commit_id=commit_id, + requested_revision=requested_revision, + ), + subdirectory=link.subdirectory_fragment, + ) + elif link.is_existing_dir(): + return DirectUrl( + url=link.url_without_fragment, + info=DirInfo(), + subdirectory=link.subdirectory_fragment, + ) + else: + hash = None + hash_name = link.hash_name + if hash_name: + hash = f"{hash_name}={link.hash}" + return DirectUrl( + url=link.url_without_fragment, + info=ArchiveInfo(hash=hash), + subdirectory=link.subdirectory_fragment, + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py new file mode 100644 index 0000000..4a384a6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/egg_link.py @@ -0,0 +1,80 @@ +import os +import re +import sys +from typing import List, Optional + +from pip._internal.locations import site_packages, user_site +from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, +) + +__all__ = [ + "egg_link_path_from_sys_path", + "egg_link_path_from_location", +] + + +def _egg_link_names(raw_name: str) -> List[str]: + """ + Convert a Name metadata value to a .egg-link name, by applying + the same substitution as pkg_resources's safe_name function. + Note: we cannot use canonicalize_name because it has a different logic. + + We also look for the raw name (without normalization) as setuptools 69 changed + the way it names .egg-link files (https://github.com/pypa/setuptools/issues/4167). + """ + return [ + re.sub("[^A-Za-z0-9.]+", "-", raw_name) + ".egg-link", + f"{raw_name}.egg-link", + ] + + +def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]: + """ + Look for a .egg-link file for project name, by walking sys.path. + """ + egg_link_names = _egg_link_names(raw_name) + for path_item in sys.path: + for egg_link_name in egg_link_names: + egg_link = os.path.join(path_item, egg_link_name) + if os.path.isfile(egg_link): + return egg_link + return None + + +def egg_link_path_from_location(raw_name: str) -> Optional[str]: + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites: List[str] = [] + if running_under_virtualenv(): + sites.append(site_packages) + if not virtualenv_no_global() and user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + egg_link_names = _egg_link_names(raw_name) + for site in sites: + for egg_link_name in egg_link_names: + egglink = os.path.join(site, egg_link_name) + if os.path.isfile(egglink): + return egglink + return None diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/encoding.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000..008f06a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,36 @@ +import codecs +import locale +import re +import sys +from typing import List, Tuple + +BOMS: List[Tuple[bytes, str]] = [ + (codecs.BOM_UTF8, "utf-8"), + (codecs.BOM_UTF16, "utf-16"), + (codecs.BOM_UTF16_BE, "utf-16-be"), + (codecs.BOM_UTF16_LE, "utf-16-le"), + (codecs.BOM_UTF32, "utf-32"), + (codecs.BOM_UTF32_BE, "utf-32-be"), + (codecs.BOM_UTF32_LE, "utf-32-le"), +] + +ENCODING_RE = re.compile(rb"coding[:=]\s*([-\w.]+)") + + +def auto_decode(data: bytes) -> str: + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom) :].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b"\n")[:2]: + if line[0:1] == b"#" and ENCODING_RE.search(line): + result = ENCODING_RE.search(line) + assert result is not None + encoding = result.groups()[0].decode("ascii") + return data.decode(encoding) + return data.decode( + locale.getpreferredencoding(False) or sys.getdefaultencoding(), + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py new file mode 100644 index 0000000..1501369 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/entrypoints.py @@ -0,0 +1,84 @@ +import itertools +import os +import shutil +import sys +from typing import List, Optional + +from pip._internal.cli.main import main +from pip._internal.utils.compat import WINDOWS + +_EXECUTABLE_NAMES = [ + "pip", + f"pip{sys.version_info.major}", + f"pip{sys.version_info.major}.{sys.version_info.minor}", +] +if WINDOWS: + _allowed_extensions = {"", ".exe"} + _EXECUTABLE_NAMES = [ + "".join(parts) + for parts in itertools.product(_EXECUTABLE_NAMES, _allowed_extensions) + ] + + +def _wrapper(args: Optional[List[str]] = None) -> int: + """Central wrapper for all old entrypoints. + + Historically pip has had several entrypoints defined. Because of issues + arising from PATH, sys.path, multiple Pythons, their interactions, and most + of them having a pip installed, users suffer every time an entrypoint gets + moved. + + To alleviate this pain, and provide a mechanism for warning users and + directing them to an appropriate place for help, we now define all of + our old entrypoints as wrappers for the current one. + """ + sys.stderr.write( + "WARNING: pip is being invoked by an old script wrapper. This will " + "fail in a future version of pip.\n" + "Please see https://github.com/pypa/pip/issues/5599 for advice on " + "fixing the underlying issue.\n" + "To avoid this problem you can invoke Python with '-m pip' instead of " + "running pip directly.\n" + ) + return main(args) + + +def get_best_invocation_for_this_pip() -> str: + """Try to figure out the best way to invoke pip in the current environment.""" + binary_directory = "Scripts" if WINDOWS else "bin" + binary_prefix = os.path.join(sys.prefix, binary_directory) + + # Try to use pip[X[.Y]] names, if those executables for this environment are + # the first on PATH with that name. + path_parts = os.path.normcase(os.environ.get("PATH", "")).split(os.pathsep) + exe_are_in_PATH = os.path.normcase(binary_prefix) in path_parts + if exe_are_in_PATH: + for exe_name in _EXECUTABLE_NAMES: + found_executable = shutil.which(exe_name) + binary_executable = os.path.join(binary_prefix, exe_name) + if ( + found_executable + and os.path.exists(binary_executable) + and os.path.samefile( + found_executable, + binary_executable, + ) + ): + return exe_name + + # Use the `-m` invocation, if there's no "nice" invocation. + return f"{get_best_invocation_for_this_python()} -m pip" + + +def get_best_invocation_for_this_python() -> str: + """Try to figure out the best way to invoke the current Python.""" + exe = sys.executable + exe_name = os.path.basename(exe) + + # Try to use the basename, if it's the first executable. + found_executable = shutil.which(exe_name) + if found_executable and os.path.samefile(found_executable, exe): + return exe_name + + # Use the full executable name, because we couldn't find something simpler. + return exe diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000..83c2df7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,153 @@ +import fnmatch +import os +import os.path +import random +import sys +from contextlib import contextmanager +from tempfile import NamedTemporaryFile +from typing import Any, BinaryIO, Generator, List, Union, cast + +from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed + +from pip._internal.utils.compat import get_path_uid +from pip._internal.utils.misc import format_size + + +def check_path_owner(path: str) -> bool: + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if sys.platform == "win32" or not hasattr(os, "geteuid"): + return True + + assert os.path.isabs(path) + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) + return False # assume we don't own the path + + +@contextmanager +def adjacent_tmp_file(path: str, **kwargs: Any) -> Generator[BinaryIO, None, None]: + """Return a file-like object pointing to a tmp file next to path. + + The file is created securely and is ensured to be written to disk + after the context reaches its end. + + kwargs will be passed to tempfile.NamedTemporaryFile to control + the way the temporary file will be opened. + """ + with NamedTemporaryFile( + delete=False, + dir=os.path.dirname(path), + prefix=os.path.basename(path), + suffix=".tmp", + **kwargs, + ) as f: + result = cast(BinaryIO, f) + try: + yield result + finally: + result.flush() + os.fsync(result.fileno()) + + +# Tenacity raises RetryError by default, explicitly raise the original exception +_replace_retry = retry(reraise=True, stop=stop_after_delay(1), wait=wait_fixed(0.25)) + +replace = _replace_retry(os.replace) + + +# test_writable_dir and _test_writable_dir_win are copied from Flit, +# with the author's agreement to also place them under pip's license. +def test_writable_dir(path: str) -> bool: + """Check if a directory is writable. + + Uses os.access() on POSIX, tries creating files on Windows. + """ + # If the directory doesn't exist, find the closest parent that does. + while not os.path.isdir(path): + parent = os.path.dirname(path) + if parent == path: + break # Should never get here, but infinite loops are bad + path = parent + + if os.name == "posix": + return os.access(path, os.W_OK) + + return _test_writable_dir_win(path) + + +def _test_writable_dir_win(path: str) -> bool: + # os.access doesn't work on Windows: http://bugs.python.org/issue2528 + # and we can't use tempfile: http://bugs.python.org/issue22107 + basename = "accesstest_deleteme_fishfingers_custard_" + alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" + for _ in range(10): + name = basename + "".join(random.choice(alphabet) for _ in range(6)) + file = os.path.join(path, name) + try: + fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL) + except FileExistsError: + pass + except PermissionError: + # This could be because there's a directory with the same name. + # But it's highly unlikely there's a directory called that, + # so we'll assume it's because the parent dir is not writable. + # This could as well be because the parent dir is not readable, + # due to non-privileged user access. + return False + else: + os.close(fd) + os.unlink(file) + return True + + # This should never be reached + raise OSError("Unexpected condition testing for writable directory") + + +def find_files(path: str, pattern: str) -> List[str]: + """Returns a list of absolute paths of files beneath path, recursively, + with filenames which match the UNIX-style shell glob pattern.""" + result: List[str] = [] + for root, _, files in os.walk(path): + matches = fnmatch.filter(files, pattern) + result.extend(os.path.join(root, f) for f in matches) + return result + + +def file_size(path: str) -> Union[int, float]: + # If it's a symlink, return 0. + if os.path.islink(path): + return 0 + return os.path.getsize(path) + + +def format_file_size(path: str) -> str: + return format_size(file_size(path)) + + +def directory_size(path: str) -> Union[int, float]: + size = 0.0 + for root, _dirs, files in os.walk(path): + for filename in files: + file_path = os.path.join(root, filename) + size += file_size(file_path) + return size + + +def format_directory_size(path: str) -> str: + return format_size(directory_size(path)) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py new file mode 100644 index 0000000..5948570 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/filetypes.py @@ -0,0 +1,27 @@ +"""Filetype information. +""" + +from typing import Tuple + +from pip._internal.utils.misc import splitext + +WHEEL_EXTENSION = ".whl" +BZ2_EXTENSIONS: Tuple[str, ...] = (".tar.bz2", ".tbz") +XZ_EXTENSIONS: Tuple[str, ...] = ( + ".tar.xz", + ".txz", + ".tlz", + ".tar.lz", + ".tar.lzma", +) +ZIP_EXTENSIONS: Tuple[str, ...] = (".zip", WHEEL_EXTENSION) +TAR_EXTENSIONS: Tuple[str, ...] = (".tar.gz", ".tgz", ".tar") +ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS + + +def is_archive_file(name: str) -> bool: + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000..81342af --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,88 @@ +import os +import sys +from typing import Optional, Tuple + + +def glibc_version_string() -> Optional[str]: + "Returns glibc version string, or None if not using glibc." + return glibc_version_string_confstr() or glibc_version_string_ctypes() + + +def glibc_version_string_confstr() -> Optional[str]: + "Primary implementation of glibc_version_string using os.confstr." + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module: + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + if sys.platform == "win32": + return None + try: + gnu_libc_version = os.confstr("CS_GNU_LIBC_VERSION") + if gnu_libc_version is None: + return None + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17": + _, version = gnu_libc_version.split() + except (AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def glibc_version_string_ctypes() -> Optional[str]: + "Fallback implementation of glibc_version_string using ctypes." + + try: + import ctypes + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver() -> Tuple[str, str]: + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000..843cffc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,151 @@ +import hashlib +from typing import TYPE_CHECKING, BinaryIO, Dict, Iterable, List, Optional + +from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError +from pip._internal.utils.misc import read_chunks + +if TYPE_CHECKING: + from hashlib import _Hash + + # NoReturn introduced in 3.6.2; imported only for type checking to maintain + # pip compatibility with older patch versions of Python 3.6 + from typing import NoReturn + + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = "sha256" + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ["sha256", "sha384", "sha512"] + + +class Hashes: + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + + def __init__(self, hashes: Optional[Dict[str, List[str]]] = None) -> None: + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + allowed = {} + if hashes is not None: + for alg, keys in hashes.items(): + # Make sure values are always sorted (to ease equality checks) + allowed[alg] = sorted(keys) + self._allowed = allowed + + def __and__(self, other: "Hashes") -> "Hashes": + if not isinstance(other, Hashes): + return NotImplemented + + # If either of the Hashes object is entirely empty (i.e. no hash + # specified at all), all hashes from the other object are allowed. + if not other: + return self + if not self: + return other + + # Otherwise only hashes that present in both objects are allowed. + new = {} + for alg, values in other._allowed.items(): + if alg not in self._allowed: + continue + new[alg] = [v for v in values if v in self._allowed[alg]] + return Hashes(new) + + @property + def digest_count(self) -> int: + return sum(len(digests) for digests in self._allowed.values()) + + def is_hash_allowed(self, hash_name: str, hex_digest: str) -> bool: + """Return whether the given hex digest is allowed.""" + return hex_digest in self._allowed.get(hash_name, []) + + def check_against_chunks(self, chunks: Iterable[bytes]) -> None: + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in self._allowed.keys(): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError(f"Unknown hash name: {hash_name}") + + for chunk in chunks: + for hash in gots.values(): + hash.update(chunk) + + for hash_name, got in gots.items(): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn": + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file: BinaryIO) -> None: + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path: str) -> None: + with open(path, "rb") as file: + return self.check_against_file(file) + + def has_one_of(self, hashes: Dict[str, str]) -> bool: + """Return whether any of the given hashes are allowed.""" + for hash_name, hex_digest in hashes.items(): + if self.is_hash_allowed(hash_name, hex_digest): + return True + return False + + def __bool__(self) -> bool: + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Hashes): + return NotImplemented + return self._allowed == other._allowed + + def __hash__(self) -> int: + return hash( + ",".join( + sorted( + ":".join((alg, digest)) + for alg, digest_list in self._allowed.items() + for digest in digest_list + ) + ) + ) + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + + def __init__(self) -> None: + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super().__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn": + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000..95982df --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,348 @@ +import contextlib +import errno +import logging +import logging.handlers +import os +import sys +import threading +from dataclasses import dataclass +from io import TextIOWrapper +from logging import Filter +from typing import Any, ClassVar, Generator, List, Optional, TextIO, Type + +from pip._vendor.rich.console import ( + Console, + ConsoleOptions, + ConsoleRenderable, + RenderableType, + RenderResult, + RichCast, +) +from pip._vendor.rich.highlighter import NullHighlighter +from pip._vendor.rich.logging import RichHandler +from pip._vendor.rich.segment import Segment +from pip._vendor.rich.style import Style + +from pip._internal.utils._log import VERBOSE, getLogger +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX +from pip._internal.utils.misc import ensure_dir + +_log_state = threading.local() +subprocess_logger = getLogger("pip.subprocessor") + + +class BrokenStdoutLoggingError(Exception): + """ + Raised if BrokenPipeError occurs for the stdout stream while logging. + """ + + +def _is_broken_pipe_error(exc_class: Type[BaseException], exc: BaseException) -> bool: + if exc_class is BrokenPipeError: + return True + + # On Windows, a broken pipe can show up as EINVAL rather than EPIPE: + # https://bugs.python.org/issue19612 + # https://bugs.python.org/issue30418 + if not WINDOWS: + return False + + return isinstance(exc, OSError) and exc.errno in (errno.EINVAL, errno.EPIPE) + + +@contextlib.contextmanager +def indent_log(num: int = 2) -> Generator[None, None, None]: + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + # For thread-safety + _log_state.indentation = get_indentation() + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation() -> int: + return getattr(_log_state, "indentation", 0) + + +class IndentingFormatter(logging.Formatter): + default_time_format = "%Y-%m-%dT%H:%M:%S" + + def __init__( + self, + *args: Any, + add_timestamp: bool = False, + **kwargs: Any, + ) -> None: + """ + A logging.Formatter that obeys the indent_log() context manager. + + :param add_timestamp: A bool indicating output lines should be prefixed + with their record's timestamp. + """ + self.add_timestamp = add_timestamp + super().__init__(*args, **kwargs) + + def get_message_start(self, formatted: str, levelno: int) -> str: + """ + Return the start of the formatted log message (not counting the + prefix to add to each line). + """ + if levelno < logging.WARNING: + return "" + if formatted.startswith(DEPRECATION_MSG_PREFIX): + # Then the message already has a prefix. We don't want it to + # look like "WARNING: DEPRECATION: ...." + return "" + if levelno < logging.ERROR: + return "WARNING: " + + return "ERROR: " + + def format(self, record: logging.LogRecord) -> str: + """ + Calls the standard formatter, but will indent all of the log message + lines by our current indentation level. + """ + formatted = super().format(record) + message_start = self.get_message_start(formatted, record.levelno) + formatted = message_start + formatted + + prefix = "" + if self.add_timestamp: + prefix = f"{self.formatTime(record)} " + prefix += " " * get_indentation() + formatted = "".join([prefix + line for line in formatted.splitlines(True)]) + return formatted + + +@dataclass +class IndentedRenderable: + renderable: RenderableType + indent: int + + def __rich_console__( + self, console: Console, options: ConsoleOptions + ) -> RenderResult: + segments = console.render(self.renderable, options) + lines = Segment.split_lines(segments) + for line in lines: + yield Segment(" " * self.indent) + yield from line + yield Segment("\n") + + +class RichPipStreamHandler(RichHandler): + KEYWORDS: ClassVar[Optional[List[str]]] = [] + + def __init__(self, stream: Optional[TextIO], no_color: bool) -> None: + super().__init__( + console=Console(file=stream, no_color=no_color, soft_wrap=True), + show_time=False, + show_level=False, + show_path=False, + highlighter=NullHighlighter(), + ) + + # Our custom override on Rich's logger, to make things work as we need them to. + def emit(self, record: logging.LogRecord) -> None: + style: Optional[Style] = None + + # If we are given a diagnostic error to present, present it with indentation. + assert isinstance(record.args, tuple) + if getattr(record, "rich", False): + (rich_renderable,) = record.args + assert isinstance( + rich_renderable, (ConsoleRenderable, RichCast, str) + ), f"{rich_renderable} is not rich-console-renderable" + + renderable: RenderableType = IndentedRenderable( + rich_renderable, indent=get_indentation() + ) + else: + message = self.format(record) + renderable = self.render_message(record, message) + if record.levelno is not None: + if record.levelno >= logging.ERROR: + style = Style(color="red") + elif record.levelno >= logging.WARNING: + style = Style(color="yellow") + + try: + self.console.print(renderable, overflow="ignore", crop=False, style=style) + except Exception: + self.handleError(record) + + def handleError(self, record: logging.LogRecord) -> None: + """Called when logging is unable to log some output.""" + + exc_class, exc = sys.exc_info()[:2] + # If a broken pipe occurred while calling write() or flush() on the + # stdout stream in logging's Handler.emit(), then raise our special + # exception so we can handle it in main() instead of logging the + # broken pipe error and continuing. + if ( + exc_class + and exc + and self.console.file is sys.stdout + and _is_broken_pipe_error(exc_class, exc) + ): + raise BrokenStdoutLoggingError() + + return super().handleError(record) + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + def _open(self) -> TextIOWrapper: + ensure_dir(os.path.dirname(self.baseFilename)) + return super()._open() + + +class MaxLevelFilter(Filter): + def __init__(self, level: int) -> None: + self.level = level + + def filter(self, record: logging.LogRecord) -> bool: + return record.levelno < self.level + + +class ExcludeLoggerFilter(Filter): + + """ + A logging Filter that excludes records from a logger (or its children). + """ + + def filter(self, record: logging.LogRecord) -> bool: + # The base Filter class allows only records from a logger (or its + # children). + return not super().filter(record) + + +def setup_logging(verbosity: int, no_color: bool, user_log_file: Optional[str]) -> int: + """Configures and sets up all of the logging + + Returns the requested logging level, as its integer value. + """ + + # Determine the level to be logging at. + if verbosity >= 2: + level_number = logging.DEBUG + elif verbosity == 1: + level_number = VERBOSE + elif verbosity == -1: + level_number = logging.WARNING + elif verbosity == -2: + level_number = logging.ERROR + elif verbosity <= -3: + level_number = logging.CRITICAL + else: + level_number = logging.INFO + + level = logging.getLevelName(level_number) + + # The "root" logger should match the "console" level *unless* we also need + # to log to a user log file. + include_user_log = user_log_file is not None + if include_user_log: + additional_log_file = user_log_file + root_level = "DEBUG" + else: + additional_log_file = "/dev/null" + root_level = level + + # Disable any logging besides WARNING unless we have DEBUG level logging + # enabled for vendored libraries. + vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + + # Shorthands for clarity + log_streams = { + "stdout": "ext://sys.stdout", + "stderr": "ext://sys.stderr", + } + handler_classes = { + "stream": "pip._internal.utils.logging.RichPipStreamHandler", + "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + } + handlers = ["console", "console_errors", "console_subprocess"] + ( + ["user_log"] if include_user_log else [] + ) + + logging.config.dictConfig( + { + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + "restrict_to_subprocess": { + "()": "logging.Filter", + "name": subprocess_logger.name, + }, + "exclude_subprocess": { + "()": "pip._internal.utils.logging.ExcludeLoggerFilter", + "name": subprocess_logger.name, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + "indent_with_timestamp": { + "()": IndentingFormatter, + "format": "%(message)s", + "add_timestamp": True, + }, + }, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_subprocess", "exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["exclude_subprocess"], + "formatter": "indent", + }, + # A handler responsible for logging to the console messages + # from the "subprocessor" logger. + "console_subprocess": { + "level": level, + "class": handler_classes["stream"], + "stream": log_streams["stderr"], + "no_color": no_color, + "filters": ["restrict_to_subprocess"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "encoding": "utf-8", + "delay": True, + "formatter": "indent_with_timestamp", + }, + }, + "root": { + "level": root_level, + "handlers": handlers, + }, + "loggers": {"pip._vendor": {"level": vendored_log_level}}, + } + ) + + return level_number diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000..1ad3f61 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,783 @@ +import contextlib +import errno +import getpass +import hashlib +import io +import logging +import os +import posixpath +import shutil +import stat +import sys +import sysconfig +import urllib.parse +from functools import partial +from io import StringIO +from itertools import filterfalse, tee, zip_longest +from pathlib import Path +from types import FunctionType, TracebackType +from typing import ( + Any, + BinaryIO, + Callable, + ContextManager, + Dict, + Generator, + Iterable, + Iterator, + List, + Optional, + TextIO, + Tuple, + Type, + TypeVar, + Union, + cast, +) + +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.pyproject_hooks import BuildBackendHookCaller +from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed + +from pip import __version__ +from pip._internal.exceptions import CommandError, ExternallyManagedEnvironment +from pip._internal.locations import get_major_minor_version +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.virtualenv import running_under_virtualenv + +__all__ = [ + "rmtree", + "display_path", + "backup_dir", + "ask", + "splitext", + "format_size", + "is_installable_dir", + "normalize_path", + "renames", + "get_prog", + "captured_stdout", + "ensure_dir", + "remove_auth_from_url", + "check_externally_managed", + "ConfiguredBuildBackendHookCaller", +] + +logger = logging.getLogger(__name__) + +T = TypeVar("T") +ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType] +VersionInfo = Tuple[int, int, int] +NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]] +OnExc = Callable[[FunctionType, Path, BaseException], Any] +OnErr = Callable[[FunctionType, Path, ExcInfo], Any] + + +def get_pip_version() -> str: + pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..") + pip_pkg_dir = os.path.abspath(pip_pkg_dir) + + return f"pip {__version__} from {pip_pkg_dir} (python {get_major_minor_version()})" + + +def normalize_version_info(py_version_info: Tuple[int, ...]) -> Tuple[int, int, int]: + """ + Convert a tuple of ints representing a Python version to one of length + three. + + :param py_version_info: a tuple of ints representing a Python version, + or None to specify no version. The tuple can have any length. + + :return: a tuple of length three if `py_version_info` is non-None. + Otherwise, return `py_version_info` unchanged (i.e. None). + """ + if len(py_version_info) < 3: + py_version_info += (3 - len(py_version_info)) * (0,) + elif len(py_version_info) > 3: + py_version_info = py_version_info[:3] + + return cast("VersionInfo", py_version_info) + + +def ensure_dir(path: str) -> None: + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + # Windows can raise spurious ENOTEMPTY errors. See #6426. + if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY: + raise + + +def get_prog() -> str: + try: + prog = os.path.basename(sys.argv[0]) + if prog in ("__main__.py", "-c"): + return f"{sys.executable} -m pip" + else: + return prog + except (AttributeError, TypeError, IndexError): + pass + return "pip" + + +# Retry every half second for up to 3 seconds +# Tenacity raises RetryError by default, explicitly raise the original exception +@retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5)) +def rmtree( + dir: str, + ignore_errors: bool = False, + onexc: Optional[OnExc] = None, +) -> None: + if ignore_errors: + onexc = _onerror_ignore + if onexc is None: + onexc = _onerror_reraise + handler: OnErr = partial( + # `[func, path, Union[ExcInfo, BaseException]] -> Any` is equivalent to + # `Union[([func, path, ExcInfo] -> Any), ([func, path, BaseException] -> Any)]`. + cast(Union[OnExc, OnErr], rmtree_errorhandler), + onexc=onexc, + ) + if sys.version_info >= (3, 12): + # See https://docs.python.org/3.12/whatsnew/3.12.html#shutil. + shutil.rmtree(dir, onexc=handler) # type: ignore + else: + shutil.rmtree(dir, onerror=handler) # type: ignore + + +def _onerror_ignore(*_args: Any) -> None: + pass + + +def _onerror_reraise(*_args: Any) -> None: + raise + + +def rmtree_errorhandler( + func: FunctionType, + path: Path, + exc_info: Union[ExcInfo, BaseException], + *, + onexc: OnExc = _onerror_reraise, +) -> None: + """ + `rmtree` error handler to 'force' a file remove (i.e. like `rm -f`). + + * If a file is readonly then it's write flag is set and operation is + retried. + + * `onerror` is the original callback from `rmtree(... onerror=onerror)` + that is chained at the end if the "rm -f" still fails. + """ + try: + st_mode = os.stat(path).st_mode + except OSError: + # it's equivalent to os.path.exists + return + + if not st_mode & stat.S_IWRITE: + # convert to read/write + try: + os.chmod(path, st_mode | stat.S_IWRITE) + except OSError: + pass + else: + # use the original function to repeat the operation + try: + func(path) + return + except OSError: + pass + + if not isinstance(exc_info, BaseException): + _, exc_info, _ = exc_info + onexc(func, path, exc_info) + + +def display_path(path: str) -> str: + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if path.startswith(os.getcwd() + os.path.sep): + path = "." + path[len(os.getcwd()) :] + return path + + +def backup_dir(dir: str, ext: str = ".bak") -> str: + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message: str, options: Iterable[str]) -> str: + for action in os.environ.get("PIP_EXISTS_ACTION", "").split(): + if action in options: + return action + return ask(message, options) + + +def _check_no_input(message: str) -> None: + """Raise an error if no input is allowed.""" + if os.environ.get("PIP_NO_INPUT"): + raise Exception( + f"No input was expected ($PIP_NO_INPUT set); question: {message}" + ) + + +def ask(message: str, options: Iterable[str]) -> str: + """Ask the message interactively, with the given possible responses""" + while 1: + _check_no_input(message) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + "Your response ({!r}) was not one of the expected responses: " + "{}".format(response, ", ".join(options)) + ) + else: + return response + + +def ask_input(message: str) -> str: + """Ask for input interactively.""" + _check_no_input(message) + return input(message) + + +def ask_password(message: str) -> str: + """Ask for a password interactively.""" + _check_no_input(message) + return getpass.getpass(message) + + +def strtobool(val: str) -> int: + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = val.lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return 1 + elif val in ("n", "no", "f", "false", "off", "0"): + return 0 + else: + raise ValueError(f"invalid truth value {val!r}") + + +def format_size(bytes: float) -> str: + if bytes > 1000 * 1000: + return f"{bytes / 1000.0 / 1000:.1f} MB" + elif bytes > 10 * 1000: + return f"{int(bytes / 1000)} kB" + elif bytes > 1000: + return f"{bytes / 1000.0:.1f} kB" + else: + return f"{int(bytes)} bytes" + + +def tabulate(rows: Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]]: + """Return a list of formatted rows and a list of column sizes. + + For example:: + + >>> tabulate([['foobar', 2000], [0xdeadbeef]]) + (['foobar 2000', '3735928559'], [10, 4]) + """ + rows = [tuple(map(str, row)) for row in rows] + sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue="")] + table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows] + return table, sizes + + +def is_installable_dir(path: str) -> bool: + """Is path is a directory containing pyproject.toml or setup.py? + + If pyproject.toml exists, this is a PEP 517 project. Otherwise we look for + a legacy setuptools layout by identifying setup.py. We don't check for the + setup.cfg because using it without setup.py is only available for PEP 517 + projects, which are already covered by the pyproject.toml check. + """ + if not os.path.isdir(path): + return False + if os.path.isfile(os.path.join(path, "pyproject.toml")): + return True + if os.path.isfile(os.path.join(path, "setup.py")): + return True + return False + + +def read_chunks( + file: BinaryIO, size: int = io.DEFAULT_BUFFER_SIZE +) -> Generator[bytes, None, None]: + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def normalize_path(path: str, resolve_symlinks: bool = True) -> str: + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = os.path.expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path: str) -> Tuple[str, str]: + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith(".tar"): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old: str, new: str) -> None: + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path: str) -> bool: + """ + Return True if path is within sys.prefix, if we're running in a virtualenv. + + If we're not in a virtualenv, all paths are considered "local." + + Caution: this function assumes the head of path has been normalized + with normalize_path. + """ + if not running_under_virtualenv(): + return True + return path.startswith(normalize_path(sys.prefix)) + + +def write_output(msg: Any, *args: Any) -> None: + logger.info(msg, *args) + + +class StreamWrapper(StringIO): + orig_stream: TextIO + + @classmethod + def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper": + ret = cls() + ret.orig_stream = orig_stream + return ret + + # compileall.compile_dir() needs stdout.encoding to print to stdout + # type ignore is because TextIOBase.encoding is writeable + @property + def encoding(self) -> str: # type: ignore + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name: str) -> Generator[StreamWrapper, None, None]: + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout() -> ContextManager[StreamWrapper]: + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output("stdout") + + +def captured_stderr() -> ContextManager[StreamWrapper]: + """ + See captured_stdout(). + """ + return captured_output("stderr") + + +# Simulates an enum +def enum(*sequential: Any, **named: Any) -> Type[Any]: + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = {value: key for key, value in enums.items()} + enums["reverse_mapping"] = reverse + return type("Enum", (), enums) + + +def build_netloc(host: str, port: Optional[int]) -> str: + """ + Build a netloc from a host-port pair + """ + if port is None: + return host + if ":" in host: + # Only wrap host with square brackets when it is IPv6 + host = f"[{host}]" + return f"{host}:{port}" + + +def build_url_from_netloc(netloc: str, scheme: str = "https") -> str: + """ + Build a full URL from a netloc. + """ + if netloc.count(":") >= 2 and "@" not in netloc and "[" not in netloc: + # It must be a bare IPv6 address, so wrap it with brackets. + netloc = f"[{netloc}]" + return f"{scheme}://{netloc}" + + +def parse_netloc(netloc: str) -> Tuple[Optional[str], Optional[int]]: + """ + Return the host-port pair from a netloc. + """ + url = build_url_from_netloc(netloc) + parsed = urllib.parse.urlparse(url) + return parsed.hostname, parsed.port + + +def split_auth_from_netloc(netloc: str) -> NetlocTuple: + """ + Parse out and remove the auth information from a netloc. + + Returns: (netloc, (username, password)). + """ + if "@" not in netloc: + return netloc, (None, None) + + # Split from the right because that's how urllib.parse.urlsplit() + # behaves if more than one @ is present (which can be checked using + # the password attribute of urlsplit()'s return value). + auth, netloc = netloc.rsplit("@", 1) + pw: Optional[str] = None + if ":" in auth: + # Split from the left because that's how urllib.parse.urlsplit() + # behaves if more than one : is present (which again can be checked + # using the password attribute of the return value) + user, pw = auth.split(":", 1) + else: + user, pw = auth, None + + user = urllib.parse.unquote(user) + if pw is not None: + pw = urllib.parse.unquote(pw) + + return netloc, (user, pw) + + +def redact_netloc(netloc: str) -> str: + """ + Replace the sensitive data in a netloc with "****", if it exists. + + For example: + - "user:pass@example.com" returns "user:****@example.com" + - "accesstoken@example.com" returns "****@example.com" + """ + netloc, (user, password) = split_auth_from_netloc(netloc) + if user is None: + return netloc + if password is None: + user = "****" + password = "" + else: + user = urllib.parse.quote(user) + password = ":****" + return f"{user}{password}@{netloc}" + + +def _transform_url( + url: str, transform_netloc: Callable[[str], Tuple[Any, ...]] +) -> Tuple[str, NetlocTuple]: + """Transform and replace netloc in a url. + + transform_netloc is a function taking the netloc and returning a + tuple. The first element of this tuple is the new netloc. The + entire tuple is returned. + + Returns a tuple containing the transformed url as item 0 and the + original tuple returned by transform_netloc as item 1. + """ + purl = urllib.parse.urlsplit(url) + netloc_tuple = transform_netloc(purl.netloc) + # stripped url + url_pieces = (purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment) + surl = urllib.parse.urlunsplit(url_pieces) + return surl, cast("NetlocTuple", netloc_tuple) + + +def _get_netloc(netloc: str) -> NetlocTuple: + return split_auth_from_netloc(netloc) + + +def _redact_netloc(netloc: str) -> Tuple[str]: + return (redact_netloc(netloc),) + + +def split_auth_netloc_from_url( + url: str, +) -> Tuple[str, str, Tuple[Optional[str], Optional[str]]]: + """ + Parse a url into separate netloc, auth, and url with no auth. + + Returns: (url_without_auth, netloc, (username, password)) + """ + url_without_auth, (netloc, auth) = _transform_url(url, _get_netloc) + return url_without_auth, netloc, auth + + +def remove_auth_from_url(url: str) -> str: + """Return a copy of url with 'username:password@' removed.""" + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + return _transform_url(url, _get_netloc)[0] + + +def redact_auth_from_url(url: str) -> str: + """Replace the password in a given url with ****.""" + return _transform_url(url, _redact_netloc)[0] + + +def redact_auth_from_requirement(req: Requirement) -> str: + """Replace the password in a given requirement url with ****.""" + if not req.url: + return str(req) + return str(req).replace(req.url, redact_auth_from_url(req.url)) + + +class HiddenText: + def __init__(self, secret: str, redacted: str) -> None: + self.secret = secret + self.redacted = redacted + + def __repr__(self) -> str: + return f"" + + def __str__(self) -> str: + return self.redacted + + # This is useful for testing. + def __eq__(self, other: Any) -> bool: + if type(self) != type(other): + return False + + # The string being used for redaction doesn't also have to match, + # just the raw, original string. + return self.secret == other.secret + + +def hide_value(value: str) -> HiddenText: + return HiddenText(value, redacted="****") + + +def hide_url(url: str) -> HiddenText: + redacted = redact_auth_from_url(url) + return HiddenText(url, redacted=redacted) + + +def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None: + """Protection of pip.exe from modification on Windows + + On Windows, any operation modifying pip should be run as: + python -m pip ... + """ + pip_names = [ + "pip", + f"pip{sys.version_info.major}", + f"pip{sys.version_info.major}.{sys.version_info.minor}", + ] + + # See https://github.com/pypa/pip/issues/1299 for more discussion + should_show_use_python_msg = ( + modifying_pip and WINDOWS and os.path.basename(sys.argv[0]) in pip_names + ) + + if should_show_use_python_msg: + new_command = [sys.executable, "-m", "pip"] + sys.argv[1:] + raise CommandError( + "To modify pip, please run the following command:\n{}".format( + " ".join(new_command) + ) + ) + + +def check_externally_managed() -> None: + """Check whether the current environment is externally managed. + + If the ``EXTERNALLY-MANAGED`` config file is found, the current environment + is considered externally managed, and an ExternallyManagedEnvironment is + raised. + """ + if running_under_virtualenv(): + return + marker = os.path.join(sysconfig.get_path("stdlib"), "EXTERNALLY-MANAGED") + if not os.path.isfile(marker): + return + raise ExternallyManagedEnvironment.from_config(marker) + + +def is_console_interactive() -> bool: + """Is this console interactive?""" + return sys.stdin is not None and sys.stdin.isatty() + + +def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]: + """Return (hash, length) for path using hashlib.sha256()""" + + h = hashlib.sha256() + length = 0 + with open(path, "rb") as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + return h, length + + +def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]: + """ + Return paired elements. + + For example: + s -> (s0, s1), (s2, s3), (s4, s5), ... + """ + iterable = iter(iterable) + return zip_longest(iterable, iterable) + + +def partition( + pred: Callable[[T], bool], + iterable: Iterable[T], +) -> Tuple[Iterable[T], Iterable[T]]: + """ + Use a predicate to partition entries into false entries and true entries, + like + + partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + """ + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) + + +class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller): + def __init__( + self, + config_holder: Any, + source_dir: str, + build_backend: str, + backend_path: Optional[str] = None, + runner: Optional[Callable[..., None]] = None, + python_executable: Optional[str] = None, + ): + super().__init__( + source_dir, build_backend, backend_path, runner, python_executable + ) + self.config_holder = config_holder + + def build_wheel( + self, + wheel_directory: str, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, + metadata_directory: Optional[str] = None, + ) -> str: + cs = self.config_holder.config_settings + return super().build_wheel( + wheel_directory, config_settings=cs, metadata_directory=metadata_directory + ) + + def build_sdist( + self, + sdist_directory: str, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, + ) -> str: + cs = self.config_holder.config_settings + return super().build_sdist(sdist_directory, config_settings=cs) + + def build_editable( + self, + wheel_directory: str, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, + metadata_directory: Optional[str] = None, + ) -> str: + cs = self.config_holder.config_settings + return super().build_editable( + wheel_directory, config_settings=cs, metadata_directory=metadata_directory + ) + + def get_requires_for_build_wheel( + self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None + ) -> List[str]: + cs = self.config_holder.config_settings + return super().get_requires_for_build_wheel(config_settings=cs) + + def get_requires_for_build_sdist( + self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None + ) -> List[str]: + cs = self.config_holder.config_settings + return super().get_requires_for_build_sdist(config_settings=cs) + + def get_requires_for_build_editable( + self, config_settings: Optional[Dict[str, Union[str, List[str]]]] = None + ) -> List[str]: + cs = self.config_holder.config_settings + return super().get_requires_for_build_editable(config_settings=cs) + + def prepare_metadata_for_build_wheel( + self, + metadata_directory: str, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, + _allow_fallback: bool = True, + ) -> str: + cs = self.config_holder.config_settings + return super().prepare_metadata_for_build_wheel( + metadata_directory=metadata_directory, + config_settings=cs, + _allow_fallback=_allow_fallback, + ) + + def prepare_metadata_for_build_editable( + self, + metadata_directory: str, + config_settings: Optional[Dict[str, Union[str, List[str]]]] = None, + _allow_fallback: bool = True, + ) -> str: + cs = self.config_holder.config_settings + return super().prepare_metadata_for_build_editable( + metadata_directory=metadata_directory, + config_settings=cs, + _allow_fallback=_allow_fallback, + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/models.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000..b6bb21a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,39 @@ +"""Utilities for defining models +""" + +import operator +from typing import Any, Callable, Type + + +class KeyBasedCompareMixin: + """Provides comparison capabilities that is based on a key""" + + __slots__ = ["_compare_key", "_defining_class"] + + def __init__(self, key: Any, defining_class: Type["KeyBasedCompareMixin"]) -> None: + self._compare_key = key + self._defining_class = defining_class + + def __hash__(self) -> int: + return hash(self._compare_key) + + def __lt__(self, other: Any) -> bool: + return self._compare(other, operator.__lt__) + + def __le__(self, other: Any) -> bool: + return self._compare(other, operator.__le__) + + def __gt__(self, other: Any) -> bool: + return self._compare(other, operator.__gt__) + + def __ge__(self, other: Any) -> bool: + return self._compare(other, operator.__ge__) + + def __eq__(self, other: Any) -> bool: + return self._compare(other, operator.__eq__) + + def _compare(self, other: Any, method: Callable[[Any, Any], bool]) -> bool: + if not isinstance(other, self._defining_class): + return NotImplemented + + return method(self._compare_key, other._compare_key) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000..b9f6af4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,57 @@ +import functools +import logging +import re +from typing import NewType, Optional, Tuple, cast + +from pip._vendor.packaging import specifiers, version +from pip._vendor.packaging.requirements import Requirement + +NormalizedExtra = NewType("NormalizedExtra", str) + +logger = logging.getLogger(__name__) + + +def check_requires_python( + requires_python: Optional[str], version_info: Tuple[int, ...] +) -> bool: + """ + Check if the given Python version matches a "Requires-Python" specifier. + + :param version_info: A 3-tuple of ints representing a Python + major-minor-micro version to check (e.g. `sys.version_info[:3]`). + + :return: `True` if the given Python version satisfies the requirement. + Otherwise, return `False`. + + :raises InvalidSpecifier: If `requires_python` has an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + python_version = version.parse(".".join(map(str, version_info))) + return python_version in requires_python_specifier + + +@functools.lru_cache(maxsize=512) +def get_requirement(req_string: str) -> Requirement: + """Construct a packaging.Requirement object with caching""" + # Parsing requirement strings is expensive, and is also expected to happen + # with a low diversity of different arguments (at least relative the number + # constructed). This method adds a cache to requirement object creation to + # minimize repeated parsing of the same string to construct equivalent + # Requirement objects. + return Requirement(req_string) + + +def safe_extra(extra: str) -> NormalizedExtra: + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + + This function is duplicated from ``pkg_resources``. Note that this is not + the same to either ``canonicalize_name`` or ``_egg_link_name``. + """ + return cast(NormalizedExtra, re.sub("[^A-Za-z0-9.-]+", "_", extra).lower()) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000..96d1b24 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,146 @@ +import sys +import textwrap +from typing import List, Optional, Sequence + +# Shim to wrap setup.py invocation with setuptools +# Note that __file__ is handled via two {!r} *and* %r, to ensure that paths on +# Windows are correctly handled (it should be "C:\\Users" not "C:\Users"). +_SETUPTOOLS_SHIM = textwrap.dedent( + """ + exec(compile(''' + # This is -- a caller that pip uses to run setup.py + # + # - It imports setuptools before invoking setup.py, to enable projects that directly + # import from `distutils.core` to work with newer packaging standards. + # - It provides a clear error message when setuptools is not installed. + # - It sets `sys.argv[0]` to the underlying `setup.py`, when invoking `setup.py` so + # setuptools doesn't think the script is `-c`. This avoids the following warning: + # manifest_maker: standard file '-c' not found". + # - It generates a shim setup.py, for handling setup.cfg-only projects. + import os, sys, tokenize + + try: + import setuptools + except ImportError as error: + print( + "ERROR: Can not execute `setup.py` since setuptools is not available in " + "the build environment.", + file=sys.stderr, + ) + sys.exit(1) + + __file__ = %r + sys.argv[0] = __file__ + + if os.path.exists(__file__): + filename = __file__ + with tokenize.open(__file__) as f: + setup_py_code = f.read() + else: + filename = "" + setup_py_code = "from setuptools import setup; setup()" + + exec(compile(setup_py_code, filename, "exec")) + ''' % ({!r},), "", "exec")) + """ +).rstrip() + + +def make_setuptools_shim_args( + setup_py_path: str, + global_options: Optional[Sequence[str]] = None, + no_user_config: bool = False, + unbuffered_output: bool = False, +) -> List[str]: + """ + Get setuptools command arguments with shim wrapped setup file invocation. + + :param setup_py_path: The path to setup.py to be wrapped. + :param global_options: Additional global options. + :param no_user_config: If True, disables personal user configuration. + :param unbuffered_output: If True, adds the unbuffered switch to the + argument list. + """ + args = [sys.executable] + if unbuffered_output: + args += ["-u"] + args += ["-c", _SETUPTOOLS_SHIM.format(setup_py_path)] + if global_options: + args += global_options + if no_user_config: + args += ["--no-user-cfg"] + return args + + +def make_setuptools_bdist_wheel_args( + setup_py_path: str, + global_options: Sequence[str], + build_options: Sequence[str], + destination_dir: str, +) -> List[str]: + # NOTE: Eventually, we'd want to also -S to the flags here, when we're + # isolating. Currently, it breaks Python in virtualenvs, because it + # relies on site.py to find parts of the standard library outside the + # virtualenv. + args = make_setuptools_shim_args( + setup_py_path, global_options=global_options, unbuffered_output=True + ) + args += ["bdist_wheel", "-d", destination_dir] + args += build_options + return args + + +def make_setuptools_clean_args( + setup_py_path: str, + global_options: Sequence[str], +) -> List[str]: + args = make_setuptools_shim_args( + setup_py_path, global_options=global_options, unbuffered_output=True + ) + args += ["clean", "--all"] + return args + + +def make_setuptools_develop_args( + setup_py_path: str, + *, + global_options: Sequence[str], + no_user_config: bool, + prefix: Optional[str], + home: Optional[str], + use_user_site: bool, +) -> List[str]: + assert not (use_user_site and prefix) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + ) + + args += ["develop", "--no-deps"] + + if prefix: + args += ["--prefix", prefix] + if home is not None: + args += ["--install-dir", home] + + if use_user_site: + args += ["--user", "--prefix="] + + return args + + +def make_setuptools_egg_info_args( + setup_py_path: str, + egg_info_dir: Optional[str], + no_user_config: bool, +) -> List[str]: + args = make_setuptools_shim_args(setup_py_path, no_user_config=no_user_config) + + args += ["egg_info"] + + if egg_info_dir: + args += ["--egg-base", egg_info_dir] + + return args diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py new file mode 100644 index 0000000..79580b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/subprocess.py @@ -0,0 +1,260 @@ +import logging +import os +import shlex +import subprocess +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Iterable, + List, + Mapping, + Optional, + Union, +) + +from pip._vendor.rich.markup import escape + +from pip._internal.cli.spinners import SpinnerInterface, open_spinner +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.utils.logging import VERBOSE, subprocess_logger +from pip._internal.utils.misc import HiddenText + +if TYPE_CHECKING: + # Literal was introduced in Python 3.8. + # + # TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7. + from typing import Literal + +CommandArgs = List[Union[str, HiddenText]] + + +def make_command(*args: Union[str, HiddenText, CommandArgs]) -> CommandArgs: + """ + Create a CommandArgs object. + """ + command_args: CommandArgs = [] + for arg in args: + # Check for list instead of CommandArgs since CommandArgs is + # only known during type-checking. + if isinstance(arg, list): + command_args.extend(arg) + else: + # Otherwise, arg is str or HiddenText. + command_args.append(arg) + + return command_args + + +def format_command_args(args: Union[List[str], CommandArgs]) -> str: + """ + Format command arguments for display. + """ + # For HiddenText arguments, display the redacted form by calling str(). + # Also, we don't apply str() to arguments that aren't HiddenText since + # this can trigger a UnicodeDecodeError in Python 2 if the argument + # has type unicode and includes a non-ascii character. (The type + # checker doesn't ensure the annotations are correct in all cases.) + return " ".join( + shlex.quote(str(arg)) if isinstance(arg, HiddenText) else shlex.quote(arg) + for arg in args + ) + + +def reveal_command_args(args: Union[List[str], CommandArgs]) -> List[str]: + """ + Return the arguments in their raw, unredacted form. + """ + return [arg.secret if isinstance(arg, HiddenText) else arg for arg in args] + + +def call_subprocess( + cmd: Union[List[str], CommandArgs], + show_stdout: bool = False, + cwd: Optional[str] = None, + on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", + extra_ok_returncodes: Optional[Iterable[int]] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + unset_environ: Optional[Iterable[str]] = None, + spinner: Optional[SpinnerInterface] = None, + log_failed_cmd: Optional[bool] = True, + stdout_only: Optional[bool] = False, + *, + command_desc: str, +) -> str: + """ + Args: + show_stdout: if true, use INFO to log the subprocess's stderr and + stdout streams. Otherwise, use DEBUG. Defaults to False. + extra_ok_returncodes: an iterable of integer return codes that are + acceptable, in addition to 0. Defaults to None, which means []. + unset_environ: an iterable of environment variable names to unset + prior to calling subprocess.Popen(). + log_failed_cmd: if false, failed commands are not logged, only raised. + stdout_only: if true, return only stdout, else return both. When true, + logging of both stdout and stderr occurs when the subprocess has + terminated, else logging occurs as subprocess output is produced. + """ + if extra_ok_returncodes is None: + extra_ok_returncodes = [] + if unset_environ is None: + unset_environ = [] + # Most places in pip use show_stdout=False. What this means is-- + # + # - We connect the child's output (combined stderr and stdout) to a + # single pipe, which we read. + # - We log this output to stderr at DEBUG level as it is received. + # - If DEBUG logging isn't enabled (e.g. if --verbose logging wasn't + # requested), then we show a spinner so the user can still see the + # subprocess is in progress. + # - If the subprocess exits with an error, we log the output to stderr + # at ERROR level if it hasn't already been displayed to the console + # (e.g. if --verbose logging wasn't enabled). This way we don't log + # the output to the console twice. + # + # If show_stdout=True, then the above is still done, but with DEBUG + # replaced by INFO. + if show_stdout: + # Then log the subprocess output at INFO level. + log_subprocess: Callable[..., None] = subprocess_logger.info + used_level = logging.INFO + else: + # Then log the subprocess output using VERBOSE. This also ensures + # it will be logged to the log file (aka user_log), if enabled. + log_subprocess = subprocess_logger.verbose + used_level = VERBOSE + + # Whether the subprocess will be visible in the console. + showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level + + # Only use the spinner if we're not showing the subprocess output + # and we have a spinner. + use_spinner = not showing_subprocess and spinner is not None + + log_subprocess("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + for name in unset_environ: + env.pop(name, None) + try: + proc = subprocess.Popen( + # Convert HiddenText objects to the underlying str. + reveal_command_args(cmd), + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT if not stdout_only else subprocess.PIPE, + cwd=cwd, + env=env, + errors="backslashreplace", + ) + except Exception as exc: + if log_failed_cmd: + subprocess_logger.critical( + "Error %s while executing command %s", + exc, + command_desc, + ) + raise + all_output = [] + if not stdout_only: + assert proc.stdout + assert proc.stdin + proc.stdin.close() + # In this mode, stdout and stderr are in the same pipe. + while True: + line: str = proc.stdout.readline() + if not line: + break + line = line.rstrip() + all_output.append(line + "\n") + + # Show the line immediately. + log_subprocess(line) + # Update the spinner. + if use_spinner: + assert spinner + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + output = "".join(all_output) + else: + # In this mode, stdout and stderr are in different pipes. + # We must use communicate() which is the only safe way to read both. + out, err = proc.communicate() + # log line by line to preserve pip log indenting + for out_line in out.splitlines(): + log_subprocess(out_line) + all_output.append(out) + for err_line in err.splitlines(): + log_subprocess(err_line) + all_output.append(err) + output = out + + proc_had_error = proc.returncode and proc.returncode not in extra_ok_returncodes + if use_spinner: + assert spinner + if proc_had_error: + spinner.finish("error") + else: + spinner.finish("done") + if proc_had_error: + if on_returncode == "raise": + error = InstallationSubprocessError( + command_description=command_desc, + exit_code=proc.returncode, + output_lines=all_output if not showing_subprocess else None, + ) + if log_failed_cmd: + subprocess_logger.error("%s", error, extra={"rich": True}) + subprocess_logger.verbose( + "[bold magenta]full command[/]: [blue]%s[/]", + escape(format_command_args(cmd)), + extra={"markup": True}, + ) + subprocess_logger.verbose( + "[bold magenta]cwd[/]: %s", + escape(cwd or "[inherit]"), + extra={"markup": True}, + ) + + raise error + elif on_returncode == "warn": + subprocess_logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, + proc.returncode, + cwd, + ) + elif on_returncode == "ignore": + pass + else: + raise ValueError(f"Invalid value: on_returncode={on_returncode!r}") + return output + + +def runner_with_spinner_message(message: str) -> Callable[..., None]: + """Provide a subprocess_runner that shows a spinner message. + + Intended for use with for BuildBackendHookCaller. Thus, the runner has + an API that matches what's expected by BuildBackendHookCaller.subprocess_runner. + """ + + def runner( + cmd: List[str], + cwd: Optional[str] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + ) -> None: + with open_spinner(message) as spinner: + call_subprocess( + cmd, + command_desc=message, + cwd=cwd, + extra_environ=extra_environ, + spinner=spinner, + ) + + return runner diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000..4eec5f3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,296 @@ +import errno +import itertools +import logging +import os.path +import tempfile +import traceback +from contextlib import ExitStack, contextmanager +from pathlib import Path +from typing import ( + Any, + Callable, + Dict, + Generator, + List, + Optional, + TypeVar, + Union, +) + +from pip._internal.utils.misc import enum, rmtree + +logger = logging.getLogger(__name__) + +_T = TypeVar("_T", bound="TempDirectory") + + +# Kinds of temporary directories. Only needed for ones that are +# globally-managed. +tempdir_kinds = enum( + BUILD_ENV="build-env", + EPHEM_WHEEL_CACHE="ephem-wheel-cache", + REQ_BUILD="req-build", +) + + +_tempdir_manager: Optional[ExitStack] = None + + +@contextmanager +def global_tempdir_manager() -> Generator[None, None, None]: + global _tempdir_manager + with ExitStack() as stack: + old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack + try: + yield + finally: + _tempdir_manager = old_tempdir_manager + + +class TempDirectoryTypeRegistry: + """Manages temp directory behavior""" + + def __init__(self) -> None: + self._should_delete: Dict[str, bool] = {} + + def set_delete(self, kind: str, value: bool) -> None: + """Indicate whether a TempDirectory of the given kind should be + auto-deleted. + """ + self._should_delete[kind] = value + + def get_delete(self, kind: str) -> bool: + """Get configured auto-delete flag for a given TempDirectory type, + default True. + """ + return self._should_delete.get(kind, True) + + +_tempdir_registry: Optional[TempDirectoryTypeRegistry] = None + + +@contextmanager +def tempdir_registry() -> Generator[TempDirectoryTypeRegistry, None, None]: + """Provides a scoped global tempdir registry that can be used to dictate + whether directories should be deleted. + """ + global _tempdir_registry + old_tempdir_registry = _tempdir_registry + _tempdir_registry = TempDirectoryTypeRegistry() + try: + yield _tempdir_registry + finally: + _tempdir_registry = old_tempdir_registry + + +class _Default: + pass + + +_default = _Default() + + +class TempDirectory: + """Helper class that owns and cleans up a temporary directory. + + This class can be used as a context manager or as an OO representation of a + temporary directory. + + Attributes: + path + Location to the created temporary directory + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + cleanup() + Deletes the temporary directory + + When used as a context manager, if the delete attribute is True, on + exiting the context the temporary directory is deleted. + """ + + def __init__( + self, + path: Optional[str] = None, + delete: Union[bool, None, _Default] = _default, + kind: str = "temp", + globally_managed: bool = False, + ignore_cleanup_errors: bool = True, + ): + super().__init__() + + if delete is _default: + if path is not None: + # If we were given an explicit directory, resolve delete option + # now. + delete = False + else: + # Otherwise, we wait until cleanup and see what + # tempdir_registry says. + delete = None + + # The only time we specify path is in for editables where it + # is the value of the --src option. + if path is None: + path = self._create(kind) + + self._path = path + self._deleted = False + self.delete = delete + self.kind = kind + self.ignore_cleanup_errors = ignore_cleanup_errors + + if globally_managed: + assert _tempdir_manager is not None + _tempdir_manager.enter_context(self) + + @property + def path(self) -> str: + assert not self._deleted, f"Attempted to access deleted path: {self._path}" + return self._path + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.path!r}>" + + def __enter__(self: _T) -> _T: + return self + + def __exit__(self, exc: Any, value: Any, tb: Any) -> None: + if self.delete is not None: + delete = self.delete + elif _tempdir_registry: + delete = _tempdir_registry.get_delete(self.kind) + else: + delete = True + + if delete: + self.cleanup() + + def _create(self, kind: str) -> str: + """Create a temporary directory and store its path in self.path""" + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-")) + logger.debug("Created temporary directory: %s", path) + return path + + def cleanup(self) -> None: + """Remove the temporary directory created and reset state""" + self._deleted = True + if not os.path.exists(self._path): + return + + errors: List[BaseException] = [] + + def onerror( + func: Callable[..., Any], + path: Path, + exc_val: BaseException, + ) -> None: + """Log a warning for a `rmtree` error and continue""" + formatted_exc = "\n".join( + traceback.format_exception_only(type(exc_val), exc_val) + ) + formatted_exc = formatted_exc.rstrip() # remove trailing new line + if func in (os.unlink, os.remove, os.rmdir): + logger.debug( + "Failed to remove a temporary file '%s' due to %s.\n", + path, + formatted_exc, + ) + else: + logger.debug("%s failed with %s.", func.__qualname__, formatted_exc) + errors.append(exc_val) + + if self.ignore_cleanup_errors: + try: + # first try with tenacity; retrying to handle ephemeral errors + rmtree(self._path, ignore_errors=False) + except OSError: + # last pass ignore/log all errors + rmtree(self._path, onexc=onerror) + if errors: + logger.warning( + "Failed to remove contents in a temporary directory '%s'.\n" + "You can safely remove it manually.", + self._path, + ) + else: + rmtree(self._path) + + +class AdjacentTempDirectory(TempDirectory): + """Helper class that creates a temporary directory adjacent to a real one. + + Attributes: + original + The original directory to create a temp directory for. + path + After calling create() or entering, contains the full + path to the temporary directory. + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + """ + + # The characters that may be used to name the temp directory + # We always prepend a ~ and then rotate through these until + # a usable name is found. + # pkg_resources raises a different error for .dist-info folder + # with leading '-' and invalid metadata + LEADING_CHARS = "-~.=%0123456789" + + def __init__(self, original: str, delete: Optional[bool] = None) -> None: + self.original = original.rstrip("/\\") + super().__init__(delete=delete) + + @classmethod + def _generate_names(cls, name: str) -> Generator[str, None, None]: + """Generates a series of temporary names. + + The algorithm replaces the leading characters in the name + with ones that are valid filesystem characters, but are not + valid package names (for both Python and pip definitions of + package). + """ + for i in range(1, len(name)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i - 1 + ): + new_name = "~" + "".join(candidate) + name[i:] + if new_name != name: + yield new_name + + # If we make it this far, we will have to make a longer name + for i in range(len(cls.LEADING_CHARS)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i + ): + new_name = "~" + "".join(candidate) + name + if new_name != name: + yield new_name + + def _create(self, kind: str) -> str: + root, name = os.path.split(self.original) + for candidate in self._generate_names(name): + path = os.path.join(root, candidate) + try: + os.mkdir(path) + except OSError as ex: + # Continue if the name exists already + if ex.errno != errno.EEXIST: + raise + else: + path = os.path.realpath(path) + break + else: + # Final fallback on the default behavior. + path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-")) + + logger.debug("Created temporary directory: %s", path) + return path diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py new file mode 100644 index 0000000..78b5c13 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/unpacking.py @@ -0,0 +1,257 @@ +"""Utilities related archives. +""" + +import logging +import os +import shutil +import stat +import tarfile +import zipfile +from typing import Iterable, List, Optional +from zipfile import ZipInfo + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.filetypes import ( + BZ2_EXTENSIONS, + TAR_EXTENSIONS, + XZ_EXTENSIONS, + ZIP_EXTENSIONS, +) +from pip._internal.utils.misc import ensure_dir + +logger = logging.getLogger(__name__) + + +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS + +try: + import bz2 # noqa + + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug("bz2 module is not available") + +try: + # Only for Python 3.3+ + import lzma # noqa + + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug("lzma module is not available") + + +def current_umask() -> int: + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def split_leading_dir(path: str) -> List[str]: + path = path.lstrip("/").lstrip("\\") + if "/" in path and ( + ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path + ): + return path.split("/", 1) + elif "\\" in path: + return path.split("\\", 1) + else: + return [path, ""] + + +def has_leading_dir(paths: Iterable[str]) -> bool: + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def is_within_directory(directory: str, target: str) -> bool: + """ + Return true if the absolute path of target is within the directory + """ + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + return prefix == abs_directory + + +def set_extracted_file_to_default_mode_plus_executable(path: str) -> None: + """ + Make file present at path have execute for user/group/world + (chmod +x) is no-op on windows per python docs + """ + os.chmod(path, (0o777 & ~current_umask() | 0o111)) + + +def zip_item_is_executable(info: ZipInfo) -> bool: + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + return bool(mode and stat.S_ISREG(mode) and mode & 0o111) + + +def unzip_file(filename: str, location: str, flatten: bool = True) -> None: + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, "rb") + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if not is_within_directory(location, fn): + message = ( + "The zip file ({}) has a file ({}) trying to install " + "outside target directory ({})" + ) + raise InstallationError(message.format(filename, fn, location)) + if fn.endswith("/") or fn.endswith("\\"): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + # Don't use read() to avoid allocating an arbitrarily large + # chunk of memory for the file's content + fp = zip.open(name) + try: + with open(fn, "wb") as destfp: + shutil.copyfileobj(fp, destfp) + finally: + fp.close() + if zip_item_is_executable(info): + set_extracted_file_to_default_mode_plus_executable(fn) + finally: + zipfp.close() + + +def untar_file(filename: str, location: str) -> None: + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith(".gz") or filename.lower().endswith(".tgz"): + mode = "r:gz" + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = "r:bz2" + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = "r:xz" + elif filename.lower().endswith(".tar"): + mode = "r" + else: + logger.warning( + "Cannot determine compression type for file %s", + filename, + ) + mode = "r:*" + tar = tarfile.open(filename, mode, encoding="utf-8") + try: + leading = has_leading_dir([member.name for member in tar.getmembers()]) + for member in tar.getmembers(): + fn = member.name + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if not is_within_directory(location, path): + message = ( + "The tar file ({}) has a file ({}) trying to install " + "outside target directory ({})" + ) + raise InstallationError(message.format(filename, path, location)) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + tar._extract_member(member, path) + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + "In the tar file %s the member %s is invalid: %s", + filename, + member.name, + exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + "In the tar file %s the member %s is invalid: %s", + filename, + member.name, + exc, + ) + continue + ensure_dir(os.path.dirname(path)) + assert fp is not None + with open(path, "wb") as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + tar.utime(member, path) + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + set_extracted_file_to_default_mode_plus_executable(path) + finally: + tar.close() + + +def unpack_file( + filename: str, + location: str, + content_type: Optional[str] = None, +) -> None: + filename = os.path.realpath(filename) + if ( + content_type == "application/zip" + or filename.lower().endswith(ZIP_EXTENSIONS) + or zipfile.is_zipfile(filename) + ): + unzip_file(filename, location, flatten=not filename.endswith(".whl")) + elif ( + content_type == "application/x-gzip" + or tarfile.is_tarfile(filename) + or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS) + ): + untar_file(filename, location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + "Cannot unpack file %s (downloaded from %s, content-type: %s); " + "cannot detect archive format", + filename, + location, + content_type, + ) + raise InstallationError(f"Cannot determine archive format of {location}") diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py new file mode 100644 index 0000000..6ba2e04 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/urls.py @@ -0,0 +1,62 @@ +import os +import string +import urllib.parse +import urllib.request +from typing import Optional + +from .compat import WINDOWS + + +def get_url_scheme(url: str) -> Optional[str]: + if ":" not in url: + return None + return url.split(":", 1)[0].lower() + + +def path_to_url(path: str) -> str: + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib.parse.urljoin("file:", urllib.request.pathname2url(path)) + return url + + +def url_to_path(url: str) -> str: + """ + Convert a file: URL to a path. + """ + assert url.startswith( + "file:" + ), f"You can only turn file: urls into filenames (not {url!r})" + + _, netloc, path, _, _ = urllib.parse.urlsplit(url) + + if not netloc or netloc == "localhost": + # According to RFC 8089, same as empty authority. + netloc = "" + elif WINDOWS: + # If we have a UNC path, prepend UNC share notation. + netloc = "\\\\" + netloc + else: + raise ValueError( + f"non-local file URIs are not supported on this platform: {url!r}" + ) + + path = urllib.request.url2pathname(netloc + path) + + # On Windows, urlsplit parses the path as something like "/C:/Users/foo". + # This creates issues for path-related functions like io.open(), so we try + # to detect and strip the leading slash. + if ( + WINDOWS + and not netloc # Not UNC. + and len(path) >= 3 + and path[0] == "/" # Leading slash to strip. + and path[1] in string.ascii_letters # Drive letter. + and path[2:4] in (":", ":/") # Colon + end of string, or colon + absolute path. + ): + path = path[1:] + + return path diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py new file mode 100644 index 0000000..882e36f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/virtualenv.py @@ -0,0 +1,104 @@ +import logging +import os +import re +import site +import sys +from typing import List, Optional + +logger = logging.getLogger(__name__) +_INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( + r"include-system-site-packages\s*=\s*(?Ptrue|false)" +) + + +def _running_under_venv() -> bool: + """Checks if sys.base_prefix and sys.prefix match. + + This handles PEP 405 compliant virtual environments. + """ + return sys.prefix != getattr(sys, "base_prefix", sys.prefix) + + +def _running_under_legacy_virtualenv() -> bool: + """Checks if sys.real_prefix is set. + + This handles virtual environments created with pypa's virtualenv. + """ + # pypa/virtualenv case + return hasattr(sys, "real_prefix") + + +def running_under_virtualenv() -> bool: + """True if we're running inside a virtual environment, False otherwise.""" + return _running_under_venv() or _running_under_legacy_virtualenv() + + +def _get_pyvenv_cfg_lines() -> Optional[List[str]]: + """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines + + Returns None, if it could not read/access the file. + """ + pyvenv_cfg_file = os.path.join(sys.prefix, "pyvenv.cfg") + try: + # Although PEP 405 does not specify, the built-in venv module always + # writes with UTF-8. (pypa/pip#8717) + with open(pyvenv_cfg_file, encoding="utf-8") as f: + return f.read().splitlines() # avoids trailing newlines + except OSError: + return None + + +def _no_global_under_venv() -> bool: + """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion + + PEP 405 specifies that when system site-packages are not supposed to be + visible from a virtual environment, `pyvenv.cfg` must contain the following + line: + + include-system-site-packages = false + + Additionally, log a warning if accessing the file fails. + """ + cfg_lines = _get_pyvenv_cfg_lines() + if cfg_lines is None: + # We're not in a "sane" venv, so assume there is no system + # site-packages access (since that's PEP 405's default state). + logger.warning( + "Could not access 'pyvenv.cfg' despite a virtual environment " + "being active. Assuming global site-packages is not accessible " + "in this environment." + ) + return True + + for line in cfg_lines: + match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line) + if match is not None and match.group("value") == "false": + return True + return False + + +def _no_global_under_legacy_virtualenv() -> bool: + """Check if "no-global-site-packages.txt" exists beside site.py + + This mirrors logic in pypa/virtualenv for determining whether system + site-packages are visible in the virtual environment. + """ + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_site_packages_file = os.path.join( + site_mod_dir, + "no-global-site-packages.txt", + ) + return os.path.exists(no_global_site_packages_file) + + +def virtualenv_no_global() -> bool: + """Returns a boolean, whether running in venv with no system site-packages.""" + # PEP 405 compliance needs to be checked first since virtualenv >=20 would + # return True for both checks, but is only able to use the PEP 405 config. + if _running_under_venv(): + return _no_global_under_venv() + + if _running_under_legacy_virtualenv(): + return _no_global_under_legacy_virtualenv() + + return False diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py b/.venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py new file mode 100644 index 0000000..3551f8f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/utils/wheel.py @@ -0,0 +1,134 @@ +"""Support functions for working with wheel files. +""" + +import logging +from email.message import Message +from email.parser import Parser +from typing import Tuple +from zipfile import BadZipFile, ZipFile + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import UnsupportedWheel + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +def parse_wheel(wheel_zip: ZipFile, name: str) -> Tuple[str, Message]: + """Extract information from the provided wheel, ensuring it meets basic + standards. + + Returns the name of the .dist-info directory and the parsed WHEEL metadata. + """ + try: + info_dir = wheel_dist_info_dir(wheel_zip, name) + metadata = wheel_metadata(wheel_zip, info_dir) + version = wheel_version(metadata) + except UnsupportedWheel as e: + raise UnsupportedWheel(f"{name} has an invalid wheel, {str(e)}") + + check_compatibility(version, name) + + return info_dir, metadata + + +def wheel_dist_info_dir(source: ZipFile, name: str) -> str: + """Returns the name of the contained .dist-info directory. + + Raises AssertionError or UnsupportedWheel if not found, >1 found, or + it doesn't match the provided name. + """ + # Zip file path separators must be / + subdirs = {p.split("/", 1)[0] for p in source.namelist()} + + info_dirs = [s for s in subdirs if s.endswith(".dist-info")] + + if not info_dirs: + raise UnsupportedWheel(".dist-info directory not found") + + if len(info_dirs) > 1: + raise UnsupportedWheel( + "multiple .dist-info directories found: {}".format(", ".join(info_dirs)) + ) + + info_dir = info_dirs[0] + + info_dir_name = canonicalize_name(info_dir) + canonical_name = canonicalize_name(name) + if not info_dir_name.startswith(canonical_name): + raise UnsupportedWheel( + f".dist-info directory {info_dir!r} does not start with {canonical_name!r}" + ) + + return info_dir + + +def read_wheel_metadata_file(source: ZipFile, path: str) -> bytes: + try: + return source.read(path) + # BadZipFile for general corruption, KeyError for missing entry, + # and RuntimeError for password-protected files + except (BadZipFile, KeyError, RuntimeError) as e: + raise UnsupportedWheel(f"could not read {path!r} file: {e!r}") + + +def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message: + """Return the WHEEL metadata of an extracted wheel, if possible. + Otherwise, raise UnsupportedWheel. + """ + path = f"{dist_info_dir}/WHEEL" + # Zip file path separators must be / + wheel_contents = read_wheel_metadata_file(source, path) + + try: + wheel_text = wheel_contents.decode() + except UnicodeDecodeError as e: + raise UnsupportedWheel(f"error decoding {path!r}: {e!r}") + + # FeedParser (used by Parser) does not raise any exceptions. The returned + # message may have .defects populated, but for backwards-compatibility we + # currently ignore them. + return Parser().parsestr(wheel_text) + + +def wheel_version(wheel_data: Message) -> Tuple[int, ...]: + """Given WHEEL metadata, return the parsed Wheel-Version. + Otherwise, raise UnsupportedWheel. + """ + version_text = wheel_data["Wheel-Version"] + if version_text is None: + raise UnsupportedWheel("WHEEL is missing Wheel-Version") + + version = version_text.strip() + + try: + return tuple(map(int, version.split("."))) + except ValueError: + raise UnsupportedWheel(f"invalid Wheel-Version: {version!r}") + + +def check_compatibility(version: Tuple[int, ...], name: str) -> None: + """Raises errors or warns if called with an incompatible Wheel-Version. + + pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "{}'s Wheel-Version ({}) is not compatible with this version " + "of pip".format(name, ".".join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + "Installing from a newer Wheel-Version (%s)", + ".".join(map(str, version)), + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000..b6beddb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,15 @@ +# Expose a limited set of classes and functions so callers outside of +# the vcs package don't need to import deeper than `pip._internal.vcs`. +# (The test directory may still need to import from a vcs sub-package.) +# Import all vcs modules to register each VCS in the VcsSupport object. +import pip._internal.vcs.bazaar +import pip._internal.vcs.git +import pip._internal.vcs.mercurial +import pip._internal.vcs.subversion # noqa: F401 +from pip._internal.vcs.versioncontrol import ( # noqa: F401 + RemoteNotFoundError, + RemoteNotValidError, + is_url, + make_vcs_requirement_url, + vcs, +) 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 new file mode 100644 index 0000000000000000000000000000000000000000..67f6250767538b1642c1c3eca848a5654f75d690 GIT binary patch literal 552 zcmZXQ&q@O^5XO^TyR8)w5uu=h=&1)cs8bTtdne^zKGA@tLPhS zFM=m;LP5ckv!#mAf&4z^`$Cw>%lv$kV7)#E@+u(Yt!jP?`*Dp9P##H_bg3c<9cwVr zaH2C~4OC4>Nu=vZT{n`3o=s+TGig#XpzZLzdhEPiE0->XwqBfDe`*WUJpr)r&%EYJ zPRvNUv;bAMG`|yRKXWOF`$B@y!uW9#qS>A+Te-~RR2nb9a22B&clZP6P==V4%hPdB z`f_F3)&jBuBv<9?bm)psUx1U=l`t*KHnKeCoe6)4woa~uuVHWp==`$reEFbtAYj)%99pjq|~GV^7;|p~OBc;s_LvTSI(=l+sa?(Dfl%9g>Y9+58G3 L8hjE2EZ~hFW&Ett literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e4600b90a3a69fb69b44d2d21874fd2391af9f26 GIT binary patch literal 5044 zcmb^#OKcm*b@t2UPozXrqAkUue~Fk_qEb6eVkG`aapE?S6Wa(1wh2RVR}|$XmziDK z7DFLwU?6mm!Z!M`g0!f5Q{W!ln|rF!lhIy8$bpEdgBoaoA~)7j(IAJm@9lCa*;b1d z=mMPg=FOWoGw(aY-v@&}0^hyg@rB<63HcLt+)rvFYrh5L8c~VL=_HMi*SR8}=8HmF zD2i#3LzoRO?z!z(tSmL+Hd2s9w-LW!D3gss~Ad$is5v)*q!d? z2v0OJv&UV018R}$oYPB3dGaz*Jr{|pXyNNZn;L_e-81_(X1JIZb7yj7mP>lS#DR&V zz{FPzrp1(#WvgJ6vO1H_RLZ)R3fH7N6)(#|>SnXKzg zjhe9Xm{GE*p)=uJ&dj-+Mrhjv;949`{vqgIBWX@0XUEjlGQ>(r-m;EI%~&(zuV?lU{^hxcGdg9NMzGUAB^CRqpo~d zEj}fyu}wVN>f{2K-hP@SdzpMJTg_%E6DF&)Dvm(Q<))LKssi1orY);n^}3i@ z^&o1R)6S?l1*0|z5gEtyvNJSQ&^0Dd?HrS-W>sh@*LFf%vplruersqc2o0`A@;q#b zpQ!S~^fQv=Xaq(~oRj#voSHU@TFT6t;5w<-sWGGFEHib=m^7?H&P=^#l=8;-vEiep zS;&`)TFFX{oYP9@QhH%BRi3q`jne*+r@xUh3zjxq&gRZ$^O~6|7s{zjp=4}GmL!A4KwZ>Q*t;5_^ZL;VZD%_w$5wVCOymt z8K#|yn5-N5yhfQ=fILG{GZaW>7IbE^h zl%CDQ9*U|h#YCBj_DB1W-8-dOx#>(vJ6{dA{is91lx<#uW}dWU(zk21cVDA--%9UD z-G5iAls|u}13HN^1O8?)HH-fsgv?3M}gl zt}5|{5?_dauI!?il5%Y)0hAClqV1T600GA&TREnM9QKE+XIX)B9sUpJ`Ugz?Z_ed9tIDeaoJ@2>X-=$(TLz)z&Y+1+`oXZ`-2yx( zZLQCm1S=@69yzjUz8B^TGo8?e&9^@{^!``bcIeT^LCWznp9#!wP8;VlrlmqiLC1y} zqomK$r-7yFKW#w`&+0}=OE7V=P*Ndnze`79jLDU-?MmpY5d0SmA-G*Uug!ytAT+=U|``p zO9z$%19t+We~t_;DocBA_*X~A8lz(?qc5#Qj@Olrhb_bxE!(yBIj+^o$FL_120LWl z0p!QrHDUpDzDr__`~Y(5Mn?V~aL#da{2VydmTuI^-BeN`t5!kK99P2(FH8wgIE9I- z3}yLXDpeJ+48tW&GAm`sbx|x>P=TCER)Q1-_bmDx2!KnMkkd^kwPCqs-ioPFxUZQ| zqB5DB4oD_rVr@Y?%sbJCTCetRs)i2>KLHHpi_m;EPe93odp^lGb{xF9e`UwvhSFab z>Zh+HoBpoLhb|pj7`o%%-Ry}k?7zCNKGqENTp3%~wjA2E+)hGF(T!XdZ<)Exy6vQ+ zA*cfXE^JjU&pi^Re2t&!q$${9`*Xu)oV=xiW(5eYEh?(ZH7BSNaJ~kd;?`kU6Wy>k zxsPMO)2W;8ieUW?k1AurwbOuqW8B4R8!04Ybt>ovHOF%Vf2T=J+Q3Ef@svocE?3G2 zT+%bq4sg03s7byWb*pB=m`XVJzJrNssGOy6h3XLKAaW~qI71|t^@61rN}34{X3;{~ zb|t`(&Z3!unL(4GyOr^VW6)WOho^WlhLTQT*tTmY zuby0q?!8Yu-b8)88R@%r@an-Iz5r{b@#)Vadz<}(KR@`%!Nu&S zFD`XA`k%To-t61H+LvndrEZ9K`kuKv_~g=omBGR@!60lN2`DLIKtSKB-I$4v2N< z+7fWI?+jHV59r<@z60zg7GKbOq!)G83`XlGn(@b)@quPI-tv;@?lmIW*NgxO$5%tU z8lhb)p*@SI8ljh)0Z+xpXu--9)Qt9SC97ArwNLoy&ZO%5z32uFX7Z0p1x!jd{_}?McjILI4&a zk^)d4;U!bl;HjcA0pg0{(P+L=i)XHnD_#dj6O zOijOq%}HpWP#cC$Pazs?XZM_@>zNF^whjZ;er+9bl6)9vVbg;HZ$g^=>LO|S!}H_K zuIT*BVA=T-EnW%9^DnoeMDA(voYcQywg`YlYiaBkv-c5Q6Wp_>4&xW%*sZ_YUI_L()C-je;&Ffsu^9stM!0674( zwiCI##Y@ueMc5gDrOJ)dH-~QR|Ml?g`0_JvG*YM5k&L@^_*z{y7Lax==2`^6Qtm#2 zHV0s>0h_}kB^Sq91i;c5BoZmv<^ZhKV{`CQ3}pbg5x=?nSC84~tpoRwVl%JvfZ2Xz zD>~W7Jor3;hYypj*)l-K32b|!Wmz1atRtN#>qx~a=;lbVVCEbjLHA@kw&F1>lVxh; zG}Cn6J6j6EbZn<;mUvJGJdNQwZHHOT`Nzd^FU;t+V^XY26iW^BLdJO2eq_Zf|(EFFd#6-7_z*%ira=SpDsFg0NNN2^X9fJ`8+6P=E7t I617+T7b@hjwg3PC literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6cdde2d4c4d258f690861538984de342e647d651 GIT binary patch literal 19013 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..8fc7393c31d10a0c4f5997963b75e597d6ea02c2 GIT binary patch literal 29031 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 A9RL6T literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000..20a17ed --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,112 @@ +import logging +from typing import List, Optional, Tuple + +from pip._internal.utils.misc import HiddenText, display_path +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import ( + AuthInfo, + RemoteNotFoundError, + RevOptions, + VersionControl, + vcs, +) + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = "bzr" + dirname = ".bzr" + repo_name = "branch" + schemes = ( + "bzr+http", + "bzr+https", + "bzr+ssh", + "bzr+sftp", + "bzr+ftp", + "bzr+lp", + "bzr+file", + ) + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + return ["-r", rev] + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + rev_display = rev_options.to_display() + logger.info( + "Checking out %s%s to %s", + url, + rev_display, + display_path(dest), + ) + if verbosity <= 0: + flag = "--quiet" + elif verbosity == 1: + flag = "" + else: + flag = f"-{'v'*verbosity}" + cmd_args = make_command( + "checkout", "--lightweight", flag, rev_options.to_args(), url, dest + ) + self.run_command(cmd_args) + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + self.run_command(make_command("switch", url), cwd=dest) + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + output = self.run_command( + make_command("info"), show_stdout=False, stdout_only=True, cwd=dest + ) + if output.startswith("Standalone "): + # Older versions of pip used to create standalone branches. + # Convert the standalone branch to a checkout by calling "bzr bind". + cmd_args = make_command("bind", "-q", url) + self.run_command(cmd_args, cwd=dest) + + cmd_args = make_command("update", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// re-add it + url, rev, user_pass = super().get_url_rev_and_auth(url) + if url.startswith("ssh://"): + url = "bzr+" + url + return url, rev, user_pass + + @classmethod + def get_remote_url(cls, location: str) -> str: + urls = cls.run_command( + ["info"], show_stdout=False, stdout_only=True, cwd=location + ) + for line in urls.splitlines(): + line = line.strip() + for x in ("checkout of branch: ", "parent branch: "): + if line.startswith(x): + repo = line.split(x)[1] + if cls._is_local_repository(repo): + return path_to_url(repo) + return repo + raise RemoteNotFoundError + + @classmethod + def get_revision(cls, location: str) -> str: + revision = cls.run_command( + ["revno"], + show_stdout=False, + stdout_only=True, + cwd=location, + ) + return revision.splitlines()[-1] + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000..8c242cf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,526 @@ +import logging +import os.path +import pathlib +import re +import urllib.parse +import urllib.request +from typing import List, Optional, Tuple + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import HiddenText, display_path, hide_url +from pip._internal.utils.subprocess import make_command +from pip._internal.vcs.versioncontrol import ( + AuthInfo, + RemoteNotFoundError, + RemoteNotValidError, + RevOptions, + VersionControl, + find_path_to_project_root_from_repo_root, + vcs, +) + +urlsplit = urllib.parse.urlsplit +urlunsplit = urllib.parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +GIT_VERSION_REGEX = re.compile( + r"^git version " # Prefix. + r"(\d+)" # Major. + r"\.(\d+)" # Dot, minor. + r"(?:\.(\d+))?" # Optional dot, patch. + r".*$" # Suffix, including any pre- and post-release segments we don't care about. +) + +HASH_REGEX = re.compile("^[a-fA-F0-9]{40}$") + +# SCP (Secure copy protocol) shorthand. e.g. 'git@example.com:foo/bar.git' +SCP_REGEX = re.compile( + r"""^ + # Optional user, e.g. 'git@' + (\w+@)? + # Server, e.g. 'github.com'. + ([^/:]+): + # The server-side path. e.g. 'user/project.git'. Must start with an + # alphanumeric character so as not to be confusable with a Windows paths + # like 'C:/foo/bar' or 'C:\foo\bar'. + (\w[^:]*) + $""", + re.VERBOSE, +) + + +def looks_like_hash(sha: str) -> bool: + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = "git" + dirname = ".git" + repo_name = "clone" + schemes = ( + "git+http", + "git+https", + "git+ssh", + "git+git", + "git+file", + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ("GIT_DIR", "GIT_WORK_TREE") + default_arg_rev = "HEAD" + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + return [rev] + + def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: + _, rev_options = self.get_url_rev_options(hide_url(url)) + if not rev_options.rev: + return False + if not self.is_commit_id_equal(dest, rev_options.rev): + # the current commit is different from rev, + # which means rev was something else than a commit hash + return False + # return False in the rare case rev is both a commit hash + # and a tag or a branch; we don't want to cache in that case + # because that branch/tag could point to something else in the future + is_tag_or_branch = bool(self.get_revision_sha(dest, rev_options.rev)[0]) + return not is_tag_or_branch + + def get_git_version(self) -> Tuple[int, ...]: + version = self.run_command( + ["version"], + command_desc="git version", + show_stdout=False, + stdout_only=True, + ) + match = GIT_VERSION_REGEX.match(version) + if not match: + logger.warning("Can't parse git version: %s", version) + return () + return (int(match.group(1)), int(match.group(2))) + + @classmethod + def get_current_branch(cls, location: str) -> Optional[str]: + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + # git-symbolic-ref exits with empty stdout if "HEAD" is a detached + # HEAD rather than a symbolic ref. In addition, the -q causes the + # command to exit with status code 1 instead of 128 in this case + # and to suppress the message to stderr. + args = ["symbolic-ref", "-q", "HEAD"] + output = cls.run_command( + args, + extra_ok_returncodes=(1,), + show_stdout=False, + stdout_only=True, + cwd=location, + ) + ref = output.strip() + + if ref.startswith("refs/heads/"): + return ref[len("refs/heads/") :] + + return None + + @classmethod + def get_revision_sha(cls, dest: str, rev: str) -> Tuple[Optional[str], bool]: + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = cls.run_command( + ["show-ref", rev], + cwd=dest, + show_stdout=False, + stdout_only=True, + on_returncode="ignore", + ) + refs = {} + # NOTE: We do not use splitlines here since that would split on other + # unicode separators, which can be maliciously used to install a + # different revision. + for line in output.strip().split("\n"): + line = line.rstrip("\r") + if not line: + continue + try: + ref_sha, ref_name = line.split(" ", maxsplit=2) + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError(f"unexpected show-ref line: {line!r}") + + refs[ref_name] = ref_sha + + branch_ref = f"refs/remotes/origin/{rev}" + tag_ref = f"refs/tags/{rev}" + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + @classmethod + def _should_fetch(cls, dest: str, rev: str) -> bool: + """ + Return true if rev is a ref or is a commit that we don't have locally. + + Branches and tags are not considered in this method because they are + assumed to be always available locally (which is a normal outcome of + ``git clone`` and ``git fetch --tags``). + """ + if rev.startswith("refs/"): + # Always fetch remote refs. + return True + + if not looks_like_hash(rev): + # Git fetch would fail with abbreviated commits. + return False + + if cls.has_commit(dest, rev): + # Don't fetch if we have the commit locally. + return False + + return True + + @classmethod + def resolve_revision( + cls, dest: str, url: HiddenText, rev_options: RevOptions + ) -> RevOptions: + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + # The arg_rev property's implementation for Git ensures that the + # rev return value is always non-None. + assert rev is not None + + sha, is_branch = cls.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not cls._should_fetch(dest, rev): + return rev_options + + # fetch the requested revision + cls.run_command( + make_command("fetch", "-q", url, rev_options.to_args()), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = cls.get_revision(dest, rev="FETCH_HEAD") + rev_options = rev_options.make_new(sha) + + return rev_options + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return cls.get_revision(dest) == name + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + rev_display = rev_options.to_display() + logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest)) + if verbosity <= 0: + flags: Tuple[str, ...] = ("--quiet",) + elif verbosity == 1: + flags = () + else: + flags = ("--verbose", "--progress") + if self.get_git_version() >= (2, 17): + # Git added support for partial clone in 2.17 + # https://git-scm.com/docs/partial-clone + # Speeds up cloning by functioning without a complete copy of repository + self.run_command( + make_command( + "clone", + "--filter=blob:none", + *flags, + url, + dest, + ) + ) + else: + self.run_command(make_command("clone", *flags, url, dest)) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, "branch_name", None) + logger.debug("Rev options %s, branch_name %s", rev_options, branch_name) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = make_command( + "checkout", + "-q", + rev_options.to_args(), + ) + self.run_command(cmd_args, cwd=dest) + elif self.get_current_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = f"origin/{branch_name}" + cmd_args = [ + "checkout", + "-b", + branch_name, + "--track", + track_branch, + ] + self.run_command(cmd_args, cwd=dest) + else: + sha = self.get_revision(dest) + rev_options = rev_options.make_new(sha) + + logger.info("Resolved %s to commit %s", url, rev_options.rev) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + self.run_command( + make_command("config", "remote.origin.url", url), + cwd=dest, + ) + cmd_args = make_command("checkout", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + # First fetch changes from the default remote + if self.get_git_version() >= (1, 9): + # fetch tags in addition to everything else + self.run_command(["fetch", "-q", "--tags"], cwd=dest) + else: + self.run_command(["fetch", "-q"], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + @classmethod + def get_remote_url(cls, location: str) -> str: + """ + Return URL of the first remote encountered. + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + # We need to pass 1 for extra_ok_returncodes since the command + # exits with return code 1 if there are no matching lines. + stdout = cls.run_command( + ["config", "--get-regexp", r"remote\..*\.url"], + extra_ok_returncodes=(1,), + show_stdout=False, + stdout_only=True, + cwd=location, + ) + remotes = stdout.splitlines() + try: + found_remote = remotes[0] + except IndexError: + raise RemoteNotFoundError + + for remote in remotes: + if remote.startswith("remote.origin.url "): + found_remote = remote + break + url = found_remote.split(" ")[1] + return cls._git_remote_to_pip_url(url.strip()) + + @staticmethod + def _git_remote_to_pip_url(url: str) -> str: + """ + Convert a remote url from what git uses to what pip accepts. + + There are 3 legal forms **url** may take: + + 1. A fully qualified url: ssh://git@example.com/foo/bar.git + 2. A local project.git folder: /path/to/bare/repository.git + 3. SCP shorthand for form 1: git@example.com:foo/bar.git + + Form 1 is output as-is. Form 2 must be converted to URI and form 3 must + be converted to form 1. + + See the corresponding test test_git_remote_url_to_pip() for examples of + sample inputs/outputs. + """ + if re.match(r"\w+://", url): + # This is already valid. Pass it though as-is. + return url + if os.path.exists(url): + # A local bare remote (git clone --mirror). + # Needs a file:// prefix. + return pathlib.PurePath(url).as_uri() + scp_match = SCP_REGEX.match(url) + if scp_match: + # Add an ssh:// prefix and replace the ':' with a '/'. + return scp_match.expand(r"ssh://\1\2/\3") + # Otherwise, bail out. + raise RemoteNotValidError(url) + + @classmethod + def has_commit(cls, location: str, rev: str) -> bool: + """ + Check if rev is a commit that is available in the local repository. + """ + try: + cls.run_command( + ["rev-parse", "-q", "--verify", "sha^" + rev], + cwd=location, + log_failed_cmd=False, + ) + except InstallationError: + return False + else: + return True + + @classmethod + def get_revision(cls, location: str, rev: Optional[str] = None) -> str: + if rev is None: + rev = "HEAD" + current_rev = cls.run_command( + ["rev-parse", rev], + show_stdout=False, + stdout_only=True, + cwd=location, + ) + return current_rev.strip() + + @classmethod + def get_subdirectory(cls, location: str) -> Optional[str]: + """ + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. + """ + # find the repo root + git_dir = cls.run_command( + ["rev-parse", "--git-dir"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + repo_root = os.path.abspath(os.path.join(git_dir, "..")) + return find_path_to_project_root_from_repo_root(location, repo_root) + + @classmethod + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don't + work with a ssh:// scheme (e.g. GitHub). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith("file"): + initial_slashes = path[: -len(path.lstrip("/"))] + newpath = initial_slashes + urllib.request.url2pathname(path).replace( + "\\", "/" + ).lstrip("/") + after_plus = scheme.find("+") + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + if "://" not in url: + assert "file:" not in url + url = url.replace("git+", "git+ssh://") + url, rev, user_pass = super().get_url_rev_and_auth(url) + url = url.replace("ssh://", "") + else: + url, rev, user_pass = super().get_url_rev_and_auth(url) + + return url, rev, user_pass + + @classmethod + def update_submodules(cls, location: str) -> None: + if not os.path.exists(os.path.join(location, ".gitmodules")): + return + cls.run_command( + ["submodule", "update", "--init", "--recursive", "-q"], + cwd=location, + ) + + @classmethod + def get_repository_root(cls, location: str) -> Optional[str]: + loc = super().get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ["rev-parse", "--show-toplevel"], + cwd=location, + show_stdout=False, + stdout_only=True, + on_returncode="raise", + log_failed_cmd=False, + ) + except BadCommand: + logger.debug( + "could not determine if %s is under git control " + "because git is not available", + location, + ) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip("\r\n")) + + @staticmethod + def should_add_vcs_url_prefix(repo_url: str) -> bool: + """In either https or ssh form, requirements must be prefixed with git+.""" + return True + + +vcs.register(Git) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000..c183d41 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,163 @@ +import configparser +import logging +import os +from typing import List, Optional, Tuple + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import HiddenText, display_path +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import ( + RevOptions, + VersionControl, + find_path_to_project_root_from_repo_root, + vcs, +) + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = "hg" + dirname = ".hg" + repo_name = "clone" + schemes = ( + "hg+file", + "hg+http", + "hg+https", + "hg+ssh", + "hg+static-http", + ) + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + return [f"--rev={rev}"] + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + rev_display = rev_options.to_display() + logger.info( + "Cloning hg %s%s to %s", + url, + rev_display, + display_path(dest), + ) + if verbosity <= 0: + flags: Tuple[str, ...] = ("--quiet",) + elif verbosity == 1: + flags = () + elif verbosity == 2: + flags = ("--verbose",) + else: + flags = ("--verbose", "--debug") + self.run_command(make_command("clone", "--noupdate", *flags, url, dest)) + self.run_command( + make_command("update", *flags, rev_options.to_args()), + cwd=dest, + ) + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + repo_config = os.path.join(dest, self.dirname, "hgrc") + config = configparser.RawConfigParser() + try: + config.read(repo_config) + config.set("paths", "default", url.secret) + with open(repo_config, "w") as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning("Could not switch Mercurial repository to %s: %s", url, exc) + else: + cmd_args = make_command("update", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + self.run_command(["pull", "-q"], cwd=dest) + cmd_args = make_command("update", "-q", rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_remote_url(cls, location: str) -> str: + url = cls.run_command( + ["showconfig", "paths.default"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + if cls._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + @classmethod + def get_revision(cls, location: str) -> str: + """ + Return the repository-local changeset revision number, as an integer. + """ + current_revision = cls.run_command( + ["parents", "--template={rev}"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + return current_revision + + @classmethod + def get_requirement_revision(cls, location: str) -> str: + """ + Return the changeset identification hash, as a 40-character + hexadecimal string + """ + current_rev_hash = cls.run_command( + ["parents", "--template={node}"], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + return current_rev_hash + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """Always assume the versions don't match""" + return False + + @classmethod + def get_subdirectory(cls, location: str) -> Optional[str]: + """ + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. + """ + # find the repo root + repo_root = cls.run_command( + ["root"], show_stdout=False, stdout_only=True, cwd=location + ).strip() + if not os.path.isabs(repo_root): + repo_root = os.path.abspath(os.path.join(location, repo_root)) + return find_path_to_project_root_from_repo_root(location, repo_root) + + @classmethod + def get_repository_root(cls, location: str) -> Optional[str]: + loc = super().get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ["root"], + cwd=location, + show_stdout=False, + stdout_only=True, + on_returncode="raise", + log_failed_cmd=False, + ) + except BadCommand: + logger.debug( + "could not determine if %s is under hg control " + "because hg is not available", + location, + ) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip("\r\n")) + + +vcs.register(Mercurial) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000..16d93a6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,324 @@ +import logging +import os +import re +from typing import List, Optional, Tuple + +from pip._internal.utils.misc import ( + HiddenText, + display_path, + is_console_interactive, + is_installable_dir, + split_auth_from_netloc, +) +from pip._internal.utils.subprocess import CommandArgs, make_command +from pip._internal.vcs.versioncontrol import ( + AuthInfo, + RemoteNotFoundError, + RevOptions, + VersionControl, + vcs, +) + +logger = logging.getLogger(__name__) + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile(r'committed-rev="(\d+)"') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r"(.*)") + + +class Subversion(VersionControl): + name = "svn" + dirname = ".svn" + repo_name = "checkout" + schemes = ("svn+ssh", "svn+http", "svn+https", "svn+svn", "svn+file") + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: + return True + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + return ["-r", rev] + + @classmethod + def get_revision(cls, location: str) -> str: + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, _ in os.walk(location): + if cls.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(cls.dirname) + entries_fn = os.path.join(base, cls.dirname, "entries") + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = cls._get_svn_url_rev(base) + + if base == location: + assert dirurl is not None + base = dirurl + "/" # save the root url + elif not dirurl or not dirurl.startswith(base): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return str(revision) + + @classmethod + def get_netloc_and_auth( + cls, netloc: str, scheme: str + ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: + """ + This override allows the auth information to be passed to svn via the + --username and --password options instead of via the URL. + """ + if scheme == "ssh": + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super().get_netloc_and_auth(netloc, scheme) + + return split_auth_from_netloc(netloc) + + @classmethod + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + # hotfix the URL scheme after removing svn+ from svn+ssh:// re-add it + url, rev, user_pass = super().get_url_rev_and_auth(url) + if url.startswith("ssh://"): + url = "svn+" + url + return url, rev, user_pass + + @staticmethod + def make_rev_args( + username: Optional[str], password: Optional[HiddenText] + ) -> CommandArgs: + extra_args: CommandArgs = [] + if username: + extra_args += ["--username", username] + if password: + extra_args += ["--password", password] + + return extra_args + + @classmethod + def get_remote_url(cls, location: str) -> str: + # In cases where the source is in a subdirectory, we have to look up in + # the location until we find a valid project root. + orig_location = location + while not is_installable_dir(location): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding a Python project. + logger.warning( + "Could not find Python project for directory %s (tried all " + "parent directories)", + orig_location, + ) + raise RemoteNotFoundError + + url, _rev = cls._get_svn_url_rev(location) + if url is None: + raise RemoteNotFoundError + + return url + + @classmethod + def _get_svn_url_rev(cls, location: str) -> Tuple[Optional[str], int]: + from pip._internal.exceptions import InstallationError + + entries_path = os.path.join(location, cls.dirname, "entries") + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = "" + + url = None + if data.startswith("8") or data.startswith("9") or data.startswith("10"): + entries = list(map(str.splitlines, data.split("\n\x0c\n"))) + del entries[0][0] # get rid of the '8' + url = entries[0][3] + revs = [int(d[9]) for d in entries if len(d) > 9 and d[9]] + [0] + elif data.startswith("= 1.7 + # Note that using get_remote_call_options is not necessary here + # because `svn info` is being run against a local directory. + # We don't need to worry about making sure interactive mode + # is being used to prompt for passwords, because passwords + # are only potentially needed for remote server requests. + xml = cls.run_command( + ["info", "--xml", location], + show_stdout=False, + stdout_only=True, + ) + match = _svn_info_xml_url_re.search(xml) + assert match is not None + url = match.group(1) + revs = [int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)] + except InstallationError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """Always assume the versions don't match""" + return False + + def __init__(self, use_interactive: Optional[bool] = None) -> None: + if use_interactive is None: + use_interactive = is_console_interactive() + self.use_interactive = use_interactive + + # This member is used to cache the fetched version of the current + # ``svn`` client. + # Special value definitions: + # None: Not evaluated yet. + # Empty tuple: Could not parse version. + self._vcs_version: Optional[Tuple[int, ...]] = None + + super().__init__() + + def call_vcs_version(self) -> Tuple[int, ...]: + """Query the version of the currently installed Subversion client. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + # Example versions: + # svn, version 1.10.3 (r1842928) + # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0 + # svn, version 1.7.14 (r1542130) + # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu + # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0) + # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2 + version_prefix = "svn, version " + version = self.run_command(["--version"], show_stdout=False, stdout_only=True) + if not version.startswith(version_prefix): + return () + + version = version[len(version_prefix) :].split()[0] + version_list = version.partition("-")[0].split(".") + try: + parsed_version = tuple(map(int, version_list)) + except ValueError: + return () + + return parsed_version + + def get_vcs_version(self) -> Tuple[int, ...]: + """Return the version of the currently installed Subversion client. + + If the version of the Subversion client has already been queried, + a cached value will be used. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + if self._vcs_version is not None: + # Use cached version, if available. + # If parsing the version failed previously (empty tuple), + # do not attempt to parse it again. + return self._vcs_version + + vcs_version = self.call_vcs_version() + self._vcs_version = vcs_version + return vcs_version + + def get_remote_call_options(self) -> CommandArgs: + """Return options to be used on calls to Subversion that contact the server. + + These options are applicable for the following ``svn`` subcommands used + in this class. + + - checkout + - switch + - update + + :return: A list of command line arguments to pass to ``svn``. + """ + if not self.use_interactive: + # --non-interactive switch is available since Subversion 0.14.4. + # Subversion < 1.8 runs in interactive mode by default. + return ["--non-interactive"] + + svn_version = self.get_vcs_version() + # By default, Subversion >= 1.8 runs in non-interactive mode if + # stdin is not a TTY. Since that is how pip invokes SVN, in + # call_subprocess(), pip must pass --force-interactive to ensure + # the user can be prompted for a password, if required. + # SVN added the --force-interactive option in SVN 1.8. Since + # e.g. RHEL/CentOS 7, which is supported until 2024, ships with + # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip + # can't safely add the option if the SVN version is < 1.8 (or unknown). + if svn_version >= (1, 8): + return ["--force-interactive"] + + return [] + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + rev_display = rev_options.to_display() + logger.info( + "Checking out %s%s to %s", + url, + rev_display, + display_path(dest), + ) + if verbosity <= 0: + flag = "--quiet" + else: + flag = "" + cmd_args = make_command( + "checkout", + flag, + self.get_remote_call_options(), + rev_options.to_args(), + url, + dest, + ) + self.run_command(cmd_args) + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + cmd_args = make_command( + "switch", + self.get_remote_call_options(), + rev_options.to_args(), + url, + dest, + ) + self.run_command(cmd_args) + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + cmd_args = make_command( + "update", + self.get_remote_call_options(), + rev_options.to_args(), + dest, + ) + self.run_command(cmd_args) + + +vcs.register(Subversion) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py new file mode 100644 index 0000000..46ca279 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/vcs/versioncontrol.py @@ -0,0 +1,705 @@ +"""Handles all VCS (version control) support""" + +import logging +import os +import shutil +import sys +import urllib.parse +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Tuple, + Type, + Union, +) + +from pip._internal.cli.spinners import SpinnerInterface +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import ( + HiddenText, + ask_path_exists, + backup_dir, + display_path, + hide_url, + hide_value, + is_installable_dir, + rmtree, +) +from pip._internal.utils.subprocess import ( + CommandArgs, + call_subprocess, + format_command_args, + make_command, +) +from pip._internal.utils.urls import get_url_scheme + +if TYPE_CHECKING: + # Literal was introduced in Python 3.8. + # + # TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7. + from typing import Literal + + +__all__ = ["vcs"] + + +logger = logging.getLogger(__name__) + +AuthInfo = Tuple[Optional[str], Optional[str]] + + +def is_url(name: str) -> bool: + """ + Return true if the name looks like a URL. + """ + scheme = get_url_scheme(name) + if scheme is None: + return False + return scheme in ["http", "https", "file", "ftp"] + vcs.all_schemes + + +def make_vcs_requirement_url( + repo_url: str, rev: str, project_name: str, subdir: Optional[str] = None +) -> str: + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + project_name: the (unescaped) project name. + """ + egg_project_name = project_name.replace("-", "_") + req = f"{repo_url}@{rev}#egg={egg_project_name}" + if subdir: + req += f"&subdirectory={subdir}" + + return req + + +def find_path_to_project_root_from_repo_root( + location: str, repo_root: str +) -> Optional[str]: + """ + Find the the Python project's root by searching up the filesystem from + `location`. Return the path to project root relative to `repo_root`. + Return None if the project root is `repo_root`, or cannot be found. + """ + # find project root. + orig_location = location + while not is_installable_dir(location): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding a Python project. + logger.warning( + "Could not find a Python project for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + if os.path.samefile(repo_root, location): + return None + + return os.path.relpath(location, repo_root) + + +class RemoteNotFoundError(Exception): + pass + + +class RemoteNotValidError(Exception): + def __init__(self, url: str): + super().__init__(url) + self.url = url + + +class RevOptions: + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__( + self, + vc_class: Type["VersionControl"], + rev: Optional[str] = None, + extra_args: Optional[CommandArgs] = None, + ) -> None: + """ + Args: + vc_class: a VersionControl subclass. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vc_class = vc_class + self.branch_name: Optional[str] = None + + def __repr__(self) -> str: + return f"" + + @property + def arg_rev(self) -> Optional[str]: + if self.rev is None: + return self.vc_class.default_arg_rev + + return self.rev + + def to_args(self) -> CommandArgs: + """ + Return the VCS-specific command arguments. + """ + args: CommandArgs = [] + rev = self.arg_rev + if rev is not None: + args += self.vc_class.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self) -> str: + if not self.rev: + return "" + + return f" (to revision {self.rev})" + + def make_new(self, rev: str) -> "RevOptions": + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vc_class.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport: + _registry: Dict[str, "VersionControl"] = {} + schemes = ["ssh", "git", "hg", "bzr", "sftp", "svn"] + + def __init__(self) -> None: + # Register more schemes with urlparse for various version control + # systems + urllib.parse.uses_netloc.extend(self.schemes) + super().__init__() + + def __iter__(self) -> Iterator[str]: + return self._registry.__iter__() + + @property + def backends(self) -> List["VersionControl"]: + return list(self._registry.values()) + + @property + def dirnames(self) -> List[str]: + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self) -> List[str]: + schemes: List[str] = [] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls: Type["VersionControl"]) -> None: + if not hasattr(cls, "name"): + logger.warning("Cannot register VCS %s", cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls() + logger.debug("Registered VCS backend: %s", cls.name) + + def unregister(self, name: str) -> None: + if name in self._registry: + del self._registry[name] + + def get_backend_for_dir(self, location: str) -> Optional["VersionControl"]: + """ + Return a VersionControl object if a repository of that type is found + at the given directory. + """ + vcs_backends = {} + for vcs_backend in self._registry.values(): + repo_path = vcs_backend.get_repository_root(location) + if not repo_path: + continue + logger.debug("Determine that %s uses VCS: %s", location, vcs_backend.name) + vcs_backends[repo_path] = vcs_backend + + if not vcs_backends: + return None + + # Choose the VCS in the inner-most directory. Since all repository + # roots found here would be either `location` or one of its + # parents, the longest path should have the most path components, + # i.e. the backend representing the inner-most repository. + inner_most_repo_path = max(vcs_backends, key=len) + return vcs_backends[inner_most_repo_path] + + def get_backend_for_scheme(self, scheme: str) -> Optional["VersionControl"]: + """ + Return a VersionControl object or None. + """ + for vcs_backend in self._registry.values(): + if scheme in vcs_backend.schemes: + return vcs_backend + return None + + def get_backend(self, name: str) -> Optional["VersionControl"]: + """ + Return a VersionControl object or None. + """ + name = name.lower() + return self._registry.get(name) + + +vcs = VcsSupport() + + +class VersionControl: + name = "" + dirname = "" + repo_name = "" + # List of supported schemes for this Version Control + schemes: Tuple[str, ...] = () + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ: Tuple[str, ...] = () + default_arg_rev: Optional[str] = None + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url: str) -> bool: + """ + Return whether the vcs prefix (e.g. "git+") should be added to a + repository's remote url when used in a requirement. + """ + return not remote_url.lower().startswith(f"{cls.name}:") + + @classmethod + def get_subdirectory(cls, location: str) -> Optional[str]: + """ + Return the path to Python project root, relative to the repo root. + Return None if the project root is in the repo root. + """ + return None + + @classmethod + def get_requirement_revision(cls, repo_dir: str) -> str: + """ + Return the revision string that should be used in a requirement. + """ + return cls.get_revision(repo_dir) + + @classmethod + def get_src_requirement(cls, repo_dir: str, project_name: str) -> str: + """ + Return the requirement string to use to redownload the files + currently at the given repository directory. + + Args: + project_name: the (unescaped) project name. + + The return value has a form similar to the following: + + {repository_url}@{revision}#egg={project_name} + """ + repo_url = cls.get_remote_url(repo_dir) + + if cls.should_add_vcs_url_prefix(repo_url): + repo_url = f"{cls.name}+{repo_url}" + + revision = cls.get_requirement_revision(repo_dir) + subdir = cls.get_subdirectory(repo_dir) + req = make_vcs_requirement_url(repo_url, revision, project_name, subdir=subdir) + + return req + + @staticmethod + def get_base_rev_args(rev: str) -> List[str]: + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def is_immutable_rev_checkout(self, url: str, dest: str) -> bool: + """ + Return true if the commit hash checked out at dest matches + the revision in url. + + Always return False, if the VCS does not support immutable commit + hashes. + + This method does not check if there are local uncommitted changes + in dest after checkout, as pip currently has no use case for that. + """ + return False + + @classmethod + def make_rev_options( + cls, rev: Optional[str] = None, extra_args: Optional[CommandArgs] = None + ) -> RevOptions: + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(cls, rev, extra_args=extra_args) + + @classmethod + def _is_local_repository(cls, repo: str) -> bool: + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or bool(drive) + + @classmethod + def get_netloc_and_auth( + cls, netloc: str, scheme: str + ) -> Tuple[str, Tuple[Optional[str], Optional[str]]]: + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + @classmethod + def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) + if "+" not in scheme: + raise ValueError( + f"Sorry, {url!r} is a malformed VCS url. " + "The format is +://, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp" + ) + # Remove the vcs prefix. + scheme = scheme.split("+", 1)[1] + netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) + rev = None + if "@" in path: + path, rev = path.rsplit("@", 1) + if not rev: + raise InstallationError( + f"The URL {url!r} has an empty revision (after @) " + "which is not supported. Include a revision after @ " + "or remove @ from the URL." + ) + url = urllib.parse.urlunsplit((scheme, netloc, path, query, "")) + return url, rev, user_pass + + @staticmethod + def make_rev_args( + username: Optional[str], password: Optional[HiddenText] + ) -> CommandArgs: + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url: HiddenText) -> Tuple[HiddenText, RevOptions]: + """ + Return the URL and RevOptions object to use in obtain(), + as a tuple (url, rev_options). + """ + secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) + username, secret_password = user_pass + password: Optional[HiddenText] = None + if secret_password is not None: + password = hide_value(secret_password) + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return hide_url(secret_url), rev_options + + @staticmethod + def normalize_url(url: str) -> str: + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib.parse.unquote(url).rstrip("/") + + @classmethod + def compare_urls(cls, url1: str, url2: str) -> bool: + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return cls.normalize_url(url1) == cls.normalize_url(url2) + + def fetch_new( + self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int + ) -> None: + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + verbosity: verbosity level. + """ + raise NotImplementedError + + def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + @classmethod + def is_commit_id_equal(cls, dest: str, name: Optional[str]) -> bool: + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest: str, url: HiddenText, verbosity: int) -> None: + """ + Install or update in editable mode the package represented by this + VersionControl object. + + :param dest: the repository directory in which to install or update. + :param url: the repository URL starting with a vcs prefix. + :param verbosity: verbosity level. + """ + url, rev_options = self.get_url_rev_options(url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options, verbosity=verbosity) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_remote_url(dest) + if self.compare_urls(existing_url, url.secret): + logger.debug( + "%s in %s exists, and has correct URL (%s)", + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.is_commit_id_equal(dest, rev_options.rev): + logger.info( + "Updating %s %s%s", + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info("Skipping because already up-to-date.") + return + + logger.warning( + "%s %s in %s exists with URL %s", + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ("(s)witch, (i)gnore, (w)ipe, (b)ackup ", ("s", "i", "w", "b")) + else: + logger.warning( + "Directory %s already exists, and is not a %s %s.", + dest, + self.name, + self.repo_name, + ) + # https://github.com/python/mypy/issues/1174 + prompt = ("(i)gnore, (w)ipe, (b)ackup ", ("i", "w", "b")) # type: ignore + + logger.warning( + "The plan is to install the %s repository %s", + self.name, + url, + ) + response = ask_path_exists(f"What to do? {prompt[0]}", prompt[1]) + + if response == "a": + sys.exit(-1) + + if response == "w": + logger.warning("Deleting %s", display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options, verbosity=verbosity) + return + + if response == "b": + dest_dir = backup_dir(dest) + logger.warning("Backing up %s to %s", display_path(dest), dest_dir) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options, verbosity=verbosity) + return + + # Do nothing if the response is "i". + if response == "s": + logger.info( + "Switching %s %s to %s%s", + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + + def unpack(self, location: str, url: HiddenText, verbosity: int) -> None: + """ + Clean up current location and download the url repository + (and vcs infos) into location + + :param url: the repository URL starting with a vcs prefix. + :param verbosity: verbosity level. + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location, url=url, verbosity=verbosity) + + @classmethod + def get_remote_url(cls, location: str) -> str: + """ + Return the url used at location + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + raise NotImplementedError + + @classmethod + def get_revision(cls, location: str) -> str: + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + @classmethod + def run_command( + cls, + cmd: Union[List[str], CommandArgs], + show_stdout: bool = True, + cwd: Optional[str] = None, + on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise", + extra_ok_returncodes: Optional[Iterable[int]] = None, + command_desc: Optional[str] = None, + extra_environ: Optional[Mapping[str, Any]] = None, + spinner: Optional[SpinnerInterface] = None, + log_failed_cmd: bool = True, + stdout_only: bool = False, + ) -> str: + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = make_command(cls.name, *cmd) + if command_desc is None: + command_desc = format_command_args(cmd) + try: + return call_subprocess( + cmd, + show_stdout, + cwd, + on_returncode=on_returncode, + extra_ok_returncodes=extra_ok_returncodes, + command_desc=command_desc, + extra_environ=extra_environ, + unset_environ=cls.unset_environ, + spinner=spinner, + log_failed_cmd=log_failed_cmd, + stdout_only=stdout_only, + ) + except FileNotFoundError: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + raise BadCommand( + f"Cannot find command {cls.name!r} - do you have " + f"{cls.name!r} installed and in your PATH?" + ) + except PermissionError: + # errno.EACCES = Permission denied + # This error occurs, for instance, when the command is installed + # only for another user. So, the current user don't have + # permission to call the other user command. + raise BadCommand( + f"No permission to execute {cls.name!r} - install it " + f"locally, globally (ask admin), or check your PATH. " + f"See possible solutions at " + f"https://pip.pypa.io/en/latest/reference/pip_freeze/" + f"#fixing-permission-denied." + ) + + @classmethod + def is_repository_directory(cls, path: str) -> bool: + """ + Return whether a directory path is a repository directory. + """ + logger.debug("Checking in %s for %s (%s)...", path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def get_repository_root(cls, location: str) -> Optional[str]: + """ + Return the "root" (top-level) directory controlled by the vcs, + or `None` if the directory is not in any. + + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For + example, the Git override checks that Git is actually available. + """ + if cls.is_repository_directory(location): + return location + return None diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py b/.venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py new file mode 100644 index 0000000..b1debe3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_internal/wheel_builder.py @@ -0,0 +1,354 @@ +"""Orchestrator for building wheels from InstallRequirements. +""" + +import logging +import os.path +import re +import shutil +from typing import Iterable, List, Optional, Tuple + +from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version +from pip._vendor.packaging.version import InvalidVersion, Version + +from pip._internal.cache import WheelCache +from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel +from pip._internal.metadata import FilesystemWheel, get_wheel_distribution +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.operations.build.wheel import build_wheel_pep517 +from pip._internal.operations.build.wheel_editable import build_wheel_editable +from pip._internal.operations.build.wheel_legacy import build_wheel_legacy +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir, hash_file +from pip._internal.utils.setuptools_build import make_setuptools_clean_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import vcs + +logger = logging.getLogger(__name__) + +_egg_info_re = re.compile(r"([a-z0-9_.]+)-([a-z0-9_.!+-]+)", re.IGNORECASE) + +BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] + + +def _contains_egg_info(s: str) -> bool: + """Determine whether the string looks like an egg_info. + + :param s: The string to parse. E.g. foo-2.1 + """ + return bool(_egg_info_re.search(s)) + + +def _should_build( + req: InstallRequirement, + need_wheel: bool, +) -> bool: + """Return whether an InstallRequirement should be built into a wheel.""" + if req.constraint: + # never build requirements that are merely constraints + return False + if req.is_wheel: + if need_wheel: + logger.info( + "Skipping %s, due to already being wheel.", + req.name, + ) + return False + + if need_wheel: + # i.e. pip wheel, not pip install + return True + + # From this point, this concerns the pip install command only + # (need_wheel=False). + + if not req.source_dir: + return False + + if req.editable: + # we only build PEP 660 editable requirements + return req.supports_pyproject_editable() + + return True + + +def should_build_for_wheel_command( + req: InstallRequirement, +) -> bool: + return _should_build(req, need_wheel=True) + + +def should_build_for_install_command( + req: InstallRequirement, +) -> bool: + return _should_build(req, need_wheel=False) + + +def _should_cache( + req: InstallRequirement, +) -> Optional[bool]: + """ + Return whether a built InstallRequirement can be stored in the persistent + wheel cache, assuming the wheel cache is available, and _should_build() + has determined a wheel needs to be built. + """ + if req.editable or not req.source_dir: + # never cache editable requirements + return False + + if req.link and req.link.is_vcs: + # VCS checkout. Do not cache + # unless it points to an immutable commit hash. + assert not req.editable + assert req.source_dir + vcs_backend = vcs.get_backend_for_scheme(req.link.scheme) + assert vcs_backend + if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir): + return True + return False + + assert req.link + base, ext = req.link.splitext() + if _contains_egg_info(base): + return True + + # Otherwise, do not cache. + return False + + +def _get_cache_dir( + req: InstallRequirement, + wheel_cache: WheelCache, +) -> str: + """Return the persistent or temporary cache directory where the built + wheel need to be stored. + """ + cache_available = bool(wheel_cache.cache_dir) + assert req.link + if cache_available and _should_cache(req): + cache_dir = wheel_cache.get_path_for_link(req.link) + else: + cache_dir = wheel_cache.get_ephem_path_for_link(req.link) + return cache_dir + + +def _verify_one(req: InstallRequirement, wheel_path: str) -> None: + canonical_name = canonicalize_name(req.name or "") + w = Wheel(os.path.basename(wheel_path)) + if canonicalize_name(w.name) != canonical_name: + raise InvalidWheelFilename( + f"Wheel has unexpected file name: expected {canonical_name!r}, " + f"got {w.name!r}", + ) + dist = get_wheel_distribution(FilesystemWheel(wheel_path), canonical_name) + dist_verstr = str(dist.version) + if canonicalize_version(dist_verstr) != canonicalize_version(w.version): + raise InvalidWheelFilename( + f"Wheel has unexpected file name: expected {dist_verstr!r}, " + f"got {w.version!r}", + ) + metadata_version_value = dist.metadata_version + if metadata_version_value is None: + raise UnsupportedWheel("Missing Metadata-Version") + try: + metadata_version = Version(metadata_version_value) + except InvalidVersion: + msg = f"Invalid Metadata-Version: {metadata_version_value}" + raise UnsupportedWheel(msg) + if metadata_version >= Version("1.2") and not isinstance(dist.version, Version): + raise UnsupportedWheel( + f"Metadata 1.2 mandates PEP 440 version, but {dist_verstr!r} is not" + ) + + +def _build_one( + req: InstallRequirement, + output_dir: str, + verify: bool, + build_options: List[str], + global_options: List[str], + editable: bool, +) -> Optional[str]: + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + artifact = "editable" if editable else "wheel" + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning( + "Building %s for %s failed: %s", + artifact, + req.name, + e, + ) + return None + + # Install build deps into temporary directory (PEP 518) + with req.build_env: + wheel_path = _build_one_inside_env( + req, output_dir, build_options, global_options, editable + ) + if wheel_path and verify: + try: + _verify_one(req, wheel_path) + except (InvalidWheelFilename, UnsupportedWheel) as e: + logger.warning("Built %s for %s is invalid: %s", artifact, req.name, e) + return None + return wheel_path + + +def _build_one_inside_env( + req: InstallRequirement, + output_dir: str, + build_options: List[str], + global_options: List[str], + editable: bool, +) -> Optional[str]: + with TempDirectory(kind="wheel") as temp_dir: + assert req.name + if req.use_pep517: + assert req.metadata_directory + assert req.pep517_backend + if global_options: + logger.warning( + "Ignoring --global-option when building %s using PEP 517", req.name + ) + if build_options: + logger.warning( + "Ignoring --build-option when building %s using PEP 517", req.name + ) + if editable: + wheel_path = build_wheel_editable( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + tempd=temp_dir.path, + ) + else: + wheel_path = build_wheel_pep517( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + tempd=temp_dir.path, + ) + else: + wheel_path = build_wheel_legacy( + name=req.name, + setup_py_path=req.setup_py_path, + source_dir=req.unpacked_source_directory, + global_options=global_options, + build_options=build_options, + tempd=temp_dir.path, + ) + + if wheel_path is not None: + wheel_name = os.path.basename(wheel_path) + dest_path = os.path.join(output_dir, wheel_name) + try: + wheel_hash, length = hash_file(wheel_path) + shutil.move(wheel_path, dest_path) + logger.info( + "Created wheel for %s: filename=%s size=%d sha256=%s", + req.name, + wheel_name, + length, + wheel_hash.hexdigest(), + ) + logger.info("Stored in directory: %s", output_dir) + return dest_path + except Exception as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, + e, + ) + # Ignore return, we can't do anything else useful. + if not req.use_pep517: + _clean_one_legacy(req, global_options) + return None + + +def _clean_one_legacy(req: InstallRequirement, global_options: List[str]) -> bool: + clean_args = make_setuptools_clean_args( + req.setup_py_path, + global_options=global_options, + ) + + logger.info("Running setup.py clean for %s", req.name) + try: + call_subprocess( + clean_args, command_desc="python setup.py clean", cwd=req.source_dir + ) + return True + except Exception: + logger.error("Failed cleaning build dir for %s", req.name) + return False + + +def build( + requirements: Iterable[InstallRequirement], + wheel_cache: WheelCache, + verify: bool, + build_options: List[str], + global_options: List[str], +) -> BuildResult: + """Build wheels. + + :return: The list of InstallRequirement that succeeded to build and + the list of InstallRequirement that failed to build. + """ + if not requirements: + return [], [] + + # Build the wheels. + logger.info( + "Building wheels for collected packages: %s", + ", ".join(req.name for req in requirements), # type: ignore + ) + + with indent_log(): + build_successes, build_failures = [], [] + for req in requirements: + assert req.name + cache_dir = _get_cache_dir(req, wheel_cache) + wheel_file = _build_one( + req, + cache_dir, + verify, + build_options, + global_options, + req.editable and req.permit_editable_wheels, + ) + if wheel_file: + # Record the download origin in the cache + if req.download_info is not None: + # download_info is guaranteed to be set because when we build an + # InstallRequirement it has been through the preparer before, but + # let's be cautious. + wheel_cache.record_download_origin(cache_dir, req.download_info) + # Update the link for this. + req.link = Link(path_to_url(wheel_file)) + req.local_file_path = req.link.file_path + assert req.link.is_wheel + build_successes.append(req) + else: + build_failures.append(req) + + # notify success/failure + if build_successes: + logger.info( + "Successfully built %s", + " ".join([req.name for req in build_successes]), # type: ignore + ) + if build_failures: + logger.info( + "Failed to build %s", + " ".join([req.name for req in build_failures]), # type: ignore + ) + # Return a list of requirements that failed to build + return build_successes, build_failures diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000..c1884ba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,121 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = False + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +WHEEL_DIR = os.path.abspath(os.path.dirname(__file__)) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # We can just silently allow import failures to pass here. If we + # got to this point it means that ``import pip._vendor.whatever`` + # failed and so did ``import whatever``. Since we're importing this + # upfront in an attempt to alias imports, not erroring here will + # just mean we get a regular import error whenever pip *actually* + # tries to import one of these modules to use it, which actually + # gives us a better error message than we would have otherwise + # gotten. + pass + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = glob.glob(os.path.join(WHEEL_DIR, "*.whl")) + sys.path + + # Actually alias all of our vendored dependencies. + vendored("cachecontrol") + vendored("certifi") + vendored("colorama") + vendored("distlib") + vendored("distro") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("six.moves.urllib.parse") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pep517") + vendored("pkg_resources") + vendored("platformdirs") + vendored("progress") + vendored("requests") + vendored("requests.exceptions") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") + vendored("resolvelib") + vendored("rich") + vendored("rich.console") + vendored("rich.highlighter") + vendored("rich.logging") + vendored("rich.markup") + vendored("rich.progress") + vendored("rich.segment") + vendored("rich.style") + vendored("rich.text") + vendored("rich.traceback") + vendored("tenacity") + vendored("tomli") + vendored("truststore") + vendored("urllib3") 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 new file mode 100644 index 0000000000000000000000000000000000000000..156cffdaa23052febf83bc4b9c42d585e353119a GIT binary patch literal 4714 zcmbW3TWlN06^3WY;Z>w%>*Aa2cx}lS+VRq|EZ?Fyu_DC|>Ps6SO)RG&R@@P}@{+Te z9opu`1{5Gb<)VNcpnz?(Kw6+c9mqia*vB?61=^PgS|jPk4=wVPH~CVa@Ket$xwJHm zsGtjwbG|eGo|&CFduIQh&887t|NL9T{J0yTZv})`ylFgrf)Ton3^aiZ#fVr+B{C6F zgdMe_6Vaf>6S1I;Ps9y8ksz_@wPBbS?KF%aUn?kkWgSAKV@_#!!*(KL#6Ce2S(3OF zVY?%UM5n_!FS&h%5wd#8NM&eG1*Gjxj;x381CGL^9SK{>&Z;*JS#G54EEODR2TO{5p@khrkajA z1xN0qDHX&jr>e~eXt-J>#1OKEJKa#Lw5mF^LU>tpk*l1rs%~lN z^qZyyQOv4i8l)MlHL_EtHOo*ZiCUzAfDuDAE0r48CoQ7tRYPq`Xw|BNE?u(?or4Ib zbD1#5gxl09Mk{K~b~qz?MN5At3SCa|JM~G2S~X4zX2qtApL?^|tg}HS6csfJzo#F7 z7fu0XdTDPUL5Mf2CPLRxSV6BVDa2z<0r+_$wCBRRQnJU0+>g$|X(KKL@!)I{_H;)4 zFTbv#P2gBXql|#&nljzF{9Zu@He%si--r2Mk6e%Pb)jyRxDhe-wdg9UV1{#I_25!a z9rYvL)gXih5Q{440(h^WtI-SSijs?;a}ysOxUPLPc%6wAh24o+kWAKdMf*-HdoJzA zAqOiu_mhP}Rj-gj!A}>OI|p{6WYJ08a{RbOi=vG>b;nOss8NIT^5e|0EtC5Rhj5*9 z=6Adm?D;rjl;yCWZU{wSKif!jVg%#(cv5$WkIO_i9I--Trf?fKL8hslv=8_}-ZA1?mx;)BieJ4b#Qz1eZ2%aHmLhs;>^emn=9wj?}6}{c@X~&(h`L)}Bd+2`mANxM6^-?n0^TVmDaN9g#zKwvDRj3pF+Ra`cOR3jDjVz1o$s2O#cKQ0VLVF%5tZVLXi3cXAmcWukmYRm$q zyI%Haw$5O)QjHabLgTJ$nXvvrJ#c$oxEz}lVHVKg$8BN{4<7Ne_N7vR5r@`T5fUe3 zTRIn+XP6*L+KiT<6D&qPtPzJhZg*&DcGl6SlDVq=I;3+Dw|Tv-QHHo87%SMRV7-LTktMHxP7Z@?K*{{!*MLP&9f|7 zM|I_pux*y%xI5Y@EMilKx6`z?C%P@_?15IBJfhFRq8Hm!Ao$l&205EvGu0JW`H zE|g)uq6)d=|JQArsE8I31xFV-G=p zH`l&jTav|X-U$&(3#YYvm5{BuwMn>UK+fuJ|0?0cPPNTU7&+9=BGui zvEZnK%7>X*EV~);FO~xQ#9bRu*({YU_`z3Gv)`hnk|=N;L4QSOmuj|~4InI1O{GIh zA|Kpz(CP3xL`?-YhtDu~O~Yh*kxart|)>P3^+{TQbe%ka+8A&)++}uw{55b9g58B-5pgDo+j}oL!8g?k%?lZs$JDHI--Xp1HT_!KsBUBMV)x z{yF;B`1$FNPv7{-%#R+&Q7k+Afrq#J17~J0czAXbpr_x%1EMDkQSk6q!QqhMP|fzs><$n22Y0qY z?D23e@CGnTwV>;$hmS3fb=bp40#9oy2R%Hr zT+@s*`@V-aL(l9_J=`PO<~p_3I&{qb%)@;`ATMYbptVU`J^YdohAaK9hkFHw!V3@a OhQ;v+qz^j+Ir(2{w)Kzz literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/six.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/six.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7af6438073fdbb03e620d50d38dac1dc20a3165 GIT binary patch literal 41291 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71a83b406e2b7781cfdbba603ca6409fa1832258 GIT binary patch literal 122071 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 adGy_g_wH%)cXJFm&U^29%>Nx?;P^j;n_qbV literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py new file mode 100644 index 0000000..4d20bc9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__init__.py @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 + +"""CacheControl import Interface. + +Make it easy to import from cachecontrol without long namespaces. +""" +__author__ = "Eric Larson" +__email__ = "eric@ionrock.org" +__version__ = "0.13.1" + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.controller import CacheController +from pip._vendor.cachecontrol.wrapper import CacheControl + +__all__ = [ + "__author__", + "__email__", + "__version__", + "CacheControlAdapter", + "CacheController", + "CacheControl", +] + +import logging + +logging.getLogger(__name__).addHandler(logging.NullHandler()) 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 new file mode 100644 index 0000000000000000000000000000000000000000..baa0969b046af7fd7cfc231fe96d61865b7aa1b1 GIT binary patch literal 924 zcmaJ<&1=*^6rb!@Hc3BKicl;8q3a%+w0g2sZLNYrTf~cy%P>u*>9muXFq3WBJ?)?1 z&8vt9rT>XuEJa!Zg@SnSR_(1PU$(n+FFJ=ezxU>S{N8)>b#&B5aL&Gz*vAq=KMG}V zDt&PA3BWrPp#VijU__;$6qSQ=REes*l2n7LfdJQdEinVLhfQ8jtiUo*jH3DnBe3D! z;!fa{5OsEL4+>o0bo9Wu(su4#TL-8YH8aqz?}I)Z#2Tn=HSNykDhapgsuY=$++|5B zRp!1F8C6>(q@HcBlU?ev%%w!P-AoQ#wvWhRA^;jSlD+^Nlb3lF@7Uj@OYk>4C*Au%vh-Xby$;p z_H=GVYZi-yip=+***%}LO+RhJ7zHflzOPwE=U|h&B&OPjX8rz5{Nb7S7&E~#jJ>pd zSzk(I)Z+B9ngS-pU)66woRB>b?yp literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..ece9f826d6ef161c824e1026ad10b344b0cec0e7 GIT binary patch literal 2668 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

- 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..927fbb1da03b0d5677d1d0c3ae187af82fe0f0e5 GIT binary patch literal 4369 zcmc&%O>7&-6`uX!KPk$REbGUS*OC*Pwk+0-8`P+sK((dVam^T((85fE^^&_3S6c3} zGfT@9WW*?pLP`_J0t!&cB|?D$aos~sJ@pcxr}SdgDx@qN)J1y9O^sZ%@Tu?3l9VOT zD9}q+(EPsn=FR)wyf^$)A`vCb?EK|RnKwOY z)UJBr^O|L8IhYr`_{GV|tJe%x1{5Rj@8g$}UkTe8Xw-;7vVu7l!UY*aSzpjEeM+EWTOG+i-m z*HJVMm8%pA1|3#3K$tBWw&E5wS1G`*ipi9`Q`MAW!Fds{OpSthOJQ(s#)5%90Uviq9Br= zG4zu+Zn&j#f!(;FIJWX5)2`e~4f`r{>T+lr%h7b-+6bI)>|Hdc zilFkiLlwq-4e9W8<|`|#!VNa-*hgIju~AKM92gzuXrL#aAO2w$OojAvkSc>y*Y^cC z$`_4cWCiA7YgTkBmX1qo$5+ar1&5Y2I}a$OvX*y8_;?s4FaOjQL( z_j+1S70TW~>zT$Sf;c2I!zy?p$c7uE3>AXQ{%k&71p3oV11jwFRqEgsXX$HB&T-8= zOJ4!6axVPvoP18fCN#9xM)W|%UZqkIZ{-+BM>Z7+sw@DQcCp74t zASp3J+dw=edhMz@WkB?=M;lf3LiK)1qL7`4C)! z8E|rF?fs^ow^e;oClxY5hKP$8{5wLZB#`UGmS@Q=={lJeUWgZLtl6n=3-dy2ysFT^ z1M7~%EkFZvvw3Z!4fBps>QFD}XN#1`4NPAGL58|3if`FA@D-*O%Bn9nH`v;^&&!-f z416(BZ zv`zXvAwao{wh&13X!8pole;Sn?EUDkfXo)eO$YW$67rxvqzg0PPgK&y+k%~c!Ft?% z&j(Sic?ksKMjF#@tkIh0FSAC^uU~+l*e!*<9sS%U&NRBgo)02z{bDw+Dn!{=NhHQnHQW2WOQ9P3;yM|?9oHb^=sGs(3 zn#Qarmr7dM3u4r>9WOR|{`}a)gnDgk^nw@Qj#~F;F9`XuV!69qI_UfITwhC9=F7)n zbM{Lp?vTw`a_Not-`GrcF3l~@Exot+9)yO~(LX0&e$snzIk*)k`}WtO%h6hLIk_sV zo?35z`El>iYdO*9Ov$22Oo;>JO6}=dP@syRzPu*_OnQzW*v@#P)tDC`gHMqA zLM9dD@yV0%nd|c&n5B^SyusEf`1xQ%Q-FKFo*pEvi_Ugo=?$m<(Wi074^w#2A8}Wl{@q;Uq>+wPGijCO7T5MoFra)j? zy1aP#gDX#xoeMEPWjr56nk;<*d;HHVLA>CRCNwYVB5wh66PSMfPt2vH%r|&Uezy|Q z28&UZX$3=Va!F5u3?0D&siJ9If+m4i5>GJ{^TpgiD|U9EEL`qKK|*e zkGj(f@@A~<-r@JZzap=OK1RN}dKNA}3CDNt&q~*Nc;LT~1}DIfMZ;KhK;bKeKPumO zCs&1^Kkp7J8OJu>Rpxn((Z#J$+mmq=(rhm*@+9U``aQ(JoDG2p+n$_r9IM`;ui@+o zoR#T!pqh)qkGndkFCO6g)u%lPrhUjWxG0M6JA4Vj3n{9qB}cDV*p92}&)|t)p9!g| z?&MXKcHwS)SnRNXH-a1fz8`;mCNNV_EHF{9b5PtR{}9^);jJTqz|dyLz?Ovd@r%5E z5=m?apugP~4IJL?X%Fn*KGGZLfBIS^(Dk%C7U<$vf#ap(sA!=A)&fN-(oc^-;f3+n z6ozsCp&oRrW%%JoG2wFaQ4B#|tmwMsk-TNXGufwyYx)LC;R}sx|15mgx5Vr80*qm< sV))d9_k|$*Gfae`uSx7Ha`dkxy++c1liI(MkNqoX3PRVn1WS(eAJmop;s5{u literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e073c6e24b75aed84d2a07e492ea83a5a8b69d33 GIT binary patch literal 6716 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 N?it*3;l literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..9658505070685cf5f2fcbeaf715ea067549e2582 GIT binary patch literal 1696 zcmaJ>-EZ4e6u&oi9LI^9kA|*m%a*l;VPG+^0%c-|F01xYEt=?FaD^f_xg;L`2=_W; ziCig2NPx8WR{RB^^2DDp-cTQmrAS3+LcCe(cC*ukn%0nQ=?dgb4Fy!V)zR z0>t49lp8mbmZV9MO`x`vmWpt~Ok1)hM_4qctc;e4aMH|LIV}f4a^hD{Ps;x#)#ZBX ztGJf$NXpco>;ei5Qo3zBuI?I+O_W$5Klu3mdbNCey}WU!ax0L^j%lJAvr_JKS+6xw z*|A;hm}_;tuVH5CfjzK`-WU z9`D7y#E)=a7+7~i4?LvB{pYI3JaK>M0fI9)+$3icD11eU2dNrMQx&;-g9H*exl)j9 zj_w*{YlAG(L{w|0PDqf8PU+hws#?fxI(5=bG+noKO;x)h1B4=l!wDKHX3+<6%W2!L zk_>o)sDpUq08cS{hHY84JC<;Fo)MyzrcE{g>b3W3DV`U?!xD3R0aAcTXgGzumBb=?OlIz=gG$R zsJHGHF8xwiJ$PxgckkD!d0&+M!bN}jML$39pI7|3*PhFXjL^FkW}1zrNdOFw{b+>10_*;P+?2SWA@VH?5K-Nn>4kw`|%4@-OZjfbkQqT z92-U14)|@yF{7M!bEh3VqOdY_3lptAGO^-x7u-$@ZH-@^HpM3+$_%3v|3w*2-wBPC zv^#S;ty{!B@ev3ZxovFYc}h~I1F2g5v`rPLTE*-;76{b6VK*>O1)s5b1>^mb0J5chpG86PM@Iw@-tzA uAra&j!z4oz$S#K|hSK2N)lg<=3KUkt3`1EkTZ(*zc_7R@pQgGf@V^1!Y}TUy literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py new file mode 100644 index 0000000..2c84208 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/_cmd.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import logging +from argparse import ArgumentParser +from typing import TYPE_CHECKING + +from pip._vendor import requests + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.cache import DictCache +from pip._vendor.cachecontrol.controller import logger + +if TYPE_CHECKING: + from argparse import Namespace + + from pip._vendor.cachecontrol.controller import CacheController + + +def setup_logging() -> None: + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + logger.addHandler(handler) + + +def get_session() -> requests.Session: + adapter = CacheControlAdapter( + DictCache(), cache_etags=True, serializer=None, heuristic=None + ) + sess = requests.Session() + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + sess.cache_controller = adapter.controller # type: ignore[attr-defined] + return sess + + +def get_args() -> Namespace: + parser = ArgumentParser() + parser.add_argument("url", help="The URL to try and cache") + return parser.parse_args() + + +def main() -> None: + args = get_args() + sess = get_session() + + # Make a request to get a response + resp = sess.get(args.url) + + # Turn on logging + setup_logging() + + # try setting the cache + cache_controller: CacheController = ( + sess.cache_controller # type: ignore[attr-defined] + ) + cache_controller.cache_response(resp.request, resp.raw) + + # Now try to get it + if cache_controller.cached_request(resp.request): + print("Cached!") + else: + print("Not cached :(") + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py new file mode 100644 index 0000000..3e83e30 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/adapter.py @@ -0,0 +1,161 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import functools +import types +import zlib +from typing import TYPE_CHECKING, Any, Collection, Mapping + +from pip._vendor.requests.adapters import HTTPAdapter + +from pip._vendor.cachecontrol.cache import DictCache +from pip._vendor.cachecontrol.controller import PERMANENT_REDIRECT_STATUSES, CacheController +from pip._vendor.cachecontrol.filewrapper import CallbackFileWrapper + +if TYPE_CHECKING: + from pip._vendor.requests import PreparedRequest, Response + from pip._vendor.urllib3 import HTTPResponse + + from pip._vendor.cachecontrol.cache import BaseCache + from pip._vendor.cachecontrol.heuristics import BaseHeuristic + from pip._vendor.cachecontrol.serialize import Serializer + + +class CacheControlAdapter(HTTPAdapter): + invalidating_methods = {"PUT", "PATCH", "DELETE"} + + def __init__( + self, + cache: BaseCache | None = None, + cache_etags: bool = True, + controller_class: type[CacheController] | None = None, + serializer: Serializer | None = None, + heuristic: BaseHeuristic | None = None, + cacheable_methods: Collection[str] | None = None, + *args: Any, + **kw: Any, + ) -> None: + super().__init__(*args, **kw) + self.cache = DictCache() if cache is None else cache + self.heuristic = heuristic + self.cacheable_methods = cacheable_methods or ("GET",) + + controller_factory = controller_class or CacheController + self.controller = controller_factory( + self.cache, cache_etags=cache_etags, serializer=serializer + ) + + def send( + self, + request: PreparedRequest, + stream: bool = False, + timeout: None | float | tuple[float, float] | tuple[float, None] = None, + verify: bool | str = True, + cert: (None | bytes | str | tuple[bytes | str, bytes | str]) = None, + proxies: Mapping[str, str] | None = None, + cacheable_methods: Collection[str] | None = None, + ) -> Response: + """ + Send a request. Use the request information to see if it + exists in the cache and cache the response if we need to and can. + """ + cacheable = cacheable_methods or self.cacheable_methods + if request.method in cacheable: + try: + cached_response = self.controller.cached_request(request) + except zlib.error: + cached_response = None + if cached_response: + return self.build_response(request, cached_response, from_cache=True) + + # check for etags and add headers if appropriate + request.headers.update(self.controller.conditional_headers(request)) + + resp = super().send(request, stream, timeout, verify, cert, proxies) + + return resp + + def build_response( + self, + request: PreparedRequest, + response: HTTPResponse, + from_cache: bool = False, + cacheable_methods: Collection[str] | None = None, + ) -> Response: + """ + Build a response by making a request or using the cache. + + This will end up calling send and returning a potentially + cached response + """ + cacheable = cacheable_methods or self.cacheable_methods + if not from_cache and request.method in cacheable: + # Check for any heuristics that might update headers + # before trying to cache. + if self.heuristic: + response = self.heuristic.apply(response) + + # apply any expiration heuristics + if response.status == 304: + # We must have sent an ETag request. This could mean + # that we've been expired already or that we simply + # have an etag. In either case, we want to try and + # update the cache if that is the case. + cached_response = self.controller.update_cached_response( + request, response + ) + + if cached_response is not response: + from_cache = True + + # We are done with the server response, read a + # possible response body (compliant servers will + # not return one, but we cannot be 100% sure) and + # release the connection back to the pool. + response.read(decode_content=False) + response.release_conn() + + response = cached_response + + # We always cache the 301 responses + elif int(response.status) in PERMANENT_REDIRECT_STATUSES: + self.controller.cache_response(request, response) + else: + # Wrap the response file with a wrapper that will cache the + # response when the stream has been consumed. + response._fp = CallbackFileWrapper( # type: ignore[attr-defined] + response._fp, # type: ignore[attr-defined] + functools.partial( + self.controller.cache_response, request, response + ), + ) + if response.chunked: + super_update_chunk_length = response._update_chunk_length # type: ignore[attr-defined] + + def _update_chunk_length(self: HTTPResponse) -> None: + super_update_chunk_length() + if self.chunk_left == 0: + self._fp._close() # type: ignore[attr-defined] + + response._update_chunk_length = types.MethodType( # type: ignore[attr-defined] + _update_chunk_length, response + ) + + resp: Response = super().build_response(request, response) # type: ignore[no-untyped-call] + + # See if we should invalidate the cache. + if request.method in self.invalidating_methods and resp.ok: + assert request.url is not None + cache_url = self.controller.cache_url(request.url) + self.cache.delete(cache_url) + + # Give the request a from_cache attr to let people use it + resp.from_cache = from_cache # type: ignore[attr-defined] + + return resp + + def close(self) -> None: + self.cache.close() + super().close() # type: ignore[no-untyped-call] diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py new file mode 100644 index 0000000..3293b00 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/cache.py @@ -0,0 +1,74 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 + +""" +The cache object API for implementing caches. The default is a thread +safe in-memory dictionary. +""" +from __future__ import annotations + +from threading import Lock +from typing import IO, TYPE_CHECKING, MutableMapping + +if TYPE_CHECKING: + from datetime import datetime + + +class BaseCache: + def get(self, key: str) -> bytes | None: + raise NotImplementedError() + + def set( + self, key: str, value: bytes, expires: int | datetime | None = None + ) -> None: + raise NotImplementedError() + + def delete(self, key: str) -> None: + raise NotImplementedError() + + def close(self) -> None: + pass + + +class DictCache(BaseCache): + def __init__(self, init_dict: MutableMapping[str, bytes] | None = None) -> None: + self.lock = Lock() + self.data = init_dict or {} + + def get(self, key: str) -> bytes | None: + return self.data.get(key, None) + + def set( + self, key: str, value: bytes, expires: int | datetime | None = None + ) -> None: + with self.lock: + self.data.update({key: value}) + + def delete(self, key: str) -> None: + with self.lock: + if key in self.data: + self.data.pop(key) + + +class SeparateBodyBaseCache(BaseCache): + """ + In this variant, the body is not stored mixed in with the metadata, but is + passed in (as a bytes-like object) in a separate call to ``set_body()``. + + That is, the expected interaction pattern is:: + + cache.set(key, serialized_metadata) + cache.set_body(key) + + Similarly, the body should be loaded separately via ``get_body()``. + """ + + def set_body(self, key: str, body: bytes) -> None: + raise NotImplementedError() + + def get_body(self, key: str) -> IO[bytes] | None: + """ + Return the body as file-like object. + """ + raise NotImplementedError() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py new file mode 100644 index 0000000..24ff469 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 + +from pip._vendor.cachecontrol.caches.file_cache import FileCache, SeparateBodyFileCache +from pip._vendor.cachecontrol.caches.redis_cache import RedisCache + +__all__ = ["FileCache", "SeparateBodyFileCache", "RedisCache"] 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 new file mode 100644 index 0000000000000000000000000000000000000000..2f6da2e86b21dcc3b7435a2a2e476a6c9be46001 GIT binary patch literal 457 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..a09517680da67c0a1cf1337ae7a09a7380dcb56f GIT binary patch literal 2760 zcmbVO-ES0C6uc0i2+5{wp78l@1B2iM?cx^rn~nVnhhodtH? zk|y#%6HURFu1{h@qNG0f=-;3(G_|3bKvH9T@C{su2~VDLXP1_O4|pay_v_qy&iUPQ z&YfS==?((4_>1foHA4QtL7QNVBsD(;K203rNFFIkIZ494?8zlLCzq6*BF2iRmb9EE z`l^>GC38uMP>m%(R`c{yDwmRoOa_UQxJsNPP0lFdc#`X=>Y3DEILJuRD%SOjd5GEs z74>#+sSQPf;<$(yte`eR<7! z5D^tr*NPk3{iSxM)ot4dsI+bwQF$3nVBa%)GcsmQBCNUo`5TtR1^U(GS zl;MR&iXU8Jcma`82@4F(jFOC&J-a|74YttBk>dJcln$ZnczKTb;P6aBgcm~>BkiK? zRcMr?m&-1rJW9fuc{nZ79O{KO$2&Cz#Ua=W<7Yw;)SzNM1#E&mOpvY(3r5$}(B#n6 ziOCadT*n9Uf&v<+$s$qLkQlqNY~5Ic^Q-2A$pXY+X~?i1^R`>>1hI^ zvY3{NlSIDUu9B*hk!wT8saK}VJRdN#;8B~o{-|kto*9l&6AP8;`lfBdn`V!|gOxSU za%x_3!?BJyd`z=oSMcfU8_p771! z8%O54cm7SJ)Q)QdtysBhY?(E-G|tQ$eGe5_cph1FRv?QIPsd)qEA*KaPPvJX z7raVfvRMuS5B%L@`hn?F>X=~wAOzqcdcP!?9li|@y+|*PmuQMeKxaU9g)=ad1rd%H zFhLe|Ls*zm1vWK981l(THKhDplwdThFh5d<@h3rzQ&-_3V|!qC%@AT{-NfZMICUYz zPy;i8MVgRdeJH;UiULTt|3BIr@hr9%SW82^dudh{+G2?mzMj5aqWOH(k@{S&kzoOk^93IRh75P{L10%&Q IO[bytes]: + # We only want to write to this file, so open it in write only mode + flags = os.O_WRONLY + + # os.O_CREAT | os.O_EXCL will fail if the file already exists, so we only + # will open *new* files. + # We specify this because we want to ensure that the mode we pass is the + # mode of the file. + flags |= os.O_CREAT | os.O_EXCL + + # Do not follow symlinks to prevent someone from making a symlink that + # we follow and insecurely open a cache file. + if hasattr(os, "O_NOFOLLOW"): + flags |= os.O_NOFOLLOW + + # On Windows we'll mark this file as binary + if hasattr(os, "O_BINARY"): + flags |= os.O_BINARY + + # Before we open our file, we want to delete any existing file that is + # there + try: + os.remove(filename) + except OSError: + # The file must not exist already, so we can just skip ahead to opening + pass + + # Open our file, the use of os.O_CREAT | os.O_EXCL will ensure that if a + # race condition happens between the os.remove and this line, that an + # error will be raised. Because we utilize a lockfile this should only + # happen if someone is attempting to attack us. + fd = os.open(filename, flags, fmode) + try: + return os.fdopen(fd, "wb") + + except: + # An error occurred wrapping our FD in a file object + os.close(fd) + raise + + +class _FileCacheMixin: + """Shared implementation for both FileCache variants.""" + + def __init__( + self, + directory: str, + forever: bool = False, + filemode: int = 0o0600, + dirmode: int = 0o0700, + lock_class: type[BaseFileLock] | None = None, + ) -> None: + try: + if lock_class is None: + from filelock import FileLock + + lock_class = FileLock + except ImportError: + notice = dedent( + """ + NOTE: In order to use the FileCache you must have + filelock installed. You can install it via pip: + pip install filelock + """ + ) + raise ImportError(notice) + + self.directory = directory + self.forever = forever + self.filemode = filemode + self.dirmode = dirmode + self.lock_class = lock_class + + @staticmethod + def encode(x: str) -> str: + return hashlib.sha224(x.encode()).hexdigest() + + def _fn(self, name: str) -> str: + # NOTE: This method should not change as some may depend on it. + # See: https://github.com/ionrock/cachecontrol/issues/63 + hashed = self.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key: str) -> bytes | None: + name = self._fn(key) + try: + with open(name, "rb") as fh: + return fh.read() + + except FileNotFoundError: + return None + + def set( + self, key: str, value: bytes, expires: int | datetime | None = None + ) -> None: + name = self._fn(key) + self._write(name, value) + + def _write(self, path: str, data: bytes) -> None: + """ + Safely write the data to the given path. + """ + # Make sure the directory exists + try: + os.makedirs(os.path.dirname(path), self.dirmode) + except OSError: + pass + + with self.lock_class(path + ".lock"): + # Write our actual file + with _secure_open_write(path, self.filemode) as fh: + fh.write(data) + + def _delete(self, key: str, suffix: str) -> None: + name = self._fn(key) + suffix + if not self.forever: + try: + os.remove(name) + except FileNotFoundError: + pass + + +class FileCache(_FileCacheMixin, BaseCache): + """ + Traditional FileCache: body is stored in memory, so not suitable for large + downloads. + """ + + def delete(self, key: str) -> None: + self._delete(key, "") + + +class SeparateBodyFileCache(_FileCacheMixin, SeparateBodyBaseCache): + """ + Memory-efficient FileCache: body is stored in a separate file, reducing + peak memory usage. + """ + + def get_body(self, key: str) -> IO[bytes] | None: + name = self._fn(key) + ".body" + try: + return open(name, "rb") + except FileNotFoundError: + return None + + def set_body(self, key: str, body: bytes) -> None: + name = self._fn(key) + ".body" + self._write(name, body) + + def delete(self, key: str) -> None: + self._delete(key, "") + self._delete(key, ".body") + + +def url_to_file_path(url: str, filecache: FileCache) -> str: + """Return the file cache path based on the URL. + + This does not ensure the file exists! + """ + key = CacheController.cache_url(url) + return filecache._fn(key) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py new file mode 100644 index 0000000..f4f68c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + + +from datetime import datetime, timezone +from typing import TYPE_CHECKING + +from pip._vendor.cachecontrol.cache import BaseCache + +if TYPE_CHECKING: + from redis import Redis + + +class RedisCache(BaseCache): + def __init__(self, conn: Redis[bytes]) -> None: + self.conn = conn + + def get(self, key: str) -> bytes | None: + return self.conn.get(key) + + def set( + self, key: str, value: bytes, expires: int | datetime | None = None + ) -> None: + if not expires: + self.conn.set(key, value) + elif isinstance(expires, datetime): + now_utc = datetime.now(timezone.utc) + if expires.tzinfo is None: + now_utc = now_utc.replace(tzinfo=None) + delta = expires - now_utc + self.conn.setex(key, int(delta.total_seconds()), value) + else: + self.conn.setex(key, expires, value) + + def delete(self, key: str) -> None: + self.conn.delete(key) + + def clear(self) -> None: + """Helper for clearing all the keys in a database. Use with + caution!""" + for key in self.conn.keys(): + self.conn.delete(key) + + def close(self) -> None: + """Redis uses connection pooling, no need to close the connection.""" + pass diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py new file mode 100644 index 0000000..586b9f9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/controller.py @@ -0,0 +1,494 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 + +""" +The httplib2 algorithms ported for use with requests. +""" +from __future__ import annotations + +import calendar +import logging +import re +import time +from email.utils import parsedate_tz +from typing import TYPE_CHECKING, Collection, Mapping + +from pip._vendor.requests.structures import CaseInsensitiveDict + +from pip._vendor.cachecontrol.cache import DictCache, SeparateBodyBaseCache +from pip._vendor.cachecontrol.serialize import Serializer + +if TYPE_CHECKING: + from typing import Literal + + from pip._vendor.requests import PreparedRequest + from pip._vendor.urllib3 import HTTPResponse + + from pip._vendor.cachecontrol.cache import BaseCache + +logger = logging.getLogger(__name__) + +URI = re.compile(r"^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?") + +PERMANENT_REDIRECT_STATUSES = (301, 308) + + +def parse_uri(uri: str) -> tuple[str, str, str, str, str]: + """Parses a URI using the regex given in Appendix B of RFC 3986. + + (scheme, authority, path, query, fragment) = parse_uri(uri) + """ + match = URI.match(uri) + assert match is not None + groups = match.groups() + return (groups[1], groups[3], groups[4], groups[6], groups[8]) + + +class CacheController: + """An interface to see if request should cached or not.""" + + def __init__( + self, + cache: BaseCache | None = None, + cache_etags: bool = True, + serializer: Serializer | None = None, + status_codes: Collection[int] | None = None, + ): + self.cache = DictCache() if cache is None else cache + self.cache_etags = cache_etags + self.serializer = serializer or Serializer() + self.cacheable_status_codes = status_codes or (200, 203, 300, 301, 308) + + @classmethod + def _urlnorm(cls, uri: str) -> str: + """Normalize the URL to create a safe key for the cache""" + (scheme, authority, path, query, fragment) = parse_uri(uri) + if not scheme or not authority: + raise Exception("Only absolute URIs are allowed. uri = %s" % uri) + + scheme = scheme.lower() + authority = authority.lower() + + if not path: + path = "/" + + # Could do syntax based normalization of the URI before + # computing the digest. See Section 6.2.2 of Std 66. + request_uri = query and "?".join([path, query]) or path + defrag_uri = scheme + "://" + authority + request_uri + + return defrag_uri + + @classmethod + def cache_url(cls, uri: str) -> str: + return cls._urlnorm(uri) + + def parse_cache_control(self, headers: Mapping[str, str]) -> dict[str, int | None]: + known_directives = { + # https://tools.ietf.org/html/rfc7234#section-5.2 + "max-age": (int, True), + "max-stale": (int, False), + "min-fresh": (int, True), + "no-cache": (None, False), + "no-store": (None, False), + "no-transform": (None, False), + "only-if-cached": (None, False), + "must-revalidate": (None, False), + "public": (None, False), + "private": (None, False), + "proxy-revalidate": (None, False), + "s-maxage": (int, True), + } + + cc_headers = headers.get("cache-control", headers.get("Cache-Control", "")) + + retval: dict[str, int | None] = {} + + for cc_directive in cc_headers.split(","): + if not cc_directive.strip(): + continue + + parts = cc_directive.split("=", 1) + directive = parts[0].strip() + + try: + typ, required = known_directives[directive] + except KeyError: + logger.debug("Ignoring unknown cache-control directive: %s", directive) + continue + + if not typ or not required: + retval[directive] = None + if typ: + try: + retval[directive] = typ(parts[1].strip()) + except IndexError: + if required: + logger.debug( + "Missing value for cache-control " "directive: %s", + directive, + ) + except ValueError: + logger.debug( + "Invalid value for cache-control directive " "%s, must be %s", + directive, + typ.__name__, + ) + + return retval + + def _load_from_cache(self, request: PreparedRequest) -> HTTPResponse | None: + """ + Load a cached response, or return None if it's not available. + """ + cache_url = request.url + assert cache_url is not None + cache_data = self.cache.get(cache_url) + if cache_data is None: + logger.debug("No cache entry available") + return None + + if isinstance(self.cache, SeparateBodyBaseCache): + body_file = self.cache.get_body(cache_url) + else: + body_file = None + + result = self.serializer.loads(request, cache_data, body_file) + if result is None: + logger.warning("Cache entry deserialization failed, entry ignored") + return result + + def cached_request(self, request: PreparedRequest) -> HTTPResponse | Literal[False]: + """ + Return a cached response if it exists in the cache, otherwise + return False. + """ + assert request.url is not None + cache_url = self.cache_url(request.url) + logger.debug('Looking up "%s" in the cache', cache_url) + cc = self.parse_cache_control(request.headers) + + # Bail out if the request insists on fresh data + if "no-cache" in cc: + logger.debug('Request header has "no-cache", cache bypassed') + return False + + if "max-age" in cc and cc["max-age"] == 0: + logger.debug('Request header has "max_age" as 0, cache bypassed') + return False + + # Check whether we can load the response from the cache: + resp = self._load_from_cache(request) + if not resp: + return False + + # If we have a cached permanent redirect, return it immediately. We + # don't need to test our response for other headers b/c it is + # intrinsically "cacheable" as it is Permanent. + # + # See: + # https://tools.ietf.org/html/rfc7231#section-6.4.2 + # + # Client can try to refresh the value by repeating the request + # with cache busting headers as usual (ie no-cache). + if int(resp.status) in PERMANENT_REDIRECT_STATUSES: + msg = ( + "Returning cached permanent redirect response " + "(ignoring date and etag information)" + ) + logger.debug(msg) + return resp + + headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(resp.headers) + if not headers or "date" not in headers: + if "etag" not in headers: + # Without date or etag, the cached response can never be used + # and should be deleted. + logger.debug("Purging cached response: no date or etag") + self.cache.delete(cache_url) + logger.debug("Ignoring cached response: no date") + return False + + now = time.time() + time_tuple = parsedate_tz(headers["date"]) + assert time_tuple is not None + date = calendar.timegm(time_tuple[:6]) + current_age = max(0, now - date) + logger.debug("Current age based on date: %i", current_age) + + # TODO: There is an assumption that the result will be a + # urllib3 response object. This may not be best since we + # could probably avoid instantiating or constructing the + # response until we know we need it. + resp_cc = self.parse_cache_control(headers) + + # determine freshness + freshness_lifetime = 0 + + # Check the max-age pragma in the cache control header + max_age = resp_cc.get("max-age") + if max_age is not None: + freshness_lifetime = max_age + logger.debug("Freshness lifetime from max-age: %i", freshness_lifetime) + + # If there isn't a max-age, check for an expires header + elif "expires" in headers: + expires = parsedate_tz(headers["expires"]) + if expires is not None: + expire_time = calendar.timegm(expires[:6]) - date + freshness_lifetime = max(0, expire_time) + logger.debug("Freshness lifetime from expires: %i", freshness_lifetime) + + # Determine if we are setting freshness limit in the + # request. Note, this overrides what was in the response. + max_age = cc.get("max-age") + if max_age is not None: + freshness_lifetime = max_age + logger.debug( + "Freshness lifetime from request max-age: %i", freshness_lifetime + ) + + min_fresh = cc.get("min-fresh") + if min_fresh is not None: + # adjust our current age by our min fresh + current_age += min_fresh + logger.debug("Adjusted current age from min-fresh: %i", current_age) + + # Return entry if it is fresh enough + if freshness_lifetime > current_age: + logger.debug('The response is "fresh", returning cached response') + logger.debug("%i > %i", freshness_lifetime, current_age) + return resp + + # we're not fresh. If we don't have an Etag, clear it out + if "etag" not in headers: + logger.debug('The cached response is "stale" with no etag, purging') + self.cache.delete(cache_url) + + # return the original handler + return False + + def conditional_headers(self, request: PreparedRequest) -> dict[str, str]: + resp = self._load_from_cache(request) + new_headers = {} + + if resp: + headers: CaseInsensitiveDict[str] = CaseInsensitiveDict(resp.headers) + + if "etag" in headers: + new_headers["If-None-Match"] = headers["ETag"] + + if "last-modified" in headers: + new_headers["If-Modified-Since"] = headers["Last-Modified"] + + return new_headers + + def _cache_set( + self, + cache_url: str, + request: PreparedRequest, + response: HTTPResponse, + body: bytes | None = None, + expires_time: int | None = None, + ) -> None: + """ + Store the data in the cache. + """ + if isinstance(self.cache, SeparateBodyBaseCache): + # We pass in the body separately; just put a placeholder empty + # string in the metadata. + self.cache.set( + cache_url, + self.serializer.dumps(request, response, b""), + expires=expires_time, + ) + # body is None can happen when, for example, we're only updating + # headers, as is the case in update_cached_response(). + if body is not None: + self.cache.set_body(cache_url, body) + else: + self.cache.set( + cache_url, + self.serializer.dumps(request, response, body), + expires=expires_time, + ) + + def cache_response( + self, + request: PreparedRequest, + response: HTTPResponse, + body: bytes | None = None, + status_codes: Collection[int] | None = None, + ) -> None: + """ + Algorithm for caching requests. + + This assumes a requests Response object. + """ + # From httplib2: Don't cache 206's since we aren't going to + # handle byte range requests + cacheable_status_codes = status_codes or self.cacheable_status_codes + if response.status not in cacheable_status_codes: + logger.debug( + "Status code %s not in %s", response.status, cacheable_status_codes + ) + return + + response_headers: CaseInsensitiveDict[str] = CaseInsensitiveDict( + response.headers + ) + + if "date" in response_headers: + time_tuple = parsedate_tz(response_headers["date"]) + assert time_tuple is not None + date = calendar.timegm(time_tuple[:6]) + else: + date = 0 + + # If we've been given a body, our response has a Content-Length, that + # Content-Length is valid then we can check to see if the body we've + # been given matches the expected size, and if it doesn't we'll just + # skip trying to cache it. + if ( + body is not None + and "content-length" in response_headers + and response_headers["content-length"].isdigit() + and int(response_headers["content-length"]) != len(body) + ): + return + + cc_req = self.parse_cache_control(request.headers) + cc = self.parse_cache_control(response_headers) + + assert request.url is not None + cache_url = self.cache_url(request.url) + logger.debug('Updating cache with response from "%s"', cache_url) + + # Delete it from the cache if we happen to have it stored there + no_store = False + if "no-store" in cc: + no_store = True + logger.debug('Response header has "no-store"') + if "no-store" in cc_req: + no_store = True + logger.debug('Request header has "no-store"') + if no_store and self.cache.get(cache_url): + logger.debug('Purging existing cache entry to honor "no-store"') + self.cache.delete(cache_url) + if no_store: + return + + # https://tools.ietf.org/html/rfc7234#section-4.1: + # A Vary header field-value of "*" always fails to match. + # Storing such a response leads to a deserialization warning + # during cache lookup and is not allowed to ever be served, + # so storing it can be avoided. + if "*" in response_headers.get("vary", ""): + logger.debug('Response header has "Vary: *"') + return + + # If we've been given an etag, then keep the response + if self.cache_etags and "etag" in response_headers: + expires_time = 0 + if response_headers.get("expires"): + expires = parsedate_tz(response_headers["expires"]) + if expires is not None: + expires_time = calendar.timegm(expires[:6]) - date + + expires_time = max(expires_time, 14 * 86400) + + logger.debug(f"etag object cached for {expires_time} seconds") + logger.debug("Caching due to etag") + self._cache_set(cache_url, request, response, body, expires_time) + + # Add to the cache any permanent redirects. We do this before looking + # that the Date headers. + elif int(response.status) in PERMANENT_REDIRECT_STATUSES: + logger.debug("Caching permanent redirect") + self._cache_set(cache_url, request, response, b"") + + # Add to the cache if the response headers demand it. If there + # is no date header then we can't do anything about expiring + # the cache. + elif "date" in response_headers: + time_tuple = parsedate_tz(response_headers["date"]) + assert time_tuple is not None + date = calendar.timegm(time_tuple[:6]) + # cache when there is a max-age > 0 + max_age = cc.get("max-age") + if max_age is not None and max_age > 0: + logger.debug("Caching b/c date exists and max-age > 0") + expires_time = max_age + self._cache_set( + cache_url, + request, + response, + body, + expires_time, + ) + + # If the request can expire, it means we should cache it + # in the meantime. + elif "expires" in response_headers: + if response_headers["expires"]: + expires = parsedate_tz(response_headers["expires"]) + if expires is not None: + expires_time = calendar.timegm(expires[:6]) - date + else: + expires_time = None + + logger.debug( + "Caching b/c of expires header. expires in {} seconds".format( + expires_time + ) + ) + self._cache_set( + cache_url, + request, + response, + body, + expires_time, + ) + + def update_cached_response( + self, request: PreparedRequest, response: HTTPResponse + ) -> HTTPResponse: + """On a 304 we will get a new set of headers that we want to + update our cached value with, assuming we have one. + + This should only ever be called when we've sent an ETag and + gotten a 304 as the response. + """ + assert request.url is not None + cache_url = self.cache_url(request.url) + cached_response = self._load_from_cache(request) + + if not cached_response: + # we didn't have a cached response + return response + + # Lets update our headers with the headers from the new request: + # http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional-26#section-4.1 + # + # The server isn't supposed to send headers that would make + # the cached body invalid. But... just in case, we'll be sure + # to strip out ones we know that might be problmatic due to + # typical assumptions. + excluded_headers = ["content-length"] + + cached_response.headers.update( + { + k: v + for k, v in response.headers.items() # type: ignore[no-untyped-call] + if k.lower() not in excluded_headers + } + ) + + # we want a 200 b/c we have content via the cache + cached_response.status = 200 + + # update our cache + self._cache_set(cache_url, request, cached_response) + + return cached_response diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py new file mode 100644 index 0000000..2514390 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/filewrapper.py @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import mmap +from tempfile import NamedTemporaryFile +from typing import TYPE_CHECKING, Any, Callable + +if TYPE_CHECKING: + from http.client import HTTPResponse + + +class CallbackFileWrapper: + """ + Small wrapper around a fp object which will tee everything read into a + buffer, and when that file is closed it will execute a callback with the + contents of that buffer. + + All attributes are proxied to the underlying file object. + + This class uses members with a double underscore (__) leading prefix so as + not to accidentally shadow an attribute. + + The data is stored in a temporary file until it is all available. As long + as the temporary files directory is disk-based (sometimes it's a + memory-backed-``tmpfs`` on Linux), data will be unloaded to disk if memory + pressure is high. For small files the disk usually won't be used at all, + it'll all be in the filesystem memory cache, so there should be no + performance impact. + """ + + def __init__( + self, fp: HTTPResponse, callback: Callable[[bytes], None] | None + ) -> None: + self.__buf = NamedTemporaryFile("rb+", delete=True) + self.__fp = fp + self.__callback = callback + + def __getattr__(self, name: str) -> Any: + # The vaguaries of garbage collection means that self.__fp is + # not always set. By using __getattribute__ and the private + # name[0] allows looking up the attribute value and raising an + # AttributeError when it doesn't exist. This stop thigns from + # infinitely recursing calls to getattr in the case where + # self.__fp hasn't been set. + # + # [0] https://docs.python.org/2/reference/expressions.html#atom-identifiers + fp = self.__getattribute__("_CallbackFileWrapper__fp") + return getattr(fp, name) + + def __is_fp_closed(self) -> bool: + try: + return self.__fp.fp is None + + except AttributeError: + pass + + try: + closed: bool = self.__fp.closed + return closed + + except AttributeError: + pass + + # We just don't cache it then. + # TODO: Add some logging here... + return False + + def _close(self) -> None: + if self.__callback: + if self.__buf.tell() == 0: + # Empty file: + result = b"" + else: + # Return the data without actually loading it into memory, + # relying on Python's buffer API and mmap(). mmap() just gives + # a view directly into the filesystem's memory cache, so it + # doesn't result in duplicate memory use. + self.__buf.seek(0, 0) + result = memoryview( + mmap.mmap(self.__buf.fileno(), 0, access=mmap.ACCESS_READ) + ) + self.__callback(result) + + # We assign this to None here, because otherwise we can get into + # really tricky problems where the CPython interpreter dead locks + # because the callback is holding a reference to something which + # has a __del__ method. Setting this to None breaks the cycle + # and allows the garbage collector to do it's thing normally. + self.__callback = None + + # Closing the temporary file releases memory and frees disk space. + # Important when caching big files. + self.__buf.close() + + def read(self, amt: int | None = None) -> bytes: + data: bytes = self.__fp.read(amt) + if data: + # We may be dealing with b'', a sign that things are over: + # it's passed e.g. after we've already closed self.__buf. + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data + + def _safe_read(self, amt: int) -> bytes: + data: bytes = self.__fp._safe_read(amt) # type: ignore[attr-defined] + if amt == 2 and data == b"\r\n": + # urllib executes this read to toss the CRLF at the end + # of the chunk. + return data + + self.__buf.write(data) + if self.__is_fp_closed(): + self._close() + + return data diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py new file mode 100644 index 0000000..b9d72ca --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/heuristics.py @@ -0,0 +1,154 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import calendar +import time +from datetime import datetime, timedelta, timezone +from email.utils import formatdate, parsedate, parsedate_tz +from typing import TYPE_CHECKING, Any, Mapping + +if TYPE_CHECKING: + from pip._vendor.urllib3 import HTTPResponse + +TIME_FMT = "%a, %d %b %Y %H:%M:%S GMT" + + +def expire_after(delta: timedelta, date: datetime | None = None) -> datetime: + date = date or datetime.now(timezone.utc) + return date + delta + + +def datetime_to_header(dt: datetime) -> str: + return formatdate(calendar.timegm(dt.timetuple())) + + +class BaseHeuristic: + def warning(self, response: HTTPResponse) -> str | None: + """ + Return a valid 1xx warning header value describing the cache + adjustments. + + The response is provided too allow warnings like 113 + http://tools.ietf.org/html/rfc7234#section-5.5.4 where we need + to explicitly say response is over 24 hours old. + """ + return '110 - "Response is Stale"' + + def update_headers(self, response: HTTPResponse) -> dict[str, str]: + """Update the response headers with any new headers. + + NOTE: This SHOULD always include some Warning header to + signify that the response was cached by the client, not + by way of the provided headers. + """ + return {} + + def apply(self, response: HTTPResponse) -> HTTPResponse: + updated_headers = self.update_headers(response) + + if updated_headers: + response.headers.update(updated_headers) + warning_header_value = self.warning(response) + if warning_header_value is not None: + response.headers.update({"Warning": warning_header_value}) + + return response + + +class OneDayCache(BaseHeuristic): + """ + Cache the response by providing an expires 1 day in the + future. + """ + + def update_headers(self, response: HTTPResponse) -> dict[str, str]: + headers = {} + + if "expires" not in response.headers: + date = parsedate(response.headers["date"]) + expires = expire_after(timedelta(days=1), date=datetime(*date[:6], tzinfo=timezone.utc)) # type: ignore[misc] + headers["expires"] = datetime_to_header(expires) + headers["cache-control"] = "public" + return headers + + +class ExpiresAfter(BaseHeuristic): + """ + Cache **all** requests for a defined time period. + """ + + def __init__(self, **kw: Any) -> None: + self.delta = timedelta(**kw) + + def update_headers(self, response: HTTPResponse) -> dict[str, str]: + expires = expire_after(self.delta) + return {"expires": datetime_to_header(expires), "cache-control": "public"} + + def warning(self, response: HTTPResponse) -> str | None: + tmpl = "110 - Automatically cached for %s. Response might be stale" + return tmpl % self.delta + + +class LastModified(BaseHeuristic): + """ + If there is no Expires header already, fall back on Last-Modified + using the heuristic from + http://tools.ietf.org/html/rfc7234#section-4.2.2 + to calculate a reasonable value. + + Firefox also does something like this per + https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching_FAQ + http://lxr.mozilla.org/mozilla-release/source/netwerk/protocol/http/nsHttpResponseHead.cpp#397 + Unlike mozilla we limit this to 24-hr. + """ + + cacheable_by_default_statuses = { + 200, + 203, + 204, + 206, + 300, + 301, + 404, + 405, + 410, + 414, + 501, + } + + def update_headers(self, resp: HTTPResponse) -> dict[str, str]: + headers: Mapping[str, str] = resp.headers + + if "expires" in headers: + return {} + + if "cache-control" in headers and headers["cache-control"] != "public": + return {} + + if resp.status not in self.cacheable_by_default_statuses: + return {} + + if "date" not in headers or "last-modified" not in headers: + return {} + + time_tuple = parsedate_tz(headers["date"]) + assert time_tuple is not None + date = calendar.timegm(time_tuple[:6]) + last_modified = parsedate(headers["last-modified"]) + if last_modified is None: + return {} + + now = time.time() + current_age = max(0, now - date) + delta = date - calendar.timegm(last_modified) + freshness_lifetime = max(0, min(delta / 10, 24 * 3600)) + if freshness_lifetime <= current_age: + return {} + + expires = date + freshness_lifetime + return {"expires": time.strftime(TIME_FMT, time.gmtime(expires))} + + def warning(self, resp: HTTPResponse) -> str | None: + return None diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py new file mode 100644 index 0000000..f9e967c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/serialize.py @@ -0,0 +1,206 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import io +from typing import IO, TYPE_CHECKING, Any, Mapping, cast + +from pip._vendor import msgpack +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.urllib3 import HTTPResponse + +if TYPE_CHECKING: + from pip._vendor.requests import PreparedRequest + + +class Serializer: + serde_version = "4" + + def dumps( + self, + request: PreparedRequest, + response: HTTPResponse, + body: bytes | None = None, + ) -> bytes: + response_headers: CaseInsensitiveDict[str] = CaseInsensitiveDict( + response.headers + ) + + if body is None: + # When a body isn't passed in, we'll read the response. We + # also update the response with a new file handler to be + # sure it acts as though it was never read. + body = response.read(decode_content=False) + response._fp = io.BytesIO(body) # type: ignore[attr-defined] + response.length_remaining = len(body) + + data = { + "response": { + "body": body, # Empty bytestring if body is stored separately + "headers": {str(k): str(v) for k, v in response.headers.items()}, # type: ignore[no-untyped-call] + "status": response.status, + "version": response.version, + "reason": str(response.reason), + "decode_content": response.decode_content, + } + } + + # Construct our vary headers + data["vary"] = {} + if "vary" in response_headers: + varied_headers = response_headers["vary"].split(",") + for header in varied_headers: + header = str(header).strip() + header_value = request.headers.get(header, None) + if header_value is not None: + header_value = str(header_value) + data["vary"][header] = header_value + + return b",".join([f"cc={self.serde_version}".encode(), self.serialize(data)]) + + def serialize(self, data: dict[str, Any]) -> bytes: + return cast(bytes, msgpack.dumps(data, use_bin_type=True)) + + def loads( + self, + request: PreparedRequest, + data: bytes, + body_file: IO[bytes] | None = None, + ) -> HTTPResponse | None: + # Short circuit if we've been given an empty set of data + if not data: + return None + + # Determine what version of the serializer the data was serialized + # with + try: + ver, data = data.split(b",", 1) + except ValueError: + ver = b"cc=0" + + # Make sure that our "ver" is actually a version and isn't a false + # positive from a , being in the data stream. + if ver[:3] != b"cc=": + data = ver + data + ver = b"cc=0" + + # Get the version number out of the cc=N + verstr = ver.split(b"=", 1)[-1].decode("ascii") + + # Dispatch to the actual load method for the given version + try: + return getattr(self, f"_loads_v{verstr}")(request, data, body_file) # type: ignore[no-any-return] + + except AttributeError: + # This is a version we don't have a loads function for, so we'll + # just treat it as a miss and return None + return None + + def prepare_response( + self, + request: PreparedRequest, + cached: Mapping[str, Any], + body_file: IO[bytes] | None = None, + ) -> HTTPResponse | None: + """Verify our vary headers match and construct a real urllib3 + HTTPResponse object. + """ + # Special case the '*' Vary value as it means we cannot actually + # determine if the cached response is suitable for this request. + # This case is also handled in the controller code when creating + # a cache entry, but is left here for backwards compatibility. + if "*" in cached.get("vary", {}): + return None + + # Ensure that the Vary headers for the cached response match our + # request + for header, value in cached.get("vary", {}).items(): + if request.headers.get(header, None) != value: + return None + + body_raw = cached["response"].pop("body") + + headers: CaseInsensitiveDict[str] = CaseInsensitiveDict( + data=cached["response"]["headers"] + ) + if headers.get("transfer-encoding", "") == "chunked": + headers.pop("transfer-encoding") + + cached["response"]["headers"] = headers + + try: + body: IO[bytes] + if body_file is None: + body = io.BytesIO(body_raw) + else: + body = body_file + except TypeError: + # This can happen if cachecontrol serialized to v1 format (pickle) + # using Python 2. A Python 2 str(byte string) will be unpickled as + # a Python 3 str (unicode string), which will cause the above to + # fail with: + # + # TypeError: 'str' does not support the buffer interface + body = io.BytesIO(body_raw.encode("utf8")) + + # Discard any `strict` parameter serialized by older version of cachecontrol. + cached["response"].pop("strict", None) + + return HTTPResponse(body=body, preload_content=False, **cached["response"]) + + def _loads_v0( + self, + request: PreparedRequest, + data: bytes, + body_file: IO[bytes] | None = None, + ) -> None: + # The original legacy cache data. This doesn't contain enough + # information to construct everything we need, so we'll treat this as + # a miss. + return None + + def _loads_v1( + self, + request: PreparedRequest, + data: bytes, + body_file: IO[bytes] | None = None, + ) -> HTTPResponse | None: + # The "v1" pickled cache format. This is no longer supported + # for security reasons, so we treat it as a miss. + return None + + def _loads_v2( + self, + request: PreparedRequest, + data: bytes, + body_file: IO[bytes] | None = None, + ) -> HTTPResponse | None: + # The "v2" compressed base64 cache format. + # This has been removed due to age and poor size/performance + # characteristics, so we treat it as a miss. + return None + + def _loads_v3( + self, + request: PreparedRequest, + data: bytes, + body_file: IO[bytes] | None = None, + ) -> None: + # Due to Python 2 encoding issues, it's impossible to know for sure + # exactly how to load v3 entries, thus we'll treat these as a miss so + # that they get rewritten out as v4 entries. + return None + + def _loads_v4( + self, + request: PreparedRequest, + data: bytes, + body_file: IO[bytes] | None = None, + ) -> HTTPResponse | None: + try: + cached = msgpack.loads(data, raw=False) + except ValueError: + return None + + return self.prepare_response(request, cached, body_file) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py new file mode 100644 index 0000000..f618bc3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/cachecontrol/wrapper.py @@ -0,0 +1,43 @@ +# SPDX-FileCopyrightText: 2015 Eric Larson +# +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +from typing import TYPE_CHECKING, Collection + +from pip._vendor.cachecontrol.adapter import CacheControlAdapter +from pip._vendor.cachecontrol.cache import DictCache + +if TYPE_CHECKING: + from pip._vendor import requests + + from pip._vendor.cachecontrol.cache import BaseCache + from pip._vendor.cachecontrol.controller import CacheController + from pip._vendor.cachecontrol.heuristics import BaseHeuristic + from pip._vendor.cachecontrol.serialize import Serializer + + +def CacheControl( + sess: requests.Session, + cache: BaseCache | None = None, + cache_etags: bool = True, + serializer: Serializer | None = None, + heuristic: BaseHeuristic | None = None, + controller_class: type[CacheController] | None = None, + adapter_class: type[CacheControlAdapter] | None = None, + cacheable_methods: Collection[str] | None = None, +) -> requests.Session: + cache = DictCache() if cache is None else cache + adapter_class = adapter_class or CacheControlAdapter + adapter = adapter_class( + cache, + cache_etags=cache_etags, + serializer=serializer, + heuristic=heuristic, + controller_class=controller_class, + cacheable_methods=cacheable_methods, + ) + sess.mount("http://", adapter) + sess.mount("https://", adapter) + + return sess diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py new file mode 100644 index 0000000..8ce89ce --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__init__.py @@ -0,0 +1,4 @@ +from .core import contents, where + +__all__ = ["contents", "where"] +__version__ = "2023.07.22" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py new file mode 100644 index 0000000..0037634 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__main__.py @@ -0,0 +1,12 @@ +import argparse + +from pip._vendor.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/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10a65ed31ebe236f53d708a60357980aeb160625 GIT binary patch literal 340 zcmYjMJx{|h5Vf6@mPBPkNG!nEp}CR{EC>n23POw-u+rG1hQy9+(<+_dAF%No_$#n5 zqAXNmVngcI30DG%H@y3Jce;14-EM#!<}XBCx%jy%{y=_{#jZ=95JL$zO(#>*a%oM|nvS{V+H-yFJDLUYARb18gD8&Ad#%RL)PK2Y zQ<@e9rA17}L+>hz?4Y>dcd z=?p+L;c^0n7(rFrv691RKL#T#?^kJdo95g=B`R>YBvTqPuC2HhK&guX;5 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14edfd075ab1c66d399e032b2f24e49471a1f872 GIT binary patch literal 667 zcmZutO=}b}7*6JU2DcR#VezyGqASh@_1X%RUM}8nF#M!JnlOf4gwsHJtH@JCIkk+VBpn7%Q&fE&&KwkvE*x%nyHoAADxML#z3I*R%>+` z&--}T8(2%eMr=hn@YOcz%n3ZxAQ;0+GykV`+}}EJt7Awnp@qPO;L?8D#|-ZoVX_y5 z;W-^}K^cw&8{rO=PMiori%uZDm}Oj@3BkDg#KOrNF9Gn2;q0I`U{}B-+tAa`R%FSKJ>5dJIBD8 zf#wu6-)z3S_5Rrx&^!Wb$8KZhwx(|D(7m#6%}w{^`t$W~mjC+3!OgGM7(25q@uf_$P`hi4-N7iOEWKV>%XTJ8By@Z5r7zxSc=;aX%@`xN?Mxa zvhS@dOHm3Pa*&V$QjwxIiX43CMX(Ppdg-ypqP>uz0U28t5YPaDZ%kz%fKHuRaw*DI z3#5M4iRX(e-q3<^%C+g?0BuKCG1_Ngxn!I$q}9ELXqaEF6IP- z7JS~)1FlOq-zS6(!AiL(mE@dU^5uM#=tABP|A6i@f^&lI|3u1#T58TFP?)-9H4U41`v;JLKl z39FaJFI~Pqt4@#4USVi8LyS9tf@PK~d3(X>@>X1${kbD@n>j&M1wR{1Rkz6=8nIu= zE?6Za%k#XHFK4HjRWLMg_;u?;%QiKhy=s-`t+y|Zj&p9#mrJlan*p!fDhW~J~dg{mZ;Wu}NCu+kJe+}1%->o0JvHs4J#PQER+DVSpl4JGc_v?ut ztWSQaB&vY~LrsTbjq!A2eFFrDbt{qt%7W+hLObTLq}2|hT4>|In}faL1IgPBagwJP zRwRZ|cjP?ROmmw$0i�x>=r2i*7&|0>B9}Bd@Eraoc84yNGKri{ebcLqAJE_cZ+Z zDIja)i4v>!pROyf?kHn5W$fYmb>-rYGFek5t2cg8RVM4o2i3p_uBNoW@G?&kr;rR1 zhMygSjt2jBlR$EPQUu`;{?iBe#}ag3Xzs}ib{qLN?si)t=t&3se&NGf!%(^Lx(dP`q7UOWSRKkd4F6^6k#emXxYmpeb>w%;Py$!FES{N$ zxJ;Ti4aeU~6(_(9ZY?rMajfNz&tS}ATx&ZU1~IGMu-yis)8vDodG22o9#AiM-yHB2IXzdiZcEBCHE>K<(n z8caTnJ&q3Cx$v6{pS|&o2>owx@ZU)GIab!+^_&$S$`|}c!+RZ8bi%vPf-?-a+fjb8 z(bI18lKkV2!hEEbBT``%OHAv;8OX*dw?3q0N3b}18Pq#|-DKr_$#4Ry`(~-CBVo}_ zQ-{uRtW+&;VU2VhJr4p;0|AtUBbyT&6PwpIu6=%`9zMBFzf^iRmo}C*S2tGgj^ACa zD`%>KGcNcJx#!y=pM3{Ue=aH8xW^L0hLOBbdhr4-UNOHr;{e{EQ*g=H+m|o?bbJb4 zClw#CndS@j+)3@b{}QygL5I6{C96{M#S_~ZveG=UUF=`X5G-#G#QSI;G{_OMN{+(9 z|CJ-sltURC0L5;uFs$Qg%3cN58P>KF7jdGQ1W$t9zcxOd-{SXv_9%!+ASWORl<4O2 z#`32hK@!LbNCG(lNgyYhNpPpoz!Lc~ zXIMd<@GXOJC`77Rp0k{&S*lpfhF3h}RUm%w5y9NjjODsnpWR@%W+Q-E5(#E88wKLX z_HqS3xq!j5$3}20bk+SLoP`<3Ki&K;a01u8Vs|*$`+*-dKOo;^wrRQ-Aadj%QqMo7mv$r3 zt;;*H6Sdfhds;P?u0>9-O*~d&TQhe@tNpLklv8V08hvCa^*Da~>!4pwKJFcCh`@LI zjx}W9U_vzB@S_wUy|D(aX@p3qd#?+nxG0aJ>A)JHrVV>w`ZR?eO|5h2&{QKJ(8Sir KGXl&_gZ}^&S*Du+ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem new file mode 100644 index 0000000..0212369 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/cacert.pem @@ -0,0 +1,4635 @@ + +# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA +# Label: "GlobalSign Root CA" +# Serial: 4835703278459707669005204 +# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a +# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c +# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99 +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Subject: CN=Entrust.net Certification Authority (2048) O=Entrust.net OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.)/(c) 1999 Entrust.net Limited +# Label: "Entrust.net Premium 2048 Secure Server CA" +# Serial: 946069240 +# MD5 Fingerprint: ee:29:31:bc:32:7e:9a:e6:e8:b5:f7:51:b4:34:71:90 +# SHA1 Fingerprint: 50:30:06:09:1d:97:d4:f5:ae:39:f7:cb:e7:92:7d:7d:65:2d:34:31 +# SHA256 Fingerprint: 6d:c4:71:72:e0:1c:bc:b0:bf:62:58:0d:89:5f:e2:b8:ac:9a:d4:f8:73:80:1e:0c:10:b9:c8:37:d2:1e:b1:77 +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML +RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp +bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 +IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 +MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 +LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp +YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG +A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq +K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe +sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX +MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT +XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ +HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH +4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub +j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo +U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b +u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ +bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er +fF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +# Issuer: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Subject: CN=Baltimore CyberTrust Root O=Baltimore OU=CyberTrust +# Label: "Baltimore CyberTrust Root" +# Serial: 33554617 +# MD5 Fingerprint: ac:b6:94:a5:9c:17:e0:d7:91:52:9b:b1:97:06:a6:e4 +# SHA1 Fingerprint: d4:de:20:d0:5e:66:fc:53:fe:1a:50:88:2c:78:db:28:52:ca:e4:74 +# SHA256 Fingerprint: 16:af:57:a9:f6:76:b0:ab:12:60:95:aa:5e:ba:de:f2:2a:b3:11:19:d6:44:ac:95:cd:4b:93:db:f3:f2:6a:eb +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Subject: CN=Entrust Root Certification Authority O=Entrust, Inc. OU=www.entrust.net/CPS is incorporated by reference/(c) 2006 Entrust, Inc. +# Label: "Entrust Root Certification Authority" +# Serial: 1164660820 +# MD5 Fingerprint: d6:a5:c3:ed:5d:dd:3e:00:c1:3d:87:92:1f:1d:3f:e4 +# SHA1 Fingerprint: b3:1e:b1:b7:40:e3:6c:84:02:da:dc:37:d4:4d:f5:d4:67:49:52:f9 +# SHA256 Fingerprint: 73:c1:76:43:4f:1b:c6:d5:ad:f4:5b:0e:76:e7:27:28:7c:8d:e5:76:16:c1:e6:e6:14:1a:2b:2c:bc:7d:8e:4c +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 +Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW +KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw +NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw +NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy +ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV +BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo +Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 +4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 +KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI +rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi +94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB +sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi +gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo +kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE +vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t +O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua +AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP +9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ +eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m +0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +# Issuer: CN=AAA Certificate Services O=Comodo CA Limited +# Subject: CN=AAA Certificate Services O=Comodo CA Limited +# Label: "Comodo AAA Services root" +# Serial: 1 +# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0 +# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49 +# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4 +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb +MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow +GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj +YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM +GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua +BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe +3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 +YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR +rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm +ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU +oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v +QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t +b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF +AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q +GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 +G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi +l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 +smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2" +# Serial: 1289 +# MD5 Fingerprint: 5e:39:7b:dd:f8:ba:ec:82:e9:ac:62:ba:0c:54:00:2b +# SHA1 Fingerprint: ca:3a:fb:cf:12:40:36:4b:44:b2:16:20:88:80:48:39:19:93:7c:f7 +# SHA256 Fingerprint: 85:a0:dd:7d:d7:20:ad:b7:ff:05:f8:3d:54:2b:20:9d:c7:ff:45:28:f7:d6:77:b1:83:89:fe:a5:e5:c4:9e:86 +-----BEGIN CERTIFICATE----- +MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa +GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg +Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J +WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB +rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp ++ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 +ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i +Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz +PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og +/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH +oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI +yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud +EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 +A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL +MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT +ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f +BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn +g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl +fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K +WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha +B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc +hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR +TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD +mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z +ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y +4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza +8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3" +# Serial: 1478 +# MD5 Fingerprint: 31:85:3c:62:94:97:63:b9:aa:fd:89:4e:af:6f:e0:cf +# SHA1 Fingerprint: 1f:49:14:f7:d8:74:95:1d:dd:ae:02:c0:be:fd:3a:2d:82:75:51:85 +# SHA256 Fingerprint: 18:f1:fc:7f:20:5d:f8:ad:dd:eb:7f:e0:07:dd:57:e3:af:37:5a:9c:4d:8d:73:54:6b:f4:f1:fe:d1:e1:8d:35 +-----BEGIN CERTIFICATE----- +MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x +GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv +b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV +BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W +YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM +V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB +4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr +H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd +8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv +vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT +mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe +btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc +T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt +WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ +c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A +4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD +VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG +CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 +aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 +aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu +dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw +czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G +A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC +TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg +Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 +7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem +d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd ++LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B +4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN +t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x +DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 +k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s +zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j +Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT +mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK +4SVhM7JZG+Ju1zdXtg2pEto= +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust.net OU=Security Communication RootCA1 +# Subject: O=SECOM Trust.net OU=Security Communication RootCA1 +# Label: "Security Communication Root CA" +# Serial: 0 +# MD5 Fingerprint: f1:bc:63:6a:54:e0:b5:27:f5:cd:e7:1a:e3:4d:6e:4a +# SHA1 Fingerprint: 36:b1:2b:49:f9:81:9e:d7:4c:9e:bc:38:0f:c6:56:8f:5d:ac:b2:f7 +# SHA256 Fingerprint: e7:5e:72:ed:9f:56:0e:ec:6e:b4:80:00:73:a4:3f:c3:ad:19:19:5a:39:22:82:01:78:95:97:4a:99:02:6b:6c +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY +MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t +dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 +WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD +VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 +9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ +DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 +Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N +QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ +xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G +A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG +kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr +Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 +Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU +JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot +RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== +-----END CERTIFICATE----- + +# Issuer: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Subject: CN=XRamp Global Certification Authority O=XRamp Security Services Inc OU=www.xrampsecurity.com +# Label: "XRamp Global CA Root" +# Serial: 107108908803651509692980124233745014957 +# MD5 Fingerprint: a1:0b:44:b3:ca:10:d8:00:6e:9d:0f:d8:0f:92:0a:d1 +# SHA1 Fingerprint: b8:01:86:d1:eb:9c:86:a5:41:04:cf:30:54:f3:4c:52:b7:e5:58:c6 +# SHA256 Fingerprint: ce:cd:dc:90:50:99:d8:da:df:c5:b1:d2:09:b7:37:cb:e2:c1:8c:fb:2c:10:c0:ff:0b:cf:0d:32:86:fc:1a:a2 +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB +gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk +MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY +UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx +NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 +dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy +dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 +38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP +KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q +DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 +qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa +JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi +PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P +BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs +jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 +eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR +vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa +IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy +i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ +O+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority +# Label: "Go Daddy Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67 +# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4 +# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4 +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh +MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE +YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 +MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo +ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg +MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN +ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA +PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w +wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi +EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY +avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ +YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE +sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h +/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 +IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD +ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy +OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P +TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER +dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf +ReYNnyicsbkqWletNw+vHX/bvZ8= +-----END CERTIFICATE----- + +# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority +# Label: "Starfield Class 2 CA" +# Serial: 0 +# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24 +# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a +# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58 +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl +MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp +U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw +NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE +ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp +ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 +DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf +8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN ++lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 +X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa +K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA +1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G +A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR +zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 +YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD +bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w +DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 +L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D +eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp +VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY +WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root CA" +# Serial: 17154717934120587862167794914071425081 +# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72 +# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43 +# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c +JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP +mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ +wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 +VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ +AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB +AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun +pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC +dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf +fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm +NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx +H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root CA" +# Serial: 10944719598952040374951832963794454346 +# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e +# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36 +# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61 +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD +QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB +CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 +nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt +43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P +T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 +gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR +TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw +DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr +hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg +06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF +PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls +YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert High Assurance EV Root CA" +# Serial: 3553400076410547919724730734378100087 +# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a +# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25 +# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Gold CA - G2 O=SwissSign AG +# Label: "SwissSign Gold CA - G2" +# Serial: 13492815561806991280 +# MD5 Fingerprint: 24:77:d9:a8:91:d1:3b:fa:88:2d:c2:ff:f8:cd:33:93 +# SHA1 Fingerprint: d8:c5:38:8a:b7:30:1b:1b:6e:d4:7a:e6:45:25:3a:6f:9f:1a:27:61 +# SHA256 Fingerprint: 62:dd:0b:e9:b9:f5:0a:16:3e:a0:f8:e7:5c:05:3b:1e:ca:57:ea:55:c8:68:8f:64:7c:68:81:f2:c8:35:7b:95 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Subject: CN=SwissSign Silver CA - G2 O=SwissSign AG +# Label: "SwissSign Silver CA - G2" +# Serial: 5700383053117599563 +# MD5 Fingerprint: e0:06:a1:c9:7d:cf:c9:fc:0d:c0:56:75:96:d8:62:13 +# SHA1 Fingerprint: 9b:aa:e5:9f:56:ee:21:cb:43:5a:be:25:93:df:a7:f0:40:d1:1d:cb +# SHA256 Fingerprint: be:6c:4d:a2:bb:b9:ba:59:b6:f3:93:97:68:37:42:46:c3:c0:05:99:3f:a9:8f:02:0d:1d:ed:be:d4:8a:81:d5 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=SecureTrust CA O=SecureTrust Corporation +# Subject: CN=SecureTrust CA O=SecureTrust Corporation +# Label: "SecureTrust CA" +# Serial: 17199774589125277788362757014266862032 +# MD5 Fingerprint: dc:32:c3:a7:6d:25:57:c7:68:09:9d:ea:2d:a9:a2:d1 +# SHA1 Fingerprint: 87:82:c6:c3:04:35:3b:cf:d2:96:92:d2:59:3e:7d:44:d9:34:ff:11 +# SHA256 Fingerprint: f1:c1:b5:0a:e5:a2:0d:d8:03:0e:c9:f6:bc:24:82:3d:d3:67:b5:25:57:59:b4:e7:1b:61:fc:e9:f7:37:5d:73 +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz +MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv +cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz +Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO +0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao +wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj +7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS +8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT +BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg +JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 +6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ +3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm +D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS +CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +# Issuer: CN=Secure Global CA O=SecureTrust Corporation +# Subject: CN=Secure Global CA O=SecureTrust Corporation +# Label: "Secure Global CA" +# Serial: 9751836167731051554232119481456978597 +# MD5 Fingerprint: cf:f4:27:0d:d4:ed:dc:65:16:49:6d:3d:da:bf:6e:de +# SHA1 Fingerprint: 3a:44:73:5a:e5:81:90:1f:24:86:61:46:1e:3b:9c:c4:5f:f5:3a:1b +# SHA256 Fingerprint: 42:00:f5:04:3a:c8:59:0e:bb:52:7d:20:9e:d1:50:30:29:fb:cb:d4:1c:a1:b5:06:ec:27:f1:5a:de:7d:ac:69 +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK +MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x +GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx +MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg +Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ +iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa +/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ +jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI +HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 +sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w +gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw +KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG +AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L +URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO +H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm +I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY +iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO Certification Authority O=COMODO CA Limited +# Label: "COMODO Certification Authority" +# Serial: 104350513648249232941998508985834464573 +# MD5 Fingerprint: 5c:48:dc:f7:42:72:ec:56:94:6d:1c:cc:71:35:80:75 +# SHA1 Fingerprint: 66:31:bf:9e:f7:4f:9e:b6:c9:d5:a6:0c:ba:6a:be:d1:f7:bd:ef:7b +# SHA256 Fingerprint: 0c:2c:d6:3d:f7:80:6f:a3:99:ed:e8:09:11:6b:57:5b:f8:79:89:f0:65:18:f9:80:8c:86:05:03:17:8b:af:66 +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB +gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV +BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw +MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl +YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P +RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 +UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI +2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 +Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp ++2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ +DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O +nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW +/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g +PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u +QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY +SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv +IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 +zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd +BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB +ZQ== +-----END CERTIFICATE----- + +# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited +# Label: "COMODO ECC Certification Authority" +# Serial: 41578283867086692638256921589707938090 +# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23 +# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11 +# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7 +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL +MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE +BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT +IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw +MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy +ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N +T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR +FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J +cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW +BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm +fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv +GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +# Issuer: CN=Certigna O=Dhimyotis +# Subject: CN=Certigna O=Dhimyotis +# Label: "Certigna" +# Serial: 18364802974209362175 +# MD5 Fingerprint: ab:57:a6:5b:7d:42:82:19:b5:d8:58:26:28:5e:fd:ff +# SHA1 Fingerprint: b1:2e:13:63:45:86:a4:6f:1a:b2:60:68:37:58:2d:c4:ac:fd:94:97 +# SHA256 Fingerprint: e3:b6:a2:db:2e:d7:ce:48:84:2f:7a:c5:32:41:c7:b7:1d:54:14:4b:fb:40:c1:1f:3f:1d:0b:42:f5:ee:a1:2d +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV +BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X +DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ +BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 +QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny +gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw +zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q +130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 +JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw +DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw +ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT +AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj +AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG +9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h +bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc +fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu +HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w +t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +# Issuer: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Subject: O=Chunghwa Telecom Co., Ltd. OU=ePKI Root Certification Authority +# Label: "ePKI Root Certification Authority" +# Serial: 28956088682735189655030529057352760477 +# MD5 Fingerprint: 1b:2e:00:ca:26:06:90:3d:ad:fe:6f:15:68:d3:6b:b3 +# SHA1 Fingerprint: 67:65:0d:f1:7e:8e:7e:5b:82:40:a4:f4:56:4b:cf:e2:3d:69:c6:f0 +# SHA256 Fingerprint: c0:a6:f4:dc:63:a2:4b:fd:cf:54:ef:2a:6a:08:2a:0a:72:de:35:80:3e:2f:f5:ff:52:7a:e5:d8:72:06:df:d5 +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe +Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw +IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL +SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH +SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh +ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X +DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 +TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ +fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA +sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU +WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS +nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH +dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip +NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC +AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF +MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB +uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl +PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP +JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ +gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 +j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 +5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB +o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS +/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z +Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE +W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D +hNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +# Issuer: O=certSIGN OU=certSIGN ROOT CA +# Subject: O=certSIGN OU=certSIGN ROOT CA +# Label: "certSIGN ROOT CA" +# Serial: 35210227249154 +# MD5 Fingerprint: 18:98:c0:d6:e9:3a:fc:f9:b0:f5:0c:f7:4b:01:44:17 +# SHA1 Fingerprint: fa:b7:ee:36:97:26:62:fb:2d:b0:2a:f6:bf:03:fd:e8:7c:4b:2f:9b +# SHA256 Fingerprint: ea:a9:62:c4:fa:4a:6b:af:eb:e4:15:19:6d:35:1c:cd:88:8d:4f:53:f3:fa:8a:e6:d7:c4:66:a9:4e:60:42:bb +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT +AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD +QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP +MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do +0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ +UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d +RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ +OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv +JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C +AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O +BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ +LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY +MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ +44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I +Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw +i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN +9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +# Issuer: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Subject: CN=NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny O=NetLock Kft. OU=Tan\xfas\xedtv\xe1nykiad\xf3k (Certification Services) +# Label: "NetLock Arany (Class Gold) F\u0151tan\xfas\xedtv\xe1ny" +# Serial: 80544274841616 +# MD5 Fingerprint: c5:a1:b7:ff:73:dd:d6:d7:34:32:18:df:fc:3c:ad:88 +# SHA1 Fingerprint: 06:08:3f:59:3f:15:a1:04:a0:69:a4:6b:a9:03:d0:06:b7:97:09:91 +# SHA256 Fingerprint: 6c:61:da:c3:a2:de:f0:31:50:6b:e0:36:d2:a6:fe:40:19:94:fb:d1:3d:f9:c8:d4:66:59:92:74:c4:46:ec:98 +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG +EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 +MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl +cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR +dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB +pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM +b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm +aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz +IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT +lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz +AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 +VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG +ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 +BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG +AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M +U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh +bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C ++C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F +uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 +XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +# Issuer: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Subject: CN=SecureSign RootCA11 O=Japan Certification Services, Inc. +# Label: "SecureSign RootCA11" +# Serial: 1 +# MD5 Fingerprint: b7:52:74:e2:92:b4:80:93:f2:75:e4:cc:d7:f2:ea:26 +# SHA1 Fingerprint: 3b:c4:9f:48:f8:f3:73:a0:9c:1e:bd:f8:5b:b1:c3:65:c7:d8:11:b3 +# SHA256 Fingerprint: bf:0f:ee:fb:9e:3a:58:1a:d5:f9:e9:db:75:89:98:57:43:d2:61:08:5c:4d:31:4f:6f:5d:72:59:aa:42:16:12 +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr +MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG +A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 +MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp +Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD +QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz +i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 +h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV +MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 +UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni +8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC +h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD +VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm +KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ +X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr +QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 +pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN +QSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +# Issuer: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Subject: CN=Microsec e-Szigno Root CA 2009 O=Microsec Ltd. +# Label: "Microsec e-Szigno Root CA 2009" +# Serial: 14014712776195784473 +# MD5 Fingerprint: f8:49:f4:03:bc:44:2d:83:be:48:69:7d:29:64:fc:b1 +# SHA1 Fingerprint: 89:df:74:fe:5c:f4:0f:4a:80:f9:e3:37:7d:54:da:91:e1:01:31:8e +# SHA256 Fingerprint: 3c:5f:81:fe:a5:fa:b8:2c:64:bf:a2:ea:ec:af:cd:e8:e0:77:fc:86:20:a7:ca:e5:37:16:3d:f3:6e:db:f3:78 +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD +VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 +ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G +CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y +OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx +FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp +Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP +kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc +cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U +fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 +N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC +xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 ++rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM +Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG +SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h +mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk +ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c +2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t +HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3 +# Label: "GlobalSign Root CA - R3" +# Serial: 4835703278459759426209954 +# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28 +# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad +# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 +MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 +RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT +gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm +KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd +QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ +XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw +DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o +LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU +RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp +jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK +6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX +mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs +Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH +WD9f +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 6047274297262753887 +# MD5 Fingerprint: 73:3a:74:7a:ec:bb:a3:96:a6:c2:e4:e2:c8:9b:c0:c3 +# SHA1 Fingerprint: ae:c5:fb:3f:c8:e1:bf:c4:e5:4f:03:07:5a:9a:e8:00:b7:f7:b6:fa +# SHA256 Fingerprint: 04:04:80:28:bf:1f:28:64:d4:8f:9a:d4:d8:32:94:36:6a:82:88:56:55:3f:3b:14:30:3f:90:14:7f:5d:40:ef +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy +MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD +VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv +ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl +AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF +661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 +am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 +ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 +PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS +3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k +SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF +3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM +ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g +StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz +Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB +jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +# Issuer: CN=Izenpe.com O=IZENPE S.A. +# Subject: CN=Izenpe.com O=IZENPE S.A. +# Label: "Izenpe.com" +# Serial: 917563065490389241595536686991402621 +# MD5 Fingerprint: a6:b0:cd:85:80:da:5c:50:34:a3:39:90:2f:55:67:73 +# SHA1 Fingerprint: 2f:78:3d:25:52:18:a7:4a:65:39:71:b5:2c:a2:9c:45:15:6f:e9:19 +# SHA256 Fingerprint: 25:30:cc:8e:98:32:15:02:ba:d9:6f:9b:1f:ba:1b:09:9e:2d:29:9e:0f:45:48:bb:91:4f:36:3b:c0:d4:53:1f +-----BEGIN CERTIFICATE----- +MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 +MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 +ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD +VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq +scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO +xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H +LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX +uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD +yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ +JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q +rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN +BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L +hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB +QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ +HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu +Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg +QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB +BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx +MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA +A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb +laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 +awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo +JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw +LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT +VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk +LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb +UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ +QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ +naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls +QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== +-----END CERTIFICATE----- + +# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc. +# Label: "Go Daddy Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01 +# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b +# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT +EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp +ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz +NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH +EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE +AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD +E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH +/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy +DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh +GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR +tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA +AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX +WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu +9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr +gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo +2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI +4uJEvlz36hz1 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96 +# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e +# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5 +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs +ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw +MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj +aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp +Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg +nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 +HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N +Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN +dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 +HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G +CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU +sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 +4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg +8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 +mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +# Issuer: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Subject: CN=Starfield Services Root Certificate Authority - G2 O=Starfield Technologies, Inc. +# Label: "Starfield Services Root Certificate Authority - G2" +# Serial: 0 +# MD5 Fingerprint: 17:35:74:af:7b:61:1c:eb:f4:f9:3c:e2:ee:40:f9:a2 +# SHA1 Fingerprint: 92:5a:8f:8d:2c:6d:04:e0:66:5f:59:6a:ff:22:d8:63:e8:25:6f:3f +# SHA256 Fingerprint: 56:8d:69:05:a2:c8:87:08:a4:b3:02:51:90:ed:cf:ed:b1:97:4a:60:6a:13:c6:e5:29:0f:cb:2a:e6:3e:da:b5 +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx +EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT +HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs +ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD +VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy +ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy +dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p +OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 +8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K +Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe +hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk +6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw +DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q +AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI +bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB +ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z +qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn +0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN +sSi6 +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Commercial O=AffirmTrust +# Subject: CN=AffirmTrust Commercial O=AffirmTrust +# Label: "AffirmTrust Commercial" +# Serial: 8608355977964138876 +# MD5 Fingerprint: 82:92:ba:5b:ef:cd:8a:6f:a6:3d:55:f9:84:f6:d6:b7 +# SHA1 Fingerprint: f9:b5:b6:32:45:5f:9c:be:ec:57:5f:80:dc:e9:6e:2c:c7:b2:78:b7 +# SHA256 Fingerprint: 03:76:ab:1d:54:c5:f9:80:3c:e4:b2:e2:01:a0:ee:7e:ef:7b:57:b6:36:e8:a9:3c:9b:8d:48:60:c9:6f:5f:a7 +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP +Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr +ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL +MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 +yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr +VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ +nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG +XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj +vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt +Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g +N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC +nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Networking O=AffirmTrust +# Subject: CN=AffirmTrust Networking O=AffirmTrust +# Label: "AffirmTrust Networking" +# Serial: 8957382827206547757 +# MD5 Fingerprint: 42:65:ca:be:01:9a:9a:4c:a9:8c:41:49:cd:c0:d5:7f +# SHA1 Fingerprint: 29:36:21:02:8b:20:ed:02:f5:66:c5:32:d1:d6:ed:90:9f:45:00:2f +# SHA256 Fingerprint: 0a:81:ec:5a:92:97:77:f1:45:90:4a:f3:8d:5d:50:9f:66:b5:e2:c5:8f:cd:b5:31:05:8b:0e:17:f3:f0:b4:1b +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz +dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL +MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp +cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y +YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua +kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL +QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp +6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG +yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i +QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ +KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO +tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu +QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ +Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u +olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 +x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium O=AffirmTrust +# Subject: CN=AffirmTrust Premium O=AffirmTrust +# Label: "AffirmTrust Premium" +# Serial: 7893706540734352110 +# MD5 Fingerprint: c4:5d:0e:48:b6:ac:28:30:4e:0a:bc:f9:38:16:87:57 +# SHA1 Fingerprint: d8:a6:33:2c:e0:03:6f:b1:85:f6:63:4f:7d:6a:06:65:26:32:28:27 +# SHA256 Fingerprint: 70:a7:3f:7f:37:6b:60:07:42:48:90:45:34:b1:14:82:d5:bf:0e:69:8e:cc:49:8d:f5:25:77:eb:f2:e9:3b:9a +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE +BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz +dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG +A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U +cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf +qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ +JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ ++jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS +s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 +HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 +70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG +V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S +qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S +5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia +C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX +OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE +FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 +KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B +8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ +MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc +0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ +u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF +u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH +YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 +GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO +RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e +KeC2uAloGRwYQw== +-----END CERTIFICATE----- + +# Issuer: CN=AffirmTrust Premium ECC O=AffirmTrust +# Subject: CN=AffirmTrust Premium ECC O=AffirmTrust +# Label: "AffirmTrust Premium ECC" +# Serial: 8401224907861490260 +# MD5 Fingerprint: 64:b0:09:55:cf:b1:d5:99:e2:be:13:ab:a6:5d:ea:4d +# SHA1 Fingerprint: b8:23:6b:00:2f:1d:16:86:53:01:55:6c:11:a4:37:ca:eb:ff:c3:bb +# SHA256 Fingerprint: bd:71:fd:f6:da:97:e4:cf:62:d1:64:7a:dd:25:81:b0:7d:79:ad:f8:39:7e:b4:ec:ba:9c:5e:84:88:82:14:23 +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC +VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ +cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ +BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt +VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D +0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 +ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G +A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G +A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs +aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I +flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA" +# Serial: 279744 +# MD5 Fingerprint: d5:e9:81:40:c5:18:69:fc:46:2c:89:75:62:0f:aa:78 +# SHA1 Fingerprint: 07:e0:32:e0:20:b7:2c:3f:19:2f:06:28:a2:59:3a:19:a7:0f:06:9e +# SHA256 Fingerprint: 5c:58:46:8d:55:f5:8e:49:7e:74:39:82:d2:b5:00:10:b6:d1:65:37:4a:cf:83:a7:d4:a3:2d:b7:68:c4:40:8e +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM +MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D +ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU +cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 +WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg +Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw +IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH +UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM +TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU +BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM +kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x +AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV +HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y +sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL +I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 +J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY +VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Root Certification Authority O=TAIWAN-CA OU=Root CA +# Label: "TWCA Root Certification Authority" +# Serial: 1 +# MD5 Fingerprint: aa:08:8f:f6:f9:7b:b7:f2:b1:a7:1e:9b:ea:ea:bd:79 +# SHA1 Fingerprint: cf:9e:87:6d:d3:eb:fc:42:26:97:a3:b5:a3:7a:a0:76:a9:06:23:48 +# SHA256 Fingerprint: bf:d8:8f:e1:10:1c:41:ae:3e:80:1b:f8:be:56:35:0e:e9:ba:d1:a6:b9:bd:51:5e:dc:5c:6d:5b:87:11:ac:44 +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES +MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU +V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz +WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO +LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE +AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH +K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX +RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z +rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx +3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq +hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC +MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls +XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D +lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn +aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ +YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +# Issuer: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Subject: O=SECOM Trust Systems CO.,LTD. OU=Security Communication RootCA2 +# Label: "Security Communication RootCA2" +# Serial: 0 +# MD5 Fingerprint: 6c:39:7d:a4:0e:55:59:b2:3f:d6:41:b1:12:50:de:43 +# SHA1 Fingerprint: 5f:3b:8c:f2:f8:10:b3:7d:78:b4:ce:ec:19:19:c3:73:34:b9:c7:74 +# SHA256 Fingerprint: 51:3b:2c:ec:b8:10:d4:cd:e5:dd:85:39:1a:df:c6:c2:dd:60:d8:7b:b7:36:d2:b5:21:48:4a:a4:7a:0e:be:f6 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl +MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe +U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX +DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy +dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj +YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV +OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr +zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM +VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ +hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO +ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw +awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs +OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF +coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc +okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 +t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy +1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ +SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +# Issuer: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Subject: CN=Actalis Authentication Root CA O=Actalis S.p.A./03358520967 +# Label: "Actalis Authentication Root CA" +# Serial: 6271844772424770508 +# MD5 Fingerprint: 69:c1:0d:4f:07:a3:1b:c3:fe:56:3d:04:bc:11:f6:a6 +# SHA1 Fingerprint: f3:73:b3:87:06:5a:28:84:8a:f2:f3:4a:ce:19:2b:dd:c7:8e:9c:ac +# SHA256 Fingerprint: 55:92:60:84:ec:96:3a:64:b9:6e:2a:be:01:ce:0b:a8:6a:64:fb:fe:bc:c7:aa:b5:af:c1:55:b3:7f:d7:60:66 +-----BEGIN CERTIFICATE----- +MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE +BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w +MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 +IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC +SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 +ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv +UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX +4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 +KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ +gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb +rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ +51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F +be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe +KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F +v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn +fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 +jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz +ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt +ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL +e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 +jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz +WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V +SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j +pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX +X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok +fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R +K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU +ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU +LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT +LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 2 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 2 Root CA" +# Serial: 2 +# MD5 Fingerprint: 46:a7:d2:fe:45:fb:64:5a:a8:59:90:9b:78:44:9b:29 +# SHA1 Fingerprint: 49:0a:75:74:de:87:0a:47:fe:58:ee:f6:c7:6b:eb:c6:0b:12:40:99 +# SHA256 Fingerprint: 9a:11:40:25:19:7c:5b:b9:5d:94:e6:3d:55:cd:43:79:08:47:b6:46:b2:3c:df:11:ad:a4:a0:0e:ff:15:fb:48 +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr +6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV +L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 +1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx +MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ +QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB +arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr +Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi +FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS +P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN +9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz +uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h +9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t +OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo ++fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 +KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 +DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us +H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ +I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 +5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h +3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz +Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= +-----END CERTIFICATE----- + +# Issuer: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Subject: CN=Buypass Class 3 Root CA O=Buypass AS-983163327 +# Label: "Buypass Class 3 Root CA" +# Serial: 2 +# MD5 Fingerprint: 3d:3b:18:9e:2c:64:5a:e8:d5:88:ce:0e:f9:37:c2:ec +# SHA1 Fingerprint: da:fa:f7:fa:66:84:ec:06:8f:14:50:bd:c7:c2:81:a5:bc:a9:64:57 +# SHA256 Fingerprint: ed:f7:eb:bc:a2:7a:2a:38:4d:38:7b:7d:40:10:c6:66:e2:ed:b4:84:3e:4c:29:b4:ae:1d:5b:93:32:e6:b2:4d +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd +MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg +Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow +TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw +HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB +BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y +ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E +N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 +tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX +0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c +/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X +KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY +zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS +O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D +34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP +K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 +AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv +Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj +QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS +IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 +HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa +O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv +033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u +dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE +kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 +3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD +u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq +4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 3 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 3" +# Serial: 1 +# MD5 Fingerprint: ca:fb:40:a8:4e:39:92:8a:1d:fe:8e:2f:c4:27:ea:ef +# SHA1 Fingerprint: 55:a6:72:3e:cb:f2:ec:cd:c3:23:74:70:19:9d:2a:be:11:e3:81:d1 +# SHA256 Fingerprint: fd:73:da:d3:1c:64:4f:f1:b4:3b:ef:0c:cd:da:96:71:0b:9c:d9:87:5e:ca:7e:31:70:7a:f3:e9:6d:52:2b:bd +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN +8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ +RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 +hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 +ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM +EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 +A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy +WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ +1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 +6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT +91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p +TpPDpFQUWw== +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 2009" +# Serial: 623603 +# MD5 Fingerprint: cd:e0:25:69:8d:47:ac:9c:89:35:90:f7:fd:51:3d:2f +# SHA1 Fingerprint: 58:e8:ab:b0:36:15:33:fb:80:f7:9b:1b:6d:29:d3:ff:8d:5f:00:f0 +# SHA256 Fingerprint: 49:e7:a4:42:ac:f0:ea:62:87:05:00:54:b5:25:64:b6:50:e4:f4:9e:42:e3:48:d6:aa:38:e0:39:e9:57:b1:c1 +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha +ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM +HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 +UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 +tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R +ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM +lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp +/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G +A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G +A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj +dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy +MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl +cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js +L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL +BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni +acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K +zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 +PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y +Johw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Subject: CN=D-TRUST Root Class 3 CA 2 EV 2009 O=D-Trust GmbH +# Label: "D-TRUST Root Class 3 CA 2 EV 2009" +# Serial: 623604 +# MD5 Fingerprint: aa:c6:43:2c:5e:2d:cd:c4:34:c0:50:4f:11:02:4f:b6 +# SHA1 Fingerprint: 96:c9:1b:0b:95:b4:10:98:42:fa:d0:d8:22:79:fe:60:fa:b9:16:83 +# SHA256 Fingerprint: ee:c5:49:6b:98:8c:e9:86:25:b9:34:09:2e:ec:29:08:be:d0:b0:f3:16:c2:d4:73:0c:84:ea:f1:f3:d3:48:81 +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF +MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD +bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw +NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV +BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn +ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 +3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z +qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR +p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 +HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw +ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea +HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw +Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh +c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E +RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt +dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku +Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp +3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF +CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na +xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX +KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +# Issuer: CN=CA Disig Root R2 O=Disig a.s. +# Subject: CN=CA Disig Root R2 O=Disig a.s. +# Label: "CA Disig Root R2" +# Serial: 10572350602393338211 +# MD5 Fingerprint: 26:01:fb:d8:27:a7:17:9a:45:54:38:1a:43:01:3b:03 +# SHA1 Fingerprint: b5:61:eb:ea:a4:de:e4:25:4b:69:1a:98:a5:57:47:c2:34:c7:d9:71 +# SHA256 Fingerprint: e2:3d:4a:03:6d:7b:70:e9:f5:95:b1:42:20:79:d2:b9:1e:df:bb:1f:b6:51:a0:63:3e:aa:8a:9d:c5:f8:07:03 +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV +BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu +MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy +MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx +EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe +NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH +PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I +x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe +QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR +yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO +QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 +H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ +QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD +i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs +nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 +rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI +hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf +GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb +lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka ++elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal +TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i +nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 +gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr +G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os +zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x +L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +# Issuer: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Subject: CN=ACCVRAIZ1 O=ACCV OU=PKIACCV +# Label: "ACCVRAIZ1" +# Serial: 6828503384748696800 +# MD5 Fingerprint: d0:a0:5a:ee:05:b6:09:94:21:a1:7d:f1:b2:29:82:02 +# SHA1 Fingerprint: 93:05:7a:88:15:c6:4f:ce:88:2f:fa:91:16:52:28:78:bc:53:64:17 +# SHA256 Fingerprint: 9a:6e:c0:12:e1:a7:da:9d:be:34:19:4d:47:8a:d7:c0:db:18:22:fb:07:1d:f1:29:81:49:6e:d1:04:38:41:13 +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE +AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw +CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ +BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND +VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb +qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY +HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo +G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA +lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr +IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ +0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH +k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 +4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO +m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa +cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl +uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI +KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls +ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG +AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT +VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG +CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA +cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA +QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA +7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA +cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA +QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA +czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu +aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt +aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud +DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF +BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp +D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU +JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m +AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD +vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms +tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH +7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA +h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF +d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H +pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +# Issuer: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Subject: CN=TWCA Global Root CA O=TAIWAN-CA OU=Root CA +# Label: "TWCA Global Root CA" +# Serial: 3262 +# MD5 Fingerprint: f9:03:7e:cf:e6:9e:3c:73:7a:2a:90:07:69:ff:2b:96 +# SHA1 Fingerprint: 9c:bb:48:53:f6:a4:f6:d3:52:a4:e8:32:52:55:60:13:f5:ad:af:65 +# SHA256 Fingerprint: 59:76:90:07:f7:68:5d:0f:cd:50:87:2f:9f:95:d5:75:5a:5b:2b:45:7d:81:f3:69:2b:61:0a:98:67:2f:0e:1b +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx +EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT +VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 +NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT +B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF +10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz +0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh +MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH +zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc +46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 +yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi +laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP +oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA +BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE +qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm +4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL +1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF +H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo +RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ +nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh +15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW +6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW +nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j +wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz +aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy +KwbQBM0= +-----END CERTIFICATE----- + +# Issuer: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Subject: CN=TeliaSonera Root CA v1 O=TeliaSonera +# Label: "TeliaSonera Root CA v1" +# Serial: 199041966741090107964904287217786801558 +# MD5 Fingerprint: 37:41:49:1b:18:56:9a:26:f5:ad:c2:66:fb:40:a5:4c +# SHA1 Fingerprint: 43:13:bb:96:f1:d5:86:9b:c1:4e:6a:92:f6:cf:f6:34:69:87:82:37 +# SHA256 Fingerprint: dd:69:36:fe:21:f8:f0:77:c1:23:a1:a5:21:c1:22:24:f7:22:55:b7:3e:03:a7:26:06:93:e8:a2:4b:0f:a3:89 +-----BEGIN CERTIFICATE----- +MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw +NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv +b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD +VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F +VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 +7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X +Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ +/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs +81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm +dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe +Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu +sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 +pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs +slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ +arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD +VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG +9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl +dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx +0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj +TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed +Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 +Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI +OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 +vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW +t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn +HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx +SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= +-----END CERTIFICATE----- + +# Issuer: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Subject: CN=T-TeleSec GlobalRoot Class 2 O=T-Systems Enterprise Services GmbH OU=T-Systems Trust Center +# Label: "T-TeleSec GlobalRoot Class 2" +# Serial: 1 +# MD5 Fingerprint: 2b:9b:9e:e4:7b:6c:1f:00:72:1a:cc:c1:77:79:df:6a +# SHA1 Fingerprint: 59:0d:2d:7d:88:4f:40:2e:61:7e:a5:62:32:17:65:cf:17:d8:94:e9 +# SHA256 Fingerprint: 91:e2:f5:78:8d:58:10:eb:a7:ba:58:73:7d:e1:54:8a:8e:ca:cd:01:45:98:bc:0b:14:3e:04:1b:17:05:25:52 +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx +KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd +BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl +YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 +OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy +aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 +ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd +AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC +FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi +1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq +jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ +wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ +WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy +NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC +uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw +IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 +g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN +9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP +BSeOE6Fuwg== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot 2011 O=Atos +# Subject: CN=Atos TrustedRoot 2011 O=Atos +# Label: "Atos TrustedRoot 2011" +# Serial: 6643877497813316402 +# MD5 Fingerprint: ae:b9:c4:32:4b:ac:7f:5d:66:cc:77:94:bb:2a:77:56 +# SHA1 Fingerprint: 2b:b1:f5:3e:55:0c:1d:c5:f1:d4:e6:b7:6a:46:4b:55:06:02:ac:21 +# SHA256 Fingerprint: f3:56:be:a2:44:b7:a9:1e:b3:5d:53:ca:9a:d7:86:4a:ce:01:8e:2d:35:d5:f8:f9:6d:df:68:a6:f4:1a:a4:74 +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE +AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG +EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM +FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC +REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp +Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM +VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ +SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ +4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L +cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi +eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG +A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 +DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j +vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP +DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc +maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D +lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv +KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 1 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 1 G3" +# Serial: 687049649626669250736271037606554624078720034195 +# MD5 Fingerprint: a4:bc:5b:3f:fe:37:9a:fa:64:f0:e2:fa:05:3d:0b:ab +# SHA1 Fingerprint: 1b:8e:ea:57:96:29:1a:c9:39:ea:b8:0a:81:1a:73:73:c0:93:79:67 +# SHA256 Fingerprint: 8a:86:6f:d1:b2:76:b5:7e:57:8e:92:1c:65:82:8a:2b:ed:58:e9:f2:f2:88:05:41:34:b7:f1:f4:bf:c9:cc:74 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 +MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV +wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe +rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 +68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh +4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp +UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o +abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc +3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G +KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt +hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO +Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt +zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD +ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC +MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 +cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN +qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 +YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv +b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 +8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k +NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj +ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp +q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt +nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 2 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 2 G3" +# Serial: 390156079458959257446133169266079962026824725800 +# MD5 Fingerprint: af:0c:86:6e:bf:40:2d:7f:0b:3e:12:50:ba:12:3d:06 +# SHA1 Fingerprint: 09:3c:61:f3:8b:8b:dc:7d:55:df:75:38:02:05:00:e1:25:f5:c8:36 +# SHA256 Fingerprint: 8f:e4:fb:0a:f9:3a:4d:0d:67:db:0b:eb:b2:3e:37:c7:1b:f3:25:dc:bc:dd:24:0e:a0:4d:af:58:b4:7e:18:40 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 +MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf +qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW +n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym +c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ +O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 +o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j +IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq +IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz +8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh +vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l +7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG +cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD +ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 +AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC +roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga +W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n +lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE ++V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV +csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd +dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg +KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM +HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 +WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M +-----END CERTIFICATE----- + +# Issuer: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Subject: CN=QuoVadis Root CA 3 G3 O=QuoVadis Limited +# Label: "QuoVadis Root CA 3 G3" +# Serial: 268090761170461462463995952157327242137089239581 +# MD5 Fingerprint: df:7d:b9:ad:54:6f:68:a1:df:89:57:03:97:43:b0:d7 +# SHA1 Fingerprint: 48:12:bd:92:3c:a8:c4:39:06:e7:30:6d:27:96:e6:a4:cf:22:2e:7d +# SHA256 Fingerprint: 88:ef:81:de:20:2e:b0:18:45:2e:43:f8:64:72:5c:ea:5f:bd:1f:c2:d9:d2:05:73:07:09:c5:d8:b8:69:0f:46 +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc +BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 +MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM +aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR +/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu +FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR +U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c +ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR +FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k +A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw +eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl +sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp +VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q +A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ +ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD +ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px +KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI +FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv +oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg +u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP +0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf +3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl +8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ +DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN +PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ +ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G2" +# Serial: 15385348160840213938643033620894905419 +# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d +# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f +# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85 +-----BEGIN CERTIFICATE----- +MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv +b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl +cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA +n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc +biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp +EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA +bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu +YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB +AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW +BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI +QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I +0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni +lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 +B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv +ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo +IhNzbM8m9Yop5w== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Assured ID Root G3" +# Serial: 15459312981008553731928384953135426796 +# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb +# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89 +# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2 +-----BEGIN CERTIFICATE----- +MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg +RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf +Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q +RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ +BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD +AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY +JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv +6pZjamVFkpUBtA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G2" +# Serial: 4293743540046975378534879503202253541 +# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44 +# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4 +# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH +MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT +MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j +b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI +2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx +1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ +q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz +tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ +vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV +5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY +1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 +NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG +Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 +8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe +pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl +MrY= +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Global Root G3" +# Serial: 7089244469030293291760083333884364146 +# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca +# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e +# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0 +-----BEGIN CERTIFICATE----- +MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw +CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu +ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe +Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw +EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x +IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG +fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO +Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd +BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx +AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ +oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 +sycX +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com +# Label: "DigiCert Trusted Root G4" +# Serial: 7451500558977370777930084869016614236 +# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49 +# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4 +# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88 +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg +RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV +UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu +Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y +ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If +xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV +ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO +DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ +jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ +CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi +EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM +fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY +uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK +chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t +9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB +hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD +ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 +SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd ++SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc +fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa +sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N +cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N +0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie +4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI +r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 +/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm +gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ +-----END CERTIFICATE----- + +# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited +# Label: "COMODO RSA Certification Authority" +# Serial: 101909084537582093308941363524873193117 +# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18 +# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4 +# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34 +-----BEGIN CERTIFICATE----- +MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR +6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X +pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC +9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV +/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf +Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z ++pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w +qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah +SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC +u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf +Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq +crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E +FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB +/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl +wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM +4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV +2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna +FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ +CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK +boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke +jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL +S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb +QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl +0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB +NVOFBkpdn627G190 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network +# Label: "USERTrust RSA Certification Authority" +# Serial: 2645093764781058787591871645665788717 +# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5 +# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e +# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2 +-----BEGIN CERTIFICATE----- +MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB +iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl +cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV +BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw +MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU +aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK +AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B +3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY +tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ +Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 +VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT +79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 +c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT +Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l +c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee +UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE +Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd +BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF +Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO +VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 +ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs +8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR +iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze +Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ +XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ +qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB +VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB +L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG +jjxDah2nGN59PRbxYvnKkKj9 +-----END CERTIFICATE----- + +# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network +# Label: "USERTrust ECC Certification Authority" +# Serial: 123013823720199481456569720443997572134 +# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1 +# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0 +# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a +-----BEGIN CERTIFICATE----- +MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl +eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT +JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx +MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT +Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg +VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm +aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo +I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng +o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G +A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB +zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW +RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5 +# Label: "GlobalSign ECC Root CA - R5" +# Serial: 32785792099990507226680698011560947931244 +# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08 +# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa +# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24 +-----BEGIN CERTIFICATE----- +MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk +MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH +bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX +DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD +QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc +8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke +hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD +VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI +KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg +515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO +xwy8p2Fp8fc74SrL+SvzZpA3 +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust +# Label: "IdenTrust Commercial Root CA 1" +# Serial: 13298821034946342390520003877796839426 +# MD5 Fingerprint: b3:3e:77:73:75:ee:a0:d3:e3:7e:49:63:49:59:bb:c7 +# SHA1 Fingerprint: df:71:7e:aa:4a:d9:4e:c9:55:84:99:60:2d:48:de:5f:bc:f0:3a:25 +# SHA256 Fingerprint: 5d:56:49:9b:e4:d2:e0:8b:cf:ca:d0:8a:3e:38:72:3d:50:50:3b:de:70:69:48:e4:2f:55:60:30:19:e5:28:ae +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu +VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw +MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw +JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT +3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU ++ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp +S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 +bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi +T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL +vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK +Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK +dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT +c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv +l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N +iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD +ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH +6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt +LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 +nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 ++wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK +W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT +AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq +l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG +4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ +mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A +7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H +-----END CERTIFICATE----- + +# Issuer: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Subject: CN=IdenTrust Public Sector Root CA 1 O=IdenTrust +# Label: "IdenTrust Public Sector Root CA 1" +# Serial: 13298821034946342390521976156843933698 +# MD5 Fingerprint: 37:06:a5:b0:fc:89:9d:ba:f4:6b:8c:1a:64:cd:d5:ba +# SHA1 Fingerprint: ba:29:41:60:77:98:3f:f4:f3:ef:f2:31:05:3b:2e:ea:6d:4d:45:fd +# SHA256 Fingerprint: 30:d0:89:5a:9a:44:8a:26:20:91:63:55:22:d1:f5:20:10:b5:86:7a:ca:e1:2c:78:ef:95:8f:d4:f4:38:9f:2f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu +VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN +MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 +MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 +ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy +RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS +bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF +/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R +3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw +EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy +9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V +GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ +2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV +WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD +W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN +AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj +t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV +DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 +TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G +lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW +mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df +WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 ++bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ +tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA +GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv +8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G2 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2009 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G2" +# Serial: 1246989352 +# MD5 Fingerprint: 4b:e2:c9:91:96:65:0c:f4:0e:5a:93:92:a0:0a:fe:b2 +# SHA1 Fingerprint: 8c:f4:27:fd:79:0c:3a:d1:66:06:8d:e8:1e:57:ef:bb:93:22:72:d4 +# SHA256 Fingerprint: 43:df:57:74:b0:3e:7f:ef:5f:e4:0d:93:1a:7b:ed:f1:bb:2e:6b:42:73:8c:4e:6d:38:41:10:3d:3a:a7:f3:39 +-----BEGIN CERTIFICATE----- +MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 +cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs +IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz +dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy +NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu +dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt +dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 +aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T +RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN +cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW +wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 +U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 +jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN +BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ +jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ +Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v +1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R +nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH +VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - EC1 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2012 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - EC1" +# Serial: 51543124481930649114116133369 +# MD5 Fingerprint: b6:7e:1d:f0:58:c5:49:6c:24:3b:3d:ed:98:18:ed:bc +# SHA1 Fingerprint: 20:d8:06:40:df:9b:25:f5:12:25:3a:11:ea:f7:59:8a:eb:14:b5:47 +# SHA256 Fingerprint: 02:ed:0e:b2:8c:14:da:45:16:5c:56:67:91:70:0d:64:51:d7:fb:56:f0:b2:ab:1d:3b:8e:b0:70:e5:6e:df:f5 +-----BEGIN CERTIFICATE----- +MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG +A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 +d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu +dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq +RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy +MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD +VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 +L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g +Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi +A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt +ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH +Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O +BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC +R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX +hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G +-----END CERTIFICATE----- + +# Issuer: CN=CFCA EV ROOT O=China Financial Certification Authority +# Subject: CN=CFCA EV ROOT O=China Financial Certification Authority +# Label: "CFCA EV ROOT" +# Serial: 407555286 +# MD5 Fingerprint: 74:e1:b6:ed:26:7a:7a:44:30:33:94:ab:7b:27:81:30 +# SHA1 Fingerprint: e2:b8:29:4b:55:84:ab:6b:58:c2:90:46:6c:ac:3f:b8:39:8f:84:83 +# SHA256 Fingerprint: 5c:c3:d7:8e:4e:1d:5e:45:54:7a:04:e6:87:3e:64:f9:0c:f9:53:6d:1c:cc:2e:f8:00:f3:55:c4:c5:fd:70:fd +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y +aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx +MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j +aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP +T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 +sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL +TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 +/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp +7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz +EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt +hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP +a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot +aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg +TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV +PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv +cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL +tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd +BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB +ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT +ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL +jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS +ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy +P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 +xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d +Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN +5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe +/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z +AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ +5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GB CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GB CA" +# Serial: 157768595616588414422159278966750757568 +# MD5 Fingerprint: a4:eb:b9:61:28:2e:b7:2f:98:b0:35:26:90:99:51:1d +# SHA1 Fingerprint: 0f:f9:40:76:18:d3:d7:6a:4b:98:f0:a8:35:9e:0c:fd:27:ac:cc:ed +# SHA256 Fingerprint: 6b:9c:08:e8:6e:b0:f7:67:cf:ad:65:cd:98:b6:21:49:e5:49:4a:67:f5:84:5e:7b:d1:ed:01:9f:27:b8:6b:d6 +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt +MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg +Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i +YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x +CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG +b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh +bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 +HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx +WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX +1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk +u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P +99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r +M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB +BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh +cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 +gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO +ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf +aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic +Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= +-----END CERTIFICATE----- + +# Issuer: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Subject: CN=SZAFIR ROOT CA2 O=Krajowa Izba Rozliczeniowa S.A. +# Label: "SZAFIR ROOT CA2" +# Serial: 357043034767186914217277344587386743377558296292 +# MD5 Fingerprint: 11:64:c1:89:b0:24:b1:8c:b1:07:7e:89:9e:51:9e:99 +# SHA1 Fingerprint: e2:52:fa:95:3f:ed:db:24:60:bd:6e:28:f3:9c:cc:cf:5e:b3:3f:de +# SHA256 Fingerprint: a1:33:9d:33:28:1a:0b:56:e5:57:d3:d3:2b:1c:e7:f9:36:7e:b0:94:bd:5f:a7:2a:7e:50:04:c8:de:d7:ca:fe +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL +BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 +ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw +NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L +cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg +Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN +QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT +3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw +3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 +3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 +BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN +XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF +AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw +8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG +nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP +oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy +d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg +LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Network CA 2 O=Unizeto Technologies S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Network CA 2" +# Serial: 44979900017204383099463764357512596969 +# MD5 Fingerprint: 6d:46:9e:d9:25:6d:08:23:5b:5e:74:7d:1e:27:db:f2 +# SHA1 Fingerprint: d3:dd:48:3e:2b:bf:4c:05:e8:af:10:f5:fa:76:26:cf:d3:dc:30:92 +# SHA256 Fingerprint: b6:76:f2:ed:da:e8:77:5c:d3:6c:b0:f6:3c:d1:d4:60:39:61:f4:9e:62:65:ba:01:3a:2f:03:07:b6:d0:b8:04 +-----BEGIN CERTIFICATE----- +MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB +gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu +QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG +A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz +OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ +VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 +b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA +DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn +0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB +OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE +fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E +Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m +o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i +sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW +OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez +Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS +adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n +3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ +F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf +CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 +XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm +djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ +WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb +AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq +P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko +b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj +XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P +5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi +DrW5viSP +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: ca:ff:e2:db:03:d9:cb:4b:e9:0f:ad:84:fd:7b:18:ce +# SHA1 Fingerprint: 01:0c:06:95:a6:98:19:14:ff:bf:5f:c6:b0:b6:95:ea:29:e9:12:a6 +# SHA256 Fingerprint: a0:40:92:9a:02:ce:53:b4:ac:f4:f2:ff:c6:98:1c:e4:49:6f:75:5e:6d:45:fe:0b:2a:69:2b:cd:52:52:3f:36 +-----BEGIN CERTIFICATE----- +MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix +DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k +IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT +N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v +dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG +A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh +ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx +QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 +dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA +4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 +AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 +4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C +ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV +9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD +gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 +Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq +NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko +LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc +Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd +ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I +XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI +M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot +9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V +Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea +j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh +X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ +l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf +bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 +pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK +e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 +vm9qp/UsQu0yrbYhnr68 +-----END CERTIFICATE----- + +# Issuer: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Subject: CN=Hellenic Academic and Research Institutions ECC RootCA 2015 O=Hellenic Academic and Research Institutions Cert. Authority +# Label: "Hellenic Academic and Research Institutions ECC RootCA 2015" +# Serial: 0 +# MD5 Fingerprint: 81:e5:b4:17:eb:c2:f5:e1:4b:0d:41:7b:49:92:fe:ef +# SHA1 Fingerprint: 9f:f1:71:8d:92:d5:9a:f3:7d:74:97:b4:bc:6f:84:68:0b:ba:b6:66 +# SHA256 Fingerprint: 44:b5:45:aa:8a:25:e6:5a:73:ca:15:dc:27:fc:36:d2:4c:1c:b9:95:3a:06:65:39:b1:15:82:dc:48:7b:48:33 +-----BEGIN CERTIFICATE----- +MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN +BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl +bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv +b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ +BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj +YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 +MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 +dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg +QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa +jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi +C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep +lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof +TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X1 O=Internet Security Research Group +# Subject: CN=ISRG Root X1 O=Internet Security Research Group +# Label: "ISRG Root X1" +# Serial: 172886928669790476064670243504169061120 +# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e +# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8 +# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6 +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- + +# Issuer: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Subject: O=FNMT-RCM OU=AC RAIZ FNMT-RCM +# Label: "AC RAIZ FNMT-RCM" +# Serial: 485876308206448804701554682760554759 +# MD5 Fingerprint: e2:09:04:b4:d3:bd:d1:a0:14:fd:1a:d2:47:c4:57:1d +# SHA1 Fingerprint: ec:50:35:07:b2:15:c4:95:62:19:e2:a8:9a:5b:42:99:2c:4c:2c:20 +# SHA256 Fingerprint: eb:c5:57:0c:29:01:8c:4d:67:b1:aa:12:7b:af:12:f7:03:b4:61:1e:bc:17:b7:da:b5:57:38:94:17:9b:93:fa +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx +CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ +WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ +BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG +Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ +yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf +BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz +WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF +tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z +374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC +IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL +mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 +wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS +MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 +ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet +UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H +YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 +LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD +nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 +RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM +LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf +77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N +JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm +fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp +6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp +1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B +9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok +RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv +uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 1 O=Amazon +# Subject: CN=Amazon Root CA 1 O=Amazon +# Label: "Amazon Root CA 1" +# Serial: 143266978916655856878034712317230054538369994 +# MD5 Fingerprint: 43:c6:bf:ae:ec:fe:ad:2f:18:c6:88:68:30:fc:c8:e6 +# SHA1 Fingerprint: 8d:a7:f9:65:ec:5e:fc:37:91:0f:1c:6e:59:fd:c1:cc:6a:6e:de:16 +# SHA256 Fingerprint: 8e:cd:e6:88:4f:3d:87:b1:12:5b:a3:1a:c3:fc:b1:3d:70:16:de:7f:57:cc:90:4f:e1:cb:97:c6:ae:98:19:6e +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 2 O=Amazon +# Subject: CN=Amazon Root CA 2 O=Amazon +# Label: "Amazon Root CA 2" +# Serial: 143266982885963551818349160658925006970653239 +# MD5 Fingerprint: c8:e5:8d:ce:a8:42:e2:7a:c0:2a:5c:7c:9e:26:bf:66 +# SHA1 Fingerprint: 5a:8c:ef:45:d7:a6:98:59:76:7a:8c:8b:44:96:b5:78:cf:47:4b:1a +# SHA256 Fingerprint: 1b:a5:b2:aa:8c:65:40:1a:82:96:01:18:f8:0b:ec:4f:62:30:4d:83:ce:c4:71:3a:19:c3:9c:01:1e:a4:6d:b4 +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK +gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ +W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg +1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K +8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r +2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me +z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR +8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj +mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz +7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 ++XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI +0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB +Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm +UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 +LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY ++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS +k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl +7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm +btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl +urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ +fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 +n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE +76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H +9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT +4PsJYGw= +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 3 O=Amazon +# Subject: CN=Amazon Root CA 3 O=Amazon +# Label: "Amazon Root CA 3" +# Serial: 143266986699090766294700635381230934788665930 +# MD5 Fingerprint: a0:d4:ef:0b:f7:b5:d8:49:95:2a:ec:f5:c4:fc:81:87 +# SHA1 Fingerprint: 0d:44:dd:8c:3c:8c:1a:1a:58:75:64:81:e9:0f:2e:2a:ff:b3:d2:6e +# SHA256 Fingerprint: 18:ce:6c:fe:7b:f1:4e:60:b2:e3:47:b8:df:e8:68:cb:31:d0:2e:bb:3a:da:27:15:69:f5:03:43:b4:6d:b3:a4 +-----BEGIN CERTIFICATE----- +MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl +ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j +QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr +ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr +BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM +YyRIHN8wfdVoOw== +-----END CERTIFICATE----- + +# Issuer: CN=Amazon Root CA 4 O=Amazon +# Subject: CN=Amazon Root CA 4 O=Amazon +# Label: "Amazon Root CA 4" +# Serial: 143266989758080763974105200630763877849284878 +# MD5 Fingerprint: 89:bc:27:d5:eb:17:8d:06:6a:69:d5:fd:89:47:b4:cd +# SHA1 Fingerprint: f6:10:84:07:d6:f8:bb:67:98:0c:c2:e2:44:c2:eb:ae:1c:ef:63:be +# SHA256 Fingerprint: e3:5d:28:41:9e:d0:20:25:cf:a6:90:38:cd:62:39:62:45:8d:a5:c6:95:fb:de:a3:c2:2b:0b:fb:25:89:70:92 +-----BEGIN CERTIFICATE----- +MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g +Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG +A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg +Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi +9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk +M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB +MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw +CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW +1KyLa2tJElMzrdfkviT8tQp21KW8EA== +-----END CERTIFICATE----- + +# Issuer: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Subject: CN=TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 O=Turkiye Bilimsel ve Teknolojik Arastirma Kurumu - TUBITAK OU=Kamu Sertifikasyon Merkezi - Kamu SM +# Label: "TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1" +# Serial: 1 +# MD5 Fingerprint: dc:00:81:dc:69:2f:3e:2f:b0:3b:f6:3d:5a:91:8e:49 +# SHA1 Fingerprint: 31:43:64:9b:ec:ce:27:ec:ed:3a:3f:0b:8f:0d:e4:e8:91:dd:ee:ca +# SHA256 Fingerprint: 46:ed:c3:68:90:46:d5:3a:45:3f:b3:10:4a:b8:0d:ca:ec:65:8b:26:60:ea:16:29:dd:7e:86:79:90:64:87:16 +-----BEGIN CERTIFICATE----- +MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx +GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp +bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w +KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 +BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy +dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG +EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll +IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU +QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT +TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg +LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 +a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr +LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr +N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X +YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ +iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f +AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH +V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh +AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf +IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 +lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c +8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf +lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= +-----END CERTIFICATE----- + +# Issuer: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Subject: CN=GDCA TrustAUTH R5 ROOT O=GUANG DONG CERTIFICATE AUTHORITY CO.,LTD. +# Label: "GDCA TrustAUTH R5 ROOT" +# Serial: 9009899650740120186 +# MD5 Fingerprint: 63:cc:d9:3d:34:35:5c:6f:53:a3:e2:08:70:48:1f:b4 +# SHA1 Fingerprint: 0f:36:38:5b:81:1a:25:c3:9b:31:4e:83:ca:e9:34:66:70:cc:74:b4 +# SHA256 Fingerprint: bf:ff:8f:d0:44:33:48:7d:6a:8a:a6:0c:1a:29:76:7a:9f:c2:bb:b0:5e:42:0f:71:3a:13:b9:92:89:1d:38:93 +-----BEGIN CERTIFICATE----- +MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE +BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ +IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 +MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w +HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj +Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj +TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u +KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj +qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm +MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 +ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP +zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk +L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC +jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA +HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC +AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg +p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm +DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 +COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry +L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf +JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg +IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io +2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV +09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ +XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq +T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe +MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation +# Label: "SSL.com Root Certification Authority RSA" +# Serial: 8875640296558310041 +# MD5 Fingerprint: 86:69:12:c0:70:f1:ec:ac:ac:c2:d5:bc:a5:5b:a1:29 +# SHA1 Fingerprint: b7:ab:33:08:d1:ea:44:77:ba:14:80:12:5a:6f:bd:a9:36:49:0c:bb +# SHA256 Fingerprint: 85:66:6a:56:2e:e0:be:5c:e9:25:c1:d8:89:0a:6f:76:a8:7e:c1:6d:4d:7d:5f:29:ea:74:19:cf:20:12:3b:69 +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE +BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK +DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz +OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv +bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R +xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX +qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC +C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 +6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh +/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF +YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E +JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc +US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 +ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm ++Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi +M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV +HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G +A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV +cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc +Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs +PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ +q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 +cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr +a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I +H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y +K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu +nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf +oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY +Ic2wBlX7Jz9TkHCpBB5XJ7k= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com Root Certification Authority ECC" +# Serial: 8495723813297216424 +# MD5 Fingerprint: 2e:da:e4:39:7f:9c:8f:37:d1:70:9f:26:17:51:3a:8e +# SHA1 Fingerprint: c3:19:7c:39:24:e6:54:af:1b:c4:ab:20:95:7a:e2:c3:0e:13:02:6a +# SHA256 Fingerprint: 34:17:bb:06:cc:60:07:da:1b:96:1c:92:0b:8a:b4:ce:3f:ad:82:0e:4a:a3:0b:9a:cb:c4:a7:4e:bd:ce:bc:65 +-----BEGIN CERTIFICATE----- +MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz +WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 +b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS +b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI +7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg +CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD +VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T +kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ +gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority RSA R2 O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority RSA R2" +# Serial: 6248227494352943350 +# MD5 Fingerprint: e1:1e:31:58:1a:ae:54:53:02:f6:17:6a:11:7b:4d:95 +# SHA1 Fingerprint: 74:3a:f0:52:9b:d0:32:a0:f4:4a:83:cd:d4:ba:a9:7b:7c:2e:c4:9a +# SHA256 Fingerprint: 2e:7b:f1:6c:c2:24:85:a7:bb:e2:aa:86:96:75:07:61:b0:ae:39:be:3b:2f:e9:d0:cc:6d:4e:f7:34:91:42:5c +-----BEGIN CERTIFICATE----- +MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV +BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE +CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy +dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy +MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G +A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD +DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq +M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf +OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa +4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 +HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR +aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA +b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ +Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV +PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO +pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu +UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY +MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV +HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 +9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW +s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 +Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg +cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM +79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz +/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt +ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm +Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK +QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ +w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi +S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 +mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Subject: CN=SSL.com EV Root Certification Authority ECC O=SSL Corporation +# Label: "SSL.com EV Root Certification Authority ECC" +# Serial: 3182246526754555285 +# MD5 Fingerprint: 59:53:22:65:83:42:01:54:c0:ce:42:b9:5a:7c:f2:90 +# SHA1 Fingerprint: 4c:dd:51:a3:d1:f5:20:32:14:b0:c6:c5:32:23:03:91:c7:46:42:6d +# SHA256 Fingerprint: 22:a2:c1:f7:bd:ed:70:4c:c1:e7:01:b5:f4:08:c3:10:88:0f:e9:56:b5:de:2a:4a:44:f9:9c:87:3a:25:a7:c8 +-----BEGIN CERTIFICATE----- +MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC +VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T +U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx +NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv +dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv +bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 +AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA +VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku +WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP +MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX +5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ +ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg +h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6 +# Label: "GlobalSign Root CA - R6" +# Serial: 1417766617973444989252670301619537 +# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae +# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1 +# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69 +-----BEGIN CERTIFICATE----- +MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg +MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx +MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET +MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI +xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k +ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD +aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw +LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw +1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX +k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 +SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h +bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n +WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY +rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce +MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu +bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN +nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt +Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 +55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj +vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf +cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz +oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp +nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs +pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v +JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R +8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 +5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= +-----END CERTIFICATE----- + +# Issuer: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Subject: CN=OISTE WISeKey Global Root GC CA O=WISeKey OU=OISTE Foundation Endorsed +# Label: "OISTE WISeKey Global Root GC CA" +# Serial: 44084345621038548146064804565436152554 +# MD5 Fingerprint: a9:d6:b9:2d:2f:93:64:f8:a5:69:ca:91:e9:68:07:23 +# SHA1 Fingerprint: e0:11:84:5e:34:de:be:88:81:b9:9c:f6:16:26:d1:96:1f:c3:b9:31 +# SHA256 Fingerprint: 85:60:f9:1c:36:24:da:ba:95:70:b5:fe:a0:db:e3:6f:f1:1a:83:23:be:94:86:85:4f:b3:f3:4a:55:71:19:8d +-----BEGIN CERTIFICATE----- +MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw +CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 +bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg +Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ +BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu +ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS +b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni +eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W +p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T +rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV +57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg +Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 +-----END CERTIFICATE----- + +# Issuer: CN=UCA Global G2 Root O=UniTrust +# Subject: CN=UCA Global G2 Root O=UniTrust +# Label: "UCA Global G2 Root" +# Serial: 124779693093741543919145257850076631279 +# MD5 Fingerprint: 80:fe:f0:c4:4a:f0:5c:62:32:9f:1c:ba:78:a9:50:f8 +# SHA1 Fingerprint: 28:f9:78:16:19:7a:ff:18:25:18:aa:44:fe:c1:a0:ce:5c:b6:4c:8a +# SHA256 Fingerprint: 9b:ea:11:c9:76:fe:01:47:64:c1:be:56:a6:f9:14:b5:a5:60:31:7a:bd:99:88:39:33:82:e5:16:1a:a0:49:3c +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH +bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x +CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds +b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr +b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 +kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm +VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R +VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc +C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj +tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY +D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv +j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl +NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 +iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP +O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV +ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj +L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 +1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl +1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU +b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV +PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj +y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb +EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg +DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI ++Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy +YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX +UB+K+wb1whnw0A== +-----END CERTIFICATE----- + +# Issuer: CN=UCA Extended Validation Root O=UniTrust +# Subject: CN=UCA Extended Validation Root O=UniTrust +# Label: "UCA Extended Validation Root" +# Serial: 106100277556486529736699587978573607008 +# MD5 Fingerprint: a1:f3:5f:43:c6:34:9b:da:bf:8c:7e:05:53:ad:96:e2 +# SHA1 Fingerprint: a3:a1:b0:6f:24:61:23:4a:e3:36:a5:c2:37:fc:a6:ff:dd:f0:d7:3a +# SHA256 Fingerprint: d4:3a:f9:b3:54:73:75:5c:96:84:fc:06:d7:d8:cb:70:ee:5c:28:e7:73:fb:29:4e:b4:1e:e7:17:22:92:4d:24 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH +MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF +eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx +MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV +BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog +D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS +sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop +O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk +sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi +c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj +VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz +KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ +TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G +sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs +1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD +fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN +l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR +ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ +VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 +c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp +4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s +t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj +2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO +vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C +xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx +cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM +fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax +-----END CERTIFICATE----- + +# Issuer: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Subject: CN=Certigna Root CA O=Dhimyotis OU=0002 48146308100036 +# Label: "Certigna Root CA" +# Serial: 269714418870597844693661054334862075617 +# MD5 Fingerprint: 0e:5c:30:62:27:eb:5b:bc:d7:ae:62:ba:e9:d5:df:77 +# SHA1 Fingerprint: 2d:0d:52:14:ff:9e:ad:99:24:01:74:20:47:6e:6c:85:27:27:f5:43 +# SHA256 Fingerprint: d4:8d:3d:23:ee:db:50:a4:59:e5:51:97:60:1c:27:77:4b:9d:7b:18:c9:4d:5a:05:95:11:a1:02:50:b9:31:68 +-----BEGIN CERTIFICATE----- +MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw +WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw +MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x +MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD +VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX +BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO +ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M +CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu +I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm +TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh +C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf +ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz +IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT +Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k +JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 +hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB +GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE +FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of +1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov +L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo +dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr +aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq +hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L +6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG +HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 +0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB +lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi +o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 +gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v +faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 +Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh +jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw +3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign Root CA - G1 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign Root CA - G1" +# Serial: 235931866688319308814040 +# MD5 Fingerprint: 9c:42:84:57:dd:cb:0b:a7:2e:95:ad:b6:f3:da:bc:ac +# SHA1 Fingerprint: 8a:c7:ad:8f:73:ac:4e:c1:b5:75:4d:a5:40:f4:fc:cf:7c:b5:8e:8c +# SHA256 Fingerprint: 40:f6:af:03:46:a9:9a:a1:cd:1d:55:5a:4e:9c:ce:62:c7:f9:63:46:03:ee:40:66:15:83:3d:c8:c8:d0:03:67 +-----BEGIN CERTIFICATE----- +MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYD +VQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBU +ZWNobm9sb2dpZXMgTGltaXRlZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBH +MTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgxODMwMDBaMGcxCzAJBgNVBAYTAklO +MRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVkaHJhIFRlY2hub2xv +Z2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQz +f2N4aLTNLnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO +8oG0x5ZOrRkVUkr+PHB1cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aq +d7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHWDV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhM +tTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ6DqS0hdW5TUaQBw+jSzt +Od9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrHhQIDAQAB +o0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31x +PaOfG1vR2vjTnGs2vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjM +wiI/aTvFthUvozXGaCocV685743QNcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6d +GNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q+Mri/Tm3R7nrft8EI6/6nAYH +6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeihU80Bv2noWgby +RQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx +iN66zB+Afko= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Subject: CN=emSign ECC Root CA - G3 O=eMudhra Technologies Limited OU=emSign PKI +# Label: "emSign ECC Root CA - G3" +# Serial: 287880440101571086945156 +# MD5 Fingerprint: ce:0b:72:d1:9f:88:8e:d0:50:03:e8:e3:b8:8b:67:40 +# SHA1 Fingerprint: 30:43:fa:4f:f2:57:dc:a0:c3:80:ee:2e:58:ea:78:b2:3f:e6:bb:c1 +# SHA256 Fingerprint: 86:a1:ec:ba:08:9c:4a:8d:3b:be:27:34:c6:12:ba:34:1d:81:3e:04:3c:f9:e8:a8:62:cd:5c:57:a3:6b:be:6b +-----BEGIN CERTIFICATE----- +MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQG +EwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNo +bm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g +RzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4MTgzMDAwWjBrMQswCQYDVQQGEwJJ +TjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9s +b2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMw +djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0 +WXTsuwYc58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xyS +fvalY8L1X44uT6EYGQIrMgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuB +zhccLikenEhjQjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggq +hkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+DCBeQyh+KTOgNG3qxrdWB +CUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7jHvrZQnD ++JbNR6iC8hZVdyR+EhCVBCyj +-----END CERTIFICATE----- + +# Issuer: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign Root CA - C1 O=eMudhra Inc OU=emSign PKI +# Label: "emSign Root CA - C1" +# Serial: 825510296613316004955058 +# MD5 Fingerprint: d8:e3:5d:01:21:fa:78:5a:b0:df:ba:d2:ee:2a:5f:68 +# SHA1 Fingerprint: e7:2e:f1:df:fc:b2:09:28:cf:5d:d4:d5:67:37:b1:51:cb:86:4f:01 +# SHA256 Fingerprint: 12:56:09:aa:30:1d:a0:a2:49:b9:7a:82:39:cb:6a:34:21:6f:44:dc:ac:9f:39:54:b1:42:92:f2:e8:c8:60:8f +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG +A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg +SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v +dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ +BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ +HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH +3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH +GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c +xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1 +aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq +TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87 +/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4 +kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG +YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT ++xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo +WXzhriKi4gp6D/piq1JM4fHfyr6DDUI= +-----END CERTIFICATE----- + +# Issuer: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Subject: CN=emSign ECC Root CA - C3 O=eMudhra Inc OU=emSign PKI +# Label: "emSign ECC Root CA - C3" +# Serial: 582948710642506000014504 +# MD5 Fingerprint: 3e:53:b3:a3:81:ee:d7:10:f8:d3:b0:1d:17:92:f5:d5 +# SHA1 Fingerprint: b6:af:43:c2:9b:81:53:7d:f6:ef:6b:c3:1f:1f:60:15:0c:ee:48:66 +# SHA256 Fingerprint: bc:4d:80:9b:15:18:9d:78:db:3e:1d:8c:f4:f9:72:6a:79:5d:a1:64:3c:a5:f1:35:8e:1d:db:0e:dc:0d:7e:b3 +-----BEGIN CERTIFICATE----- +MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQG +EwJVUzETMBEGA1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMx +IDAeBgNVBAMTF2VtU2lnbiBFQ0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAw +MFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln +biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQDExdlbVNpZ24gRUND +IFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd6bci +MK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4Ojavti +sIGJAnB9SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0O +BBYEFPtaSNCAIEDyqOkAB2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB +Af8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQC02C8Cif22TGK6Q04ThHK1rt0c +3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwUZOR8loMRnLDRWmFLpg9J +0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== +-----END CERTIFICATE----- + +# Issuer: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Subject: CN=Hongkong Post Root CA 3 O=Hongkong Post +# Label: "Hongkong Post Root CA 3" +# Serial: 46170865288971385588281144162979347873371282084 +# MD5 Fingerprint: 11:fc:9f:bd:73:30:02:8a:fd:3f:f3:58:b9:cb:20:f0 +# SHA1 Fingerprint: 58:a2:d0:ec:20:52:81:5b:c1:f3:f8:64:02:24:4e:c2:8e:02:4b:02 +# SHA256 Fingerprint: 5a:2f:c0:3f:0c:83:b0:90:bb:fa:40:60:4b:09:88:44:6c:76:36:18:3d:f9:84:6e:17:10:1a:44:7f:b8:ef:d6 +-----BEGIN CERTIFICATE----- +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 +mpv0 +-----END CERTIFICATE----- + +# Issuer: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Subject: CN=Entrust Root Certification Authority - G4 O=Entrust, Inc. OU=See www.entrust.net/legal-terms/(c) 2015 Entrust, Inc. - for authorized use only +# Label: "Entrust Root Certification Authority - G4" +# Serial: 289383649854506086828220374796556676440 +# MD5 Fingerprint: 89:53:f1:83:23:b7:7c:8e:05:f1:8c:71:38:4e:1f:88 +# SHA1 Fingerprint: 14:88:4e:86:26:37:b0:26:af:59:62:5c:40:77:ec:35:29:ba:96:01 +# SHA256 Fingerprint: db:35:17:d1:f6:73:2a:2d:5a:b9:7c:53:3e:c7:07:79:ee:32:70:a6:2f:b4:ac:42:38:37:24:60:e6:f0:1e:88 +-----BEGIN CERTIFICATE----- +MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAw +gb4xCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQL +Ex9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykg +MjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAw +BgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0 +MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1 +c3QubmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJ +bmMuIC0gZm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3Qg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEc0MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3DumSXbcr3DbVZwbPLqGgZ +2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV3imz/f3E +T+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j +5pds8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAM +C1rlLAHGVK/XqsEQe9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73T +DtTUXm6Hnmo9RR3RXRv06QqsYJn7ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNX +wbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5XxNMhIWNlUpEbsZmOeX7m640A +2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV7rtNOzK+mndm +nqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 +dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwl +N4y6mACXi0mWHv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNj +c0kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9nMA0GCSqGSIb3DQEBCwUAA4ICAQAS +5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4QjbRaZIxowLByQzTS +Gwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht7LGr +hFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/ +B7NTeLUKYvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uI +AeV8KEsD+UmDfLJ/fOPtjqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbw +H5Lk6rWS02FREAutp9lfx1/cH6NcjKF+m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+ +b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKWRGhXxNUzzxkvFMSUHHuk +2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjAJOgc47Ol +IQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk +5F6G+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuY +n/PIjhs4ViFqUZPTkcpG2om3PVODLAgfi49T3f+sHw== +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft ECC Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft ECC Root Certificate Authority 2017" +# Serial: 136839042543790627607696632466672567020 +# MD5 Fingerprint: dd:a1:03:e6:4a:93:10:d1:bf:f0:19:42:cb:fe:ed:67 +# SHA1 Fingerprint: 99:9a:64:c3:7f:f4:7d:9f:ab:95:f1:47:69:89:14:60:ee:c4:c3:c5 +# SHA256 Fingerprint: 35:8d:f3:9d:76:4a:f9:e1:b7:66:e9:c9:72:df:35:2e:e1:5c:fa:c2:27:af:6a:d1:d7:0e:8e:4a:6e:dc:ba:02 +-----BEGIN CERTIFICATE----- +MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQsw +CQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYD +VQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw +MTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4MjMxNjA0WjBlMQswCQYDVQQGEwJV +UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNy +b3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq +hkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZR +ogPZnZH6thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYb +hGBKia/teQ87zvH2RPUBeMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBTIy5lycFIM+Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3 +FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlfXu5gKcs68tvWMoQZP3zV +L8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaReNtUjGUB +iudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= +-----END CERTIFICATE----- + +# Issuer: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Subject: CN=Microsoft RSA Root Certificate Authority 2017 O=Microsoft Corporation +# Label: "Microsoft RSA Root Certificate Authority 2017" +# Serial: 40975477897264996090493496164228220339 +# MD5 Fingerprint: 10:ff:00:ff:cf:c9:f8:c7:7a:c0:ee:35:8e:c9:0f:47 +# SHA1 Fingerprint: 73:a5:e6:4a:3b:ff:83:16:ff:0e:dc:cc:61:8a:90:6e:4e:ae:4d:74 +# SHA256 Fingerprint: c7:41:f7:0f:4b:2a:8d:88:bf:2e:71:c1:41:22:ef:53:ef:10:eb:a0:cf:a5:e6:4c:fa:20:f4:18:85:30:73:e0 +-----BEGIN CERTIFICATE----- +MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBl +MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw +NAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIwNzE4MjMwMDIzWjBlMQswCQYDVQQG +EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1N +aWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZ +Nt9GkMml7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0 +ZdDMbRnMlfl7rEqUrQ7eS0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1 +HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw71VdyvD/IybLeS2v4I2wDwAW9lcfNcztm +gGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+dkC0zVJhUXAoP8XFWvLJ +jEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49FyGcohJUc +aDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaG +YaRSMLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6 +W6IYZVcSn2i51BVrlMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4K +UGsTuqwPN1q3ErWQgR5WrlcihtnJ0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH ++FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJClTUFLkqqNfs+avNJVgyeY+Q +W5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC +NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZC +LgLNFgVZJ8og6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OC +gMNPOsduET/m4xaRhPtthH80dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6 +tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk+ONVFT24bcMKpBLBaYVu32TxU5nh +SnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex/2kskZGT4d9Mozd2 +TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDyAmH3 +pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGR +xpl/j8nWZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiApp +GWSZI1b7rCoucL5mxAyE7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9 +dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKTc0QWbej09+CVgI+WXTik9KveCjCHk9hN +AHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D5KbvtwEwXlGjefVwaaZB +RA+GsCyRxj3qrg+E +-----END CERTIFICATE----- + +# Issuer: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Subject: CN=e-Szigno Root CA 2017 O=Microsec Ltd. +# Label: "e-Szigno Root CA 2017" +# Serial: 411379200276854331539784714 +# MD5 Fingerprint: de:1f:f6:9e:84:ae:a7:b4:21:ce:1e:58:7d:d1:84:98 +# SHA1 Fingerprint: 89:d4:83:03:4f:9e:9a:48:80:5f:72:37:d4:a9:a6:ef:cb:7c:1f:d1 +# SHA256 Fingerprint: be:b0:0b:30:83:9b:9b:c3:2c:32:e4:44:79:05:95:06:41:f2:64:21:b1:5e:d0:89:19:8b:51:8a:e2:ea:1b:99 +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + +# Issuer: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Subject: O=CERTSIGN SA OU=certSIGN ROOT CA G2 +# Label: "certSIGN Root CA G2" +# Serial: 313609486401300475190 +# MD5 Fingerprint: 8c:f1:75:8a:c6:19:cf:94:b7:f7:65:20:87:c3:97:c7 +# SHA1 Fingerprint: 26:f9:93:b4:ed:3d:28:27:b0:b9:4b:a7:e9:15:1d:a3:8d:92:e5:32 +# SHA256 Fingerprint: 65:7c:fe:2f:a7:3f:aa:38:46:25:71:f3:32:a2:36:3a:46:fc:e7:02:09:51:71:07:02:cd:fb:b6:ee:da:33:05 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV +BAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04g +Uk9PVCBDQSBHMjAeFw0xNzAyMDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJ +BgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJ +R04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDF +dRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05N0Iw +vlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZ +uIt4ImfkabBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhp +n+Sc8CnTXPnGFiWeI8MgwT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKs +cpc/I1mbySKEwQdPzH/iV8oScLumZfNpdWO9lfsbl83kqK/20U6o2YpxJM02PbyW +xPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91QqhngLjYl/rNUssuHLoPj1P +rCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732jcZZroiF +DsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fx +DTvf95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgy +LcsUDFDYg2WD7rlcz8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6C +eWRgKRM+o/1Pcmqr4tTluCRVLERLiohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSCIS1mxteg4BXrzkwJ +d8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOBywaK8SJJ6ejq +kX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC +b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQl +qiCA2ClV9+BB/AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0 +OJD7uNGzcgbJceaBxXntC6Z58hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+c +NywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5BiKDUyUM/FHE5r7iOZULJK2v0ZXk +ltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklWatKcsWMy5WHgUyIO +pwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tUSxfj +03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZk +PuXaTH4MNMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE +1LlSVHJ7liXMvGnjSG4N0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MX +QRBdJ3NghVdJIgc= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global Certification Authority" +# Serial: 1846098327275375458322922162 +# MD5 Fingerprint: f8:1c:18:2d:2f:ba:5f:6d:a1:6c:bc:c7:ab:91:c7:0e +# SHA1 Fingerprint: 2f:8f:36:4f:e1:58:97:44:21:59:87:a5:2a:9a:d0:69:95:26:7f:b5 +# SHA256 Fingerprint: 97:55:20:15:f5:dd:fc:3c:87:88:c0:06:94:45:55:40:88:94:45:00:84:f1:00:86:70:86:bc:1a:2b:b5:8d:c8 +-----BEGIN CERTIFICATE----- +MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQsw +CQYDVQQGEwJVUzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28x +ITAfBgNVBAoMGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1 +c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMx +OTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJVUzERMA8GA1UECAwI +SWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2ZSBI +b2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +ALldUShLPDeS0YLOvR29zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0Xzn +swuvCAAJWX/NKSqIk4cXGIDtiLK0thAfLdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu +7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4BqstTnoApTAbqOl5F2brz8 +1Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9oWN0EACyW +80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotP +JqX+OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1l +RtzuzWniTY+HKE40Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfw +hI0Vcnyh78zyiGG69Gm7DIwLdVcEuE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10 +coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm+9jaJXLE9gCxInm943xZYkqc +BW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqjifLJS3tBEW1n +twiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1Ud +DwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W +0OhUKDtkLSGm+J1WE2pIPU/HPinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfe +uyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0HZJDmHvUqoai7PF35owgLEQzxPy0Q +lG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla4gt5kNdXElE1GYhB +aCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5RvbbE +sLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPT +MaCm/zjdzyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qe +qu5AvzSxnI9O4fKSTx+O856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxh +VicGaeVyQYHTtgGJoC86cnn+OjC/QezHYj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8 +h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu3R3y4G5OBVixwJAWKqQ9 +EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP29FpHOTK +yeC2nOnOcXHebD8WpHk= +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P256 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P256 Certification Authority" +# Serial: 4151900041497450638097112925 +# MD5 Fingerprint: 5b:44:e3:8d:5d:36:86:26:e8:0d:05:d2:59:a7:83:54 +# SHA1 Fingerprint: b4:90:82:dd:45:0c:be:8b:5b:b1:66:d3:e2:a4:08:26:cd:ed:42:cf +# SHA256 Fingerprint: 94:5b:bc:82:5e:a5:54:f4:89:d1:fd:51:a7:3d:df:2e:a6:24:ac:70:19:a0:52:05:22:5c:22:a7:8c:cf:a8:b4 +-----BEGIN CERTIFICATE----- +MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDI1NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABH77bOYj43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoN +FWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqmP62jQzBBMA8GA1UdEwEB/wQFMAMBAf8w +DwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt0UrrdaVKEJmzsaGLSvcw +CgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjzRM4q3wgh +DDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 +-----END CERTIFICATE----- + +# Issuer: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Subject: CN=Trustwave Global ECC P384 Certification Authority O=Trustwave Holdings, Inc. +# Label: "Trustwave Global ECC P384 Certification Authority" +# Serial: 2704997926503831671788816187 +# MD5 Fingerprint: ea:cf:60:c4:3b:b9:15:29:40:a1:97:ed:78:27:93:d6 +# SHA1 Fingerprint: e7:f3:a3:c8:cf:6f:c3:04:2e:6d:0e:67:32:c5:9e:68:95:0d:5e:d2 +# SHA256 Fingerprint: 55:90:38:59:c8:c0:c3:eb:b8:75:9e:ce:4e:25:57:22:5f:f5:75:8b:bd:38:eb:d4:82:76:60:1e:1b:d5:80:97 +-----BEGIN CERTIFICATE----- +MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYD +VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAf +BgNVBAoTGFRydXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3 +YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0x +NzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYDVQQGEwJVUzERMA8G +A1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0 +d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBF +Q0MgUDM4NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuB +BAAiA2IABGvaDXU1CDFHBa5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJ +j9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr/TklZvFe/oyujUF5nQlgziip04pt89ZF +1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwYAMB0G +A1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNnADBkAjA3 +AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsC +MGclCrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVu +Sw== +-----END CERTIFICATE----- + +# Issuer: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Subject: CN=NAVER Global Root Certification Authority O=NAVER BUSINESS PLATFORM Corp. +# Label: "NAVER Global Root Certification Authority" +# Serial: 9013692873798656336226253319739695165984492813 +# MD5 Fingerprint: c8:7e:41:f6:25:3b:f5:09:b3:17:e8:46:3d:bf:d0:9b +# SHA1 Fingerprint: 8f:6b:f2:a9:27:4a:da:14:a0:c4:f4:8e:61:27:f9:c0:1e:78:5d:d1 +# SHA256 Fingerprint: 88:f4:38:dc:f8:ff:d1:fa:8f:42:91:15:ff:e5:f8:2a:e1:e0:6e:0c:70:c3:75:fa:ad:71:7b:34:a4:9e:72:65 +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEM +BQAwaTELMAkGA1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRG +T1JNIENvcnAuMTIwMAYDVQQDDClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4NDJaFw0zNzA4MTgyMzU5NTlaMGkx +CzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVTUyBQTEFURk9STSBD +b3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVA +iQqrDZBbUGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH +38dq6SZeWYp34+hInDEW+j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lE +HoSTGEq0n+USZGnQJoViAbbJAh2+g1G7XNr4rRVqmfeSVPc0W+m/6imBEtRTkZaz +kVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2aacp+yPOiNgSnABIqKYP +szuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4Yb8Obtoq +vC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHf +nZ3zVHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaG +YQ5fG8Ir4ozVu53BA0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo +0es+nPxdGoMuK8u180SdOqcXYZaicdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3a +CJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejyYhbLgGvtPe31HzClrkvJE+2K +AQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNVHQ4EFgQU0p+I +36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB +Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoN +qo0hV4/GPnrK21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatj +cu3cvuzHV+YwIHHW1xDBE1UBjCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm ++LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bxhYTeodoS76TiEJd6eN4MUZeoIUCL +hr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTgE34h5prCy8VCZLQe +lHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTHD8z7 +p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8 +piKCk5XQA76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLR +LBT/DShycpWbXgnbiUSYqqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX +5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oGI/hGoiLtk/bdmuYqh7GYVPEi92tF4+KO +dh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmgkpzNNIaRkPpkUZ3+/uul +9XXeifdy +-----END CERTIFICATE----- + +# Issuer: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Subject: CN=AC RAIZ FNMT-RCM SERVIDORES SEGUROS O=FNMT-RCM OU=Ceres +# Label: "AC RAIZ FNMT-RCM SERVIDORES SEGUROS" +# Serial: 131542671362353147877283741781055151509 +# MD5 Fingerprint: 19:36:9c:52:03:2f:d2:d1:bb:23:cc:dd:1e:12:55:bb +# SHA1 Fingerprint: 62:ff:d9:9e:c0:65:0d:03:ce:75:93:d2:ed:3f:2d:32:c9:e3:e5:4a +# SHA256 Fingerprint: 55:41:53:b1:3d:2c:f9:dd:b7:53:bf:be:1a:4e:0a:e0:8d:0a:a4:18:70:58:fe:60:a2:b8:62:b2:e4:b8:7b:cb +-----BEGIN CERTIFICATE----- +MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQsw +CQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgw +FgYDVQRhDA9WQVRFUy1RMjgyNjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1S +Q00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4MTIyMDA5MzczM1oXDTQzMTIyMDA5 +MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQtUkNNMQ4wDAYDVQQL +DAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNBQyBS +QUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LH +sbI6GA60XYyzZl2hNPk2LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oK +Um8BA06Oi6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqGSM49BAMDA2kAMGYCMQCu +SuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoDzBOQn5IC +MQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJy +v+c= +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root R46 O=GlobalSign nv-sa +# Label: "GlobalSign Root R46" +# Serial: 1552617688466950547958867513931858518042577 +# MD5 Fingerprint: c4:14:30:e4:fa:66:43:94:2a:6a:1b:24:5f:19:d0:ef +# SHA1 Fingerprint: 53:a2:b0:4b:ca:6b:d6:45:e6:39:8a:8e:c4:0d:d2:bf:77:c3:a2:90 +# SHA256 Fingerprint: 4f:a3:12:6d:8d:3a:11:d1:c4:85:5a:4f:80:7c:ba:d6:cf:91:9d:3a:5a:88:b0:3b:ea:2c:63:72:d9:3c:40:c9 +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUA +MEYxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYD +VQQDExNHbG9iYWxTaWduIFJvb3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMy +MDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYt +c2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08EsCVeJ +OaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQG +vGIFAha/r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud +316HCkD7rRlr+/fKYIje2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo +0q3v84RLHIf8E6M6cqJaESvWJ3En7YEtbWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSE +y132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvjK8Cd+RTyG/FWaha/LIWF +zXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD412lPFzYE ++cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCN +I/onccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzs +x2sZy/N78CsHpdlseVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqa +ByFrgY/bxFn63iLABJzjqls2k+g9vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC +4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEMBQADggIBAHx4 +7PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg +JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti +2kM3S+LGteWygxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIk +pnnpHs6i58FZFZ8d4kuaPp92CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRF +FRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZmOUdkLG5NrmJ7v2B0GbhWrJKsFjLt +rWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qqJZ4d16GLuc1CLgSk +ZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwyeqiv5 +u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP +4vkYxboznxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6 +N3ec592kD3ZDZopD8p/7DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3 +vouXsXgxT7PntgMTzlSdriVZzH81Xwj3QEUxeCp6 +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Subject: CN=GlobalSign Root E46 O=GlobalSign nv-sa +# Label: "GlobalSign Root E46" +# Serial: 1552617690338932563915843282459653771421763 +# MD5 Fingerprint: b5:b8:66:ed:de:08:83:e3:c9:e2:01:34:06:ac:51:6f +# SHA1 Fingerprint: 39:b4:6c:d5:fe:80:06:eb:e2:2f:4a:bb:08:33:a0:af:db:b9:dd:84 +# SHA256 Fingerprint: cb:b9:c4:4d:84:b8:04:3e:10:50:ea:31:a6:9f:51:49:55:d7:bf:d2:e2:c6:b4:93:01:01:9a:d6:1d:9f:50:58 +-----BEGIN CERTIFICATE----- +MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYx +CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQD +ExNHbG9iYWxTaWduIFJvb3QgRTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAw +MDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +HDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkBjtjq +R+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGdd +yXqBPCCjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBQxCpCPtsad0kRLgLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ +7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZkvLtoURMMA/cVi4RguYv/Uo7njLwcAjA8 ++RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+CAezNIm8BZ/3Hobui3A= +-----END CERTIFICATE----- + +# Issuer: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Subject: CN=GLOBALTRUST 2020 O=e-commerce monitoring GmbH +# Label: "GLOBALTRUST 2020" +# Serial: 109160994242082918454945253 +# MD5 Fingerprint: 8a:c7:6f:cb:6d:e3:cc:a2:f1:7c:83:fa:0e:78:d7:e8 +# SHA1 Fingerprint: d0:67:c1:13:51:01:0c:aa:d0:c7:6a:65:37:31:16:26:4f:53:71:a2 +# SHA256 Fingerprint: 9a:29:6a:51:82:d1:d4:51:a2:e3:7f:43:9b:74:da:af:a2:67:52:33:29:f9:0f:9a:0d:20:07:c3:34:e2:3c:9a +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkG +A1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkw +FwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYx +MDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAhBgNVBAoTGmUtY29tbWVyY2UgbW9u +aXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAyMDIwMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWiD59b +RatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9Z +YybNpyrOVPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3 +QWPKzv9pj2gOlTblzLmMCcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPw +yJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCmfecqQjuCgGOlYx8ZzHyyZqjC0203b+J+ +BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKAA1GqtH6qRNdDYfOiaxaJ +SaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9ORJitHHmkH +r96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj0 +4KlGDfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9Me +dKZssCz3AwyIDMvUclOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIw +q7ejMZdnrY8XD2zHc+0klGvIg5rQmjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2 +nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1UdIwQYMBaAFNwu +H9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJC +XtzoRlgHNQIw4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd +6IwPS3BD0IL/qMy/pJTAvoe9iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf ++I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS8cE54+X1+NZK3TTN+2/BT+MAi1bi +kvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2HcqtbepBEX4tdJP7 +wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxSvTOB +TI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6C +MUO+1918oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn +4rnvyOL2NSl6dPrFf4IFYqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+I +aFvowdlxfv1k7/9nR4hYJS8+hge9+6jlgqispdNpQ80xiEmEU5LAsTkbOYMBMMTy +qfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + +# Issuer: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Subject: CN=ANF Secure Server Root CA O=ANF Autoridad de Certificacion OU=ANF CA Raiz +# Label: "ANF Secure Server Root CA" +# Serial: 996390341000653745 +# MD5 Fingerprint: 26:a6:44:5a:d9:af:4e:2f:b2:1d:b6:65:b0:4e:e8:96 +# SHA1 Fingerprint: 5b:6e:68:d0:cc:15:b6:a0:5f:1e:c1:5f:ae:02:fc:6b:2f:5d:6f:74 +# SHA256 Fingerprint: fb:8f:ec:75:91:69:b9:10:6b:1e:51:16:44:c6:18:c5:13:04:37:3f:6c:06:43:08:8d:8b:ef:fd:1b:99:75:99 +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum EC-384 CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum EC-384 CA" +# Serial: 160250656287871593594747141429395092468 +# MD5 Fingerprint: b6:65:b3:96:60:97:12:a1:ec:4e:e1:3d:a3:c6:c9:f1 +# SHA1 Fingerprint: f3:3e:78:3c:ac:df:f4:a2:cc:ac:67:55:69:56:d7:e5:16:3c:e1:ed +# SHA256 Fingerprint: 6b:32:80:85:62:53:18:aa:50:d1:73:c9:8d:8b:da:09:d5:7e:27:41:3d:11:4c:f7:87:a0:f5:d0:6c:03:0c:f6 +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQsw +CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScw +JQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMT +EENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2MDcyNDU0WhcNNDMwMzI2MDcyNDU0 +WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBT +LkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGTAX +BgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATE +KI6rGFtqvm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7Tm +Fy8as10CW4kjPMIRBSqniBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68Kj +QjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI0GZnQkdjrzife81r1HfS+8 +EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjADVS2m5hjEfO/J +UG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0QoSZ/6vn +nvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= +-----END CERTIFICATE----- + +# Issuer: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Subject: CN=Certum Trusted Root CA O=Asseco Data Systems S.A. OU=Certum Certification Authority +# Label: "Certum Trusted Root CA" +# Serial: 40870380103424195783807378461123655149 +# MD5 Fingerprint: 51:e1:c2:e7:fe:4c:84:af:59:0e:2f:f4:54:6f:ea:29 +# SHA1 Fingerprint: c8:83:44:c0:18:ae:9f:cc:f1:87:b7:8f:22:d1:c5:d7:45:84:ba:e5 +# SHA256 Fingerprint: fe:76:96:57:38:55:77:3e:37:a9:5e:7a:d4:d9:cc:96:c3:01:57:c1:5d:31:76:5b:a9:b1:57:04:e1:ae:78:fd +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +# Issuer: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Subject: CN=TunTrust Root CA O=Agence Nationale de Certification Electronique +# Label: "TunTrust Root CA" +# Serial: 108534058042236574382096126452369648152337120275 +# MD5 Fingerprint: 85:13:b9:90:5b:36:5c:b6:5e:b8:5a:f8:e0:31:57:b4 +# SHA1 Fingerprint: cf:e9:70:84:0f:e0:73:0f:9d:f6:0c:7f:2c:4b:ee:20:46:34:9c:bb +# SHA256 Fingerprint: 2e:44:10:2a:b5:8c:b8:54:19:45:1c:8e:19:d9:ac:f3:66:2c:af:bc:61:4b:6a:53:96:0a:30:f7:d0:e2:eb:41 +-----BEGIN CERTIFICATE----- +MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQEL +BQAwYTELMAkGA1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUg +Q2VydGlmaWNhdGlvbiBFbGVjdHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJv +b3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQwNDI2MDg1NzU2WjBhMQswCQYDVQQG +EwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBDZXJ0aWZpY2F0aW9u +IEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIwDQYJ +KoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZ +n56eY+hz2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd +2JQDoOw05TDENX37Jk0bbjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgF +VwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZ +GoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAdgjH8KcwAWJeRTIAAHDOF +li/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViWVSHbhlnU +r8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2 +eY8fTpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIb +MlEsPvLfe/ZdeikZjuXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISg +jwBUFfyRbVinljvrS5YnzWuioYasDXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB +7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwSVXAkPcvCFDVDXSdOvsC9qnyW +5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI04Y+oXNZtPdE +ITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 +90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+z +xiD2BkewhpMl0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYu +QEkHDVneixCwSQXi/5E/S7fdAo74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4 +FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRYYdZ2vyJ/0Adqp2RT8JeNnYA/u8EH +22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJpadbGNjHh/PqAulxP +xOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65xxBzn +dFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5 +Xc0yGYuPjCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7b +nV2UqL1g52KAdoGDDIzMMEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQ +CvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9zZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZH +u/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3rAZ3r2OvEhJn7wAzMMujj +d9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS RSA Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS RSA Root CA 2021" +# Serial: 76817823531813593706434026085292783742 +# MD5 Fingerprint: 65:47:9b:58:86:dd:2c:f0:fc:a2:84:1f:1e:96:c4:91 +# SHA1 Fingerprint: 02:2d:05:82:fa:88:ce:14:0c:06:79:de:7f:14:10:e9:45:d7:a5:6d +# SHA256 Fingerprint: d9:5d:0e:8e:da:79:52:5b:f9:be:b1:1b:14:d2:10:0d:32:94:98:5f:0c:62:d9:fa:bd:9c:d9:99:ec:cb:7b:1d +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl +c2VhcmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0Eg +Um9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUzOFoXDTQ1MDIxMzEwNTUzN1owbDEL +MAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl +YXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569l +mwVnlskNJLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE +4VGC/6zStGndLuwRo0Xua2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uv +a9of08WRiFukiZLRgeaMOVig1mlDqa2YUlhu2wr7a89o+uOkXjpFc5gH6l8Cct4M +pbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K5FrZx40d/JiZ+yykgmvw +Kh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEvdmn8kN3b +LW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcY +AuUR0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqB +AGMUuTNe3QvboEUHGjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYq +E613TBoYm5EPWNgGVMWX+Ko/IIqmhaZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHr +W2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQCPxrvrNQKlr9qEgYRtaQQJKQ +CoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAU +X15QvWiWkKQUEapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3 +f5Z2EMVGpdAgS1D0NTsY9FVqQRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxaja +H6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxDQpSbIPDRzbLrLFPCU3hKTwSUQZqP +JzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcRj88YxeMn/ibvBZ3P +zzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5vZSt +jBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0 +/L5H9MG0qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pT +BGIBnfHAT+7hOtSLIBD6Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79 +aPib8qXPMThcFarmlwDB31qlpzmq6YR/PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YW +xw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnnkf3/W9b3raYvAwtt41dU +63ZTGI0RmLo= +-----END CERTIFICATE----- + +# Issuer: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Subject: CN=HARICA TLS ECC Root CA 2021 O=Hellenic Academic and Research Institutions CA +# Label: "HARICA TLS ECC Root CA 2021" +# Serial: 137515985548005187474074462014555733966 +# MD5 Fingerprint: ae:f7:4c:e5:66:35:d1:b7:9b:8c:22:93:74:d3:4b:b0 +# SHA1 Fingerprint: bc:b0:c1:9d:e9:98:92:70:19:38:57:e9:8d:a7:b4:5d:6e:ee:01:48 +# SHA256 Fingerprint: 3f:99:cc:47:4a:cf:ce:4d:fe:d5:87:94:66:5e:47:8d:15:47:73:9f:2e:78:0f:1b:b4:ca:9b:13:30:97:d4:01 +-----BEGIN CERTIFICATE----- +MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQsw +CQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2Vh +cmNoIEluc3RpdHV0aW9ucyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9v +dCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoXDTQ1MDIxMzExMDEwOVowbDELMAkG +A1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj +aCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7 +KKrxcm1lAEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9Y +STHMmE5gEYd103KUkE+bECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAircJRQO9gcS3ujwLEXQNw +SaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/QwCZ61IygN +nxS2PFOiTAZpffpskcYqSUXm7LcT4Tps +-----END CERTIFICATE----- + +# Issuer: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Subject: CN=Autoridad de Certificacion Firmaprofesional CIF A62634068 +# Label: "Autoridad de Certificacion Firmaprofesional CIF A62634068" +# Serial: 1977337328857672817 +# MD5 Fingerprint: 4e:6e:9b:54:4c:ca:b7:fa:48:e4:90:b1:15:4b:1c:a3 +# SHA1 Fingerprint: 0b:be:c2:27:22:49:cb:39:aa:db:35:5c:53:e3:8c:ae:78:ff:b6:fe +# SHA256 Fingerprint: 57:de:05:83:ef:d2:b2:6e:03:61:da:99:da:9d:f4:64:8d:ef:7e:e8:44:1c:3b:72:8a:fa:9b:cd:e0:f9:b2:6a +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE +BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h +cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1 +MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg +Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 +thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM +cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG +L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i +NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h +X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b +m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy +Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja +EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T +KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF +6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh +OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc +tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd +IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j +b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC +AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw +ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m +iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF +Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ +hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P +Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE +EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV +1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t +CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR +5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw +f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9 +ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK +GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV +-----END CERTIFICATE----- + +# Issuer: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus ECC Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus ECC Root CA" +# Serial: 630369271402956006249506845124680065938238527194 +# MD5 Fingerprint: de:4b:c1:f5:52:8c:9b:43:e1:3e:8f:55:54:17:8d:85 +# SHA1 Fingerprint: f6:9c:db:b0:fc:f6:02:13:b6:52:32:a6:a3:91:3f:16:70:da:c3:e1 +# SHA256 Fingerprint: 30:fb:ba:2c:32:23:8e:2a:98:54:7a:f9:79:31:e5:50:42:8b:9b:3f:1c:8e:eb:66:33:dc:fa:86:c5:b2:7d:d3 +-----BEGIN CERTIFICATE----- +MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMw +RzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAY +BgNVBAMTEXZUcnVzIEVDQyBSb290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDcz +MTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28u +LEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYwEAYHKoZIzj0CAQYF +K4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+cToL0 +v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUd +e4BdS49nTPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIw +V53dVvHH4+m4SVBrm2nDb+zDfSXkV5UTQJtS0zvzQBm8JsctBp61ezaf9SXUY2sA +AjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQLYgmRWAD5Tfs0aNoJrSEG +GJTO +-----END CERTIFICATE----- + +# Issuer: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Subject: CN=vTrus Root CA O=iTrusChina Co.,Ltd. +# Label: "vTrus Root CA" +# Serial: 387574501246983434957692974888460947164905180485 +# MD5 Fingerprint: b8:c9:37:df:fa:6b:31:84:64:c5:ea:11:6a:1b:75:fc +# SHA1 Fingerprint: 84:1a:69:fb:f5:cd:1a:25:34:13:3d:e3:f8:fc:b8:99:d0:c9:14:b7 +# SHA256 Fingerprint: 8a:71:de:65:59:33:6f:42:6c:26:e5:38:80:d0:0d:88:a1:8d:a4:c6:a9:1f:0d:cb:61:94:e2:06:c5:c9:63:87 +-----BEGIN CERTIFICATE----- +MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQEL +BQAwQzELMAkGA1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4x +FjAUBgNVBAMTDXZUcnVzIFJvb3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMx +MDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoGA1UEChMTaVRydXNDaGluYSBDby4s +THRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZotsSKYc +IrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykU +AyyNJJrIZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+ +GrPSbcKvdmaVayqwlHeFXgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z9 +8Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KAYPxMvDVTAWqXcoKv8R1w6Jz1717CbMdH +flqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70kLJrxLT5ZOrpGgrIDajt +J8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2AXPKBlim +0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZN +pGvu/9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQ +UqqzApVg+QxMaPnu1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHW +OXSuTEGC2/KmSNGzm/MzqvOmwMVO9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMB +AAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYgscasGrz2iTAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAKbqSSaet +8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd +nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1j +bhd47F18iMjrjld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvM +Kar5CKXiNxTKsbhm7xqC5PD48acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIiv +TDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJnxDHO2zTlJQNgJXtxmOTAGytfdELS +S8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554WgicEFOwE30z9J4nfr +I8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4sEb9 +b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNB +UvupLnKWnyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1P +Ti07NEPhmg4NpGaXutIcSkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929ven +sBxXVsFy6K2ir40zSbofitzmdHxghm+Hl3s= +-----END CERTIFICATE----- + +# Issuer: CN=ISRG Root X2 O=Internet Security Research Group +# Subject: CN=ISRG Root X2 O=Internet Security Research Group +# Label: "ISRG Root X2" +# Serial: 87493402998870891108772069816698636114 +# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5 +# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af +# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70 +-----BEGIN CERTIFICATE----- +MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw +CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg +R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00 +MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT +ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw +EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW ++1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9 +ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI +zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW +tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1 +/q4AaOeMSQ+2b1tbFfLn +-----END CERTIFICATE----- + +# Issuer: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Subject: CN=HiPKI Root CA - G1 O=Chunghwa Telecom Co., Ltd. +# Label: "HiPKI Root CA - G1" +# Serial: 60966262342023497858655262305426234976 +# MD5 Fingerprint: 69:45:df:16:65:4b:e8:68:9a:8f:76:5f:ff:80:9e:d3 +# SHA1 Fingerprint: 6a:92:e4:a8:ee:1b:ec:96:45:37:e3:29:57:49:cd:96:e3:e5:d2:60 +# SHA256 Fingerprint: f0:15:ce:3c:c2:39:bf:ef:06:4b:e9:f1:d2:c4:17:e1:a0:26:4a:0a:94:be:1f:0c:8d:12:18:64:eb:69:49:cc +-----BEGIN CERTIFICATE----- +MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 +ZC4xGzAZBgNVBAMMEkhpUEtJIFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRa +Fw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3 +YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kgUm9vdCBDQSAtIEcx +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0o9Qw +qNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twv +Vcg3Px+kwJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6 +lZgRZq2XNdZ1AYDgr/SEYYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnz +Qs7ZngyzsHeXZJzA9KMuH5UHsBffMNsAGJZMoYFL3QRtU6M9/Aes1MU3guvklQgZ +KILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfdhSi8MEyr48KxRURHH+CK +FgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj1jOXTyFj +HluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDr +y+K49a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ +/W3c1pzAtH2lsN0/Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgM +a/aOEmem8rJY5AIJEzypuxC00jBF8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6 +fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQDAgGGMA0GCSqG +SIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi +7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqc +SE5XCV0vrPSltJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6Fza +ZsT0pPBWGTMpWmWSBUdGSquEwx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9Tc +XzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07QJNBAsNB1CI69aO4I1258EHBGG3zg +iLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv5wiZqAxeJoBF1Pho +L5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+GpzjLrF +Ne85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wr +kkVbbiVghUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+ +vhV4nYWBSipX3tUZQ9rbyltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQU +YDksswBVLuT1sw5XxJFBAJw/6KXf6vb/yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== +-----END CERTIFICATE----- + +# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R4 +# Label: "GlobalSign ECC Root CA - R4" +# Serial: 159662223612894884239637590694 +# MD5 Fingerprint: 26:29:f8:6d:e1:88:bf:a2:65:7f:aa:c4:cd:0f:7f:fc +# SHA1 Fingerprint: 6b:a0:b0:98:e1:71:ef:5a:ad:fe:48:15:80:77:10:f4:bd:6f:0b:28 +# SHA256 Fingerprint: b0:85:d7:0b:96:4f:19:1a:73:e4:af:0d:54:ae:7a:0e:07:aa:fd:af:9b:71:dd:08:62:13:8a:b7:32:5a:24:a2 +-----BEGIN CERTIFICATE----- +MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD +VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh +bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw +MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g +UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx +uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV +HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/ ++wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147 +bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R1 O=Google Trust Services LLC +# Subject: CN=GTS Root R1 O=Google Trust Services LLC +# Label: "GTS Root R1" +# Serial: 159662320309726417404178440727 +# MD5 Fingerprint: 05:fe:d0:bf:71:a8:a3:76:63:da:01:e0:d8:52:dc:40 +# SHA1 Fingerprint: e5:8c:1c:c4:91:3b:38:63:4b:e9:10:6e:e3:ad:8e:6b:9d:d9:81:4a +# SHA256 Fingerprint: d9:47:43:2a:bd:e7:b7:fa:90:fc:2e:6b:59:10:1b:12:80:e0:e1:c7:e4:e4:0f:a3:c6:88:7f:ff:57:a7:f4:cf +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo +27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w +Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw +TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl +qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH +szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8 +Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk +MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92 +wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p +aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN +VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb +C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe +QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy +h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4 +7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J +ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef +MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/ +Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT +6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ +0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm +2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb +bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R2 O=Google Trust Services LLC +# Subject: CN=GTS Root R2 O=Google Trust Services LLC +# Label: "GTS Root R2" +# Serial: 159662449406622349769042896298 +# MD5 Fingerprint: 1e:39:c0:53:e6:1e:29:82:0b:ca:52:55:36:5d:57:dc +# SHA1 Fingerprint: 9a:44:49:76:32:db:de:fa:d0:bc:fb:5a:7b:17:bd:9e:56:09:24:94 +# SHA256 Fingerprint: 8d:25:cd:97:22:9d:bf:70:35:6b:da:4e:b3:cc:73:40:31:e2:4c:f0:0f:af:cf:d3:2d:c7:6e:b5:84:1c:7e:a8 +-----BEGIN CERTIFICATE----- +MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt +nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY +6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu +MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k +RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg +f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV ++3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo +dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW +Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa +G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq +gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID +AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H +vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 +0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC +B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u +NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg +yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev +HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6 +xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR +TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg +JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV +7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl +6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R3 O=Google Trust Services LLC +# Subject: CN=GTS Root R3 O=Google Trust Services LLC +# Label: "GTS Root R3" +# Serial: 159662495401136852707857743206 +# MD5 Fingerprint: 3e:e7:9d:58:02:94:46:51:94:e5:e0:22:4a:8b:e7:73 +# SHA1 Fingerprint: ed:e5:71:80:2b:c8:92:b9:5b:83:3c:d2:32:68:3f:09:cd:a0:1e:46 +# SHA256 Fingerprint: 34:d8:a7:3e:e2:08:d9:bc:db:0d:95:65:20:93:4b:4e:40:e6:94:82:59:6e:8b:6f:73:c8:42:6b:01:0a:6f:48 +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G +jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2 +4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7 +VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm +ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X +-----END CERTIFICATE----- + +# Issuer: CN=GTS Root R4 O=Google Trust Services LLC +# Subject: CN=GTS Root R4 O=Google Trust Services LLC +# Label: "GTS Root R4" +# Serial: 159662532700760215368942768210 +# MD5 Fingerprint: 43:96:83:77:19:4d:76:b3:9d:65:52:e4:1d:22:a5:e8 +# SHA1 Fingerprint: 77:d3:03:67:b5:e0:0c:15:f6:0c:38:61:df:7c:e1:3b:92:46:4d:47 +# SHA256 Fingerprint: 34:9d:fa:40:58:c5:e2:63:12:3b:39:8a:e7:95:57:3c:4e:13:13:c8:3f:e6:8f:93:55:6c:d5:e8:03:1b:3c:7d +-----BEGIN CERTIFICATE----- +MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD +VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG +A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw +WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz +IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi +QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR +HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW +BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D +9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8 +p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD +-----END CERTIFICATE----- + +# Issuer: CN=Telia Root CA v2 O=Telia Finland Oyj +# Subject: CN=Telia Root CA v2 O=Telia Finland Oyj +# Label: "Telia Root CA v2" +# Serial: 7288924052977061235122729490515358 +# MD5 Fingerprint: 0e:8f:ac:aa:82:df:85:b1:f4:dc:10:1c:fc:99:d9:48 +# SHA1 Fingerprint: b9:99:cd:d1:73:50:8a:c4:47:05:08:9c:8c:88:fb:be:a0:2b:40:cd +# SHA256 Fingerprint: 24:2b:69:74:2f:cb:1e:5b:2a:bf:98:89:8b:94:57:21:87:54:4e:5b:4d:99:11:78:65:73:62:1f:6a:74:b8:2c +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQx +CzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UE +AwwQVGVsaWEgUm9vdCBDQSB2MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1 +NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZ +MBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ76zBq +AMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9 +vVYiQJ3q9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9 +lRdU2HhE8Qx3FZLgmEKnpNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTOD +n3WhUidhOPFZPY5Q4L15POdslv5e2QJltI5c0BE0312/UqeBAMN/mUWZFdUXyApT +7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW5olWK8jjfN7j/4nlNW4o +6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNrRBH0pUPC +TEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6 +WT0EBXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63R +DolUK5X6wK0dmBR4M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZI +pEYslOqodmJHixBTB0hXbOKSTbauBcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGj +YzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7Wxy+G2CQ5MB0GA1UdDgQWBBRy +rOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ +8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi +0f6X+J8wfBj5tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMM +A8iZGok1GTzTyVR8qPAs5m4HeW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBS +SRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+Cy748fdHif64W1lZYudogsYMVoe+K +TTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygCQMez2P2ccGrGKMOF +6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15h2Er +3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMt +Ty3EHD70sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pT +VmBds9hCG1xLEooc6+t9xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAW +ysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQraVplI/owd8k+BsHMYeB2F326CjYSlKA +rBPuUBQemMc= +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST BR Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST BR Root CA 1 2020" +# Serial: 165870826978392376648679885835942448534 +# MD5 Fingerprint: b5:aa:4b:d5:ed:f7:e3:55:2e:8f:72:0a:f3:75:b8:ed +# SHA1 Fingerprint: 1f:5b:98:f0:e3:b5:f7:74:3c:ed:e6:b0:36:7d:32:cd:f4:09:41:67 +# SHA256 Fingerprint: e5:9a:aa:81:60:09:c2:2b:ff:5b:25:ba:d3:7d:f3:06:f0:49:79:7c:1f:81:d8:5a:b0:89:e6:57:bd:8f:00:44 +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEJSIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5 +NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7dPYS +zuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0 +QVK5buXuQqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/ +VbNafAkl1bK6CKBrqx9tMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2JyX3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFW +wKrY7RjEsK70PvomAjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHV +dWNbFJWcHwHP2NVypw87 +-----END CERTIFICATE----- + +# Issuer: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Subject: CN=D-TRUST EV Root CA 1 2020 O=D-Trust GmbH +# Label: "D-TRUST EV Root CA 1 2020" +# Serial: 126288379621884218666039612629459926992 +# MD5 Fingerprint: 8c:2d:9d:70:9f:48:99:11:06:11:fb:e9:cb:30:c0:6e +# SHA1 Fingerprint: 61:db:8c:21:59:69:03:90:d8:7c:9c:12:86:54:cf:9d:3d:f4:dd:07 +# SHA256 Fingerprint: 08:17:0d:1a:a3:64:53:90:1a:2f:95:92:45:e3:47:db:0c:8d:37:ab:aa:bc:56:b8:1a:a1:00:dc:95:89:70:db +-----BEGIN CERTIFICATE----- +MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQsw +CQYDVQQGEwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRS +VVNUIEVWIFJvb3QgQ0EgMSAyMDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5 +NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAG +A1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAGByqGSM49AgEGBSuB +BAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8ZRCC +/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rD +wpdhQntJraOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3 +OqQo5FD4pPfsazK2/umLMA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6g +PKA6hjhodHRwOi8vY3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X2V2X3Jvb3Rf +Y2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVjdG9yeS5kLXRydXN0Lm5l +dC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxPPUQtVHJ1 +c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjO +PQQDAwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CA +y/m0sRtW9XLS/BnRAjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJb +gfM0agPnIjhQW+0ZT0MW +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS ECC P384 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS ECC P384 Root G5" +# Serial: 13129116028163249804115411775095713523 +# MD5 Fingerprint: d3:71:04:6a:43:1c:db:a6:59:e1:a8:a3:aa:c5:71:ed +# SHA1 Fingerprint: 17:f3:de:5e:9f:0f:19:e9:8e:f6:1f:32:26:6e:20:c4:07:ae:30:ee +# SHA256 Fingerprint: 01:8e:13:f0:77:25:32:cf:80:9b:d1:b1:72:81:86:72:83:fc:48:c6:e1:3b:e9:c6:98:12:85:4a:49:0c:1b:05 +-----BEGIN CERTIFICATE----- +MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp +Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2 +MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS +7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp +0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS +B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 +BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ +LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4 +DXZDjC5Ty3zfDBeWUA== +-----END CERTIFICATE----- + +# Issuer: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Subject: CN=DigiCert TLS RSA4096 Root G5 O=DigiCert, Inc. +# Label: "DigiCert TLS RSA4096 Root G5" +# Serial: 11930366277458970227240571539258396554 +# MD5 Fingerprint: ac:fe:f7:34:96:a9:f2:b3:b4:12:4b:e4:27:41:6f:e1 +# SHA1 Fingerprint: a7:88:49:dc:5d:7c:75:8c:8c:de:39:98:56:b3:aa:d0:b2:a5:71:35 +# SHA256 Fingerprint: 37:1a:00:dc:05:33:b3:72:1a:7e:eb:40:e8:41:9e:70:79:9d:2b:0a:0f:2c:1d:80:69:31:65:f7:ce:c4:ad:75 +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBN +MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMT +HERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcN +NDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +IEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2IFJvb3QgRzUwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS87IE+ +ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG0 +2C+JFvuUAT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgp +wgscONyfMXdcvyej/Cestyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZM +pG2T6T867jp8nVid9E6P/DsjyG244gXazOvswzH016cpVIDPRFtMbzCe88zdH5RD +nU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnVDdXifBBiqmvwPXbzP6Po +sMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9qTXeXAaDx +Zre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cd +Lvvyz6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvX +KyY//SovcfXWJL5/MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNe +XoVPzthwiHvOAbWWl9fNff2C+MIkwcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPL +tgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4EFgQUUTMc7TZArxfTJc1paPKv +TiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN +AQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw +GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7H +PNtQOa27PShNlnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLF +O4uJ+DQtpBflF+aZfTCIITfNMBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQ +REtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/u4cnYiWB39yhL/btp/96j1EuMPik +AdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9GOUrYU9DzLjtxpdRv +/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh47a+ +p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilw +MUc/dNAUFvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WF +qUITVuwhd4GTWgzqltlJyqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCK +ovfepEWFJqgejF0pW8hL2JpqA15w8oVPbEtoL8pU9ozaMv7Da4M/OMZ+ +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root R1 O=Certainly +# Subject: CN=Certainly Root R1 O=Certainly +# Label: "Certainly Root R1" +# Serial: 188833316161142517227353805653483829216 +# MD5 Fingerprint: 07:70:d4:3e:82:87:a0:fa:33:36:13:f4:fa:33:e7:12 +# SHA1 Fingerprint: a0:50:ee:0f:28:71:f4:27:b2:12:6d:6f:50:96:25:ba:cc:86:42:af +# SHA256 Fingerprint: 77:b8:2c:d8:64:4c:43:05:f7:ac:c5:cb:15:6b:45:67:50:04:03:3d:51:c6:0c:62:02:a8:e0:c3:34:67:d3:a0 +-----BEGIN CERTIFICATE----- +MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAw +PTELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2Vy +dGFpbmx5IFJvb3QgUjEwHhcNMjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9 +MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0 +YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANA2 +1B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O5MQT +vqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbed +aFySpvXl8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b0 +1C7jcvk2xusVtyWMOvwlDbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5 +r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGIXsXwClTNSaa/ApzSRKft43jvRl5tcdF5 +cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkNKPl6I7ENPT2a/Z2B7yyQ +wHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQAjeZjOVJ +6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA +2CnbrlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyH +Wyf5QBGenDPBt+U1VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMR +eiFPCyEQtkA6qyI6BJyLm4SGcprSp6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTgqj8ljZ9EXME66C6u +d0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAszHQNTVfSVcOQr +PbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d +8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi +1wrykXprOQ4vMMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrd +rRT90+7iIgXr0PK3aBLXWopBGsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9di +taY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+gjwN/KUD+nsa2UUeYNrEjvn8K8l7 +lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgHJBu6haEaBQmAupVj +yTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7fpYn +Kx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLy +yCwzk5Iwx06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5n +wXARPbv0+Em34yaXOp/SX3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6 +OV+KmalBWQewLK8= +-----END CERTIFICATE----- + +# Issuer: CN=Certainly Root E1 O=Certainly +# Subject: CN=Certainly Root E1 O=Certainly +# Label: "Certainly Root E1" +# Serial: 8168531406727139161245376702891150584 +# MD5 Fingerprint: 0a:9e:ca:cd:3e:52:50:c6:36:f3:4b:a3:ed:a7:53:e9 +# SHA1 Fingerprint: f9:e1:6d:dc:01:89:cf:d5:82:45:63:3e:c5:37:7d:c2:eb:93:6f:2b +# SHA256 Fingerprint: b4:58:5f:22:e4:ac:75:6a:4e:86:12:a1:36:1c:5d:9d:03:1a:93:fd:84:fe:bb:77:8f:a3:06:8b:0f:c4:2d:c2 +-----BEGIN CERTIFICATE----- +MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQsw +CQYDVQQGEwJVUzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlu +bHkgUm9vdCBFMTAeFw0yMTA0MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJ +BgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlubHkxGjAYBgNVBAMTEUNlcnRhaW5s +eSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4fxzf7flHh4axpMCK ++IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9YBk2 +QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4 +hevIIgcwCgYIKoZIzj0EAwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozm +ut6Dacpps6kFtZaSF4fC0urQe87YQVt8rgIwRt7qy12a7DLCZRawTDBcMPPaTnOG +BtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication RootCA3 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication RootCA3" +# Serial: 16247922307909811815 +# MD5 Fingerprint: 1c:9a:16:ff:9e:5c:e0:4d:8a:14:01:f4:35:5d:29:26 +# SHA1 Fingerprint: c3:03:c8:22:74:92:e5:61:a2:9c:5f:79:91:2b:1e:44:13:91:30:3a +# SHA256 Fingerprint: 24:a5:5c:2a:b0:51:44:2d:06:17:76:65:41:23:9a:4a:d0:32:d7:c5:51:75:aa:34:ff:de:2f:bc:4f:5c:52:94 +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNV +BAYTAkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScw +JQYDVQQDEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2 +MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UEAxMeU2VjdXJpdHkg +Q29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4r +CmDvu20rhvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzA +lrenfna84xtSGc4RHwsENPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MG +TfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF7 +9+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGmnpjKIG58u4iFW/vAEGK7 +8vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtYXLVqAvO4 +g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3we +GVPKp7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst ++3A7caoreyYn8xrC3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M +0V9hvqG8OmpI6iZVIhZdXw3/JzOfGAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQ +T9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0VcwCBEF/VfR2ccCAwEAAaNCMEAw +HQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PA +FNr0Y/Dq9HHuTofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd +9XbXv8S2gVj/yP9kaWJ5rW4OH3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQI +UYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASxYfQAW0q3nHE3GYV5v4GwxxMOdnE+ +OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZXSEIx2C/pHF7uNke +gr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml+LLf +iAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUV +nuiZIesnKwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD +2NCcnWXL0CsnMQMeNuE9dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI// +1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm6Vwdp6POXiUyK+OVrCoHzrQoeIY8Laad +TdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +# Issuer: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Subject: CN=Security Communication ECC RootCA1 O=SECOM Trust Systems CO.,LTD. +# Label: "Security Communication ECC RootCA1" +# Serial: 15446673492073852651 +# MD5 Fingerprint: 7e:43:b0:92:68:ec:05:43:4c:98:ab:5d:35:2e:7e:86 +# SHA1 Fingerprint: b8:0e:26:a9:bf:d2:b2:3b:c0:ef:46:c9:ba:c7:bb:f6:1d:0d:41:41 +# SHA256 Fingerprint: e7:4f:bd:a5:5b:d5:64:c4:73:a3:6b:44:1a:a7:99:c8:a6:8e:07:74:40:e8:28:8b:9f:a1:e5:0e:4b:ba:ca:11 +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYT +AkpQMSUwIwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYD +VQQDEyJTZWN1cml0eSBDb21tdW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYx +NjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTELMAkGA1UEBhMCSlAxJTAjBgNVBAoT +HFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNVBAMTIlNlY3VyaXR5 +IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+Cnnfdl +dB9sELLo5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpK +ULGjQjBAMB0GA1UdDgQWBBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu +9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3LsnNdo4gIxwwCMQDAqy0O +be0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70eN9k= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA1 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA1" +# Serial: 113562791157148395269083148143378328608 +# MD5 Fingerprint: 42:32:99:76:43:33:36:24:35:07:82:9b:28:f9:d0:90 +# SHA1 Fingerprint: d5:ec:8d:7b:4c:ba:79:f4:e7:e8:cb:9d:6b:ae:77:83:10:03:21:6a +# SHA256 Fingerprint: f3:89:6f:88:fe:7c:0a:88:27:66:a7:fa:6a:d2:74:9f:b5:7a:7f:3e:98:fb:76:9c:1f:a7:b0:9c:2c:44:d5:ae +-----BEGIN CERTIFICATE----- +MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBU +MQswCQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRI +T1JJVFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAz +MTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJF +SUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2Jh +bCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFmCL3Z +xRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZ +spDyRhySsTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O5 +58dnJCNPYwpj9mZ9S1WnP3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgR +at7GGPZHOiJBhyL8xIkoVNiMpTAK+BcWyqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll +5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRjeulumijWML3mG90Vr4Tq +nMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNnMoH1V6XK +V0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/ +pj+bOT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZO +z2nxbkRs1CTqjSShGL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXn +jSXWgXSHRtQpdaJCbPdzied9v3pKH9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+ +WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMBAAGjQjBAMB0GA1UdDgQWBBTF +7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 +YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3Kli +awLwQ8hOnThJdMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u ++2D2/VnGKhs/I0qUJDAnyIm860Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88 +X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuhTaRjAv04l5U/BXCga99igUOLtFkN +SoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW4AB+dAb/OMRyHdOo +P2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmpGQrI ++pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRz +znfSxqxx4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9 +eVzYH6Eze9mCUAyTF6ps3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2 +YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4SSPfSKcOYKMryMguTjClPPGAyzQWWYezy +r/6zcCwupvI= +-----END CERTIFICATE----- + +# Issuer: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Subject: CN=BJCA Global Root CA2 O=BEIJING CERTIFICATE AUTHORITY +# Label: "BJCA Global Root CA2" +# Serial: 58605626836079930195615843123109055211 +# MD5 Fingerprint: 5e:0a:f6:47:5f:a6:14:e8:11:01:95:3f:4d:01:eb:3c +# SHA1 Fingerprint: f4:27:86:eb:6e:b8:6d:88:31:67:02:fb:ba:66:a4:53:00:aa:7a:a6 +# SHA256 Fingerprint: 57:4d:f6:93:1e:27:80:39:66:7b:72:0a:fd:c1:60:0f:c2:7e:b6:6d:d3:09:29:79:fb:73:85:64:87:21:28:82 +-----BEGIN CERTIFICATE----- +MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQsw +CQYDVQQGEwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJ +VFkxHTAbBgNVBAMMFEJKQ0EgR2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgy +MVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJ +TkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRCSkNBIEdsb2JhbCBS +b290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jlSR9B +IgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK+ ++kpRuDCK/eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJK +sVF/BvDRgh9Obl+rg/xI1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA +94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8gUXOQwKhbYdDFUDn9hf7B +43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root E46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root E46" +# Serial: 88989738453351742415770396670917916916 +# MD5 Fingerprint: 28:23:f8:b2:98:5c:37:16:3b:3e:46:13:4e:b0:b3:01 +# SHA1 Fingerprint: ec:8a:39:6c:40:f0:2e:bc:42:75:d4:9f:ab:1c:1a:5b:67:be:d2:9a +# SHA256 Fingerprint: c9:0f:26:f0:fb:1b:40:18:b2:22:27:51:9b:5c:a2:b5:3e:2c:a5:b3:be:5c:f1:8e:fe:1b:ef:47:38:0c:53:83 +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQsw +CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcN +MjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYG +A1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccC +WvkEN/U0NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+ +6xnOQ6OjQjBAMB0GA1UdDgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8B +Af8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNnADBkAjAn7qRa +qCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RHlAFWovgzJQxC36oCMB3q +4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21USAGKcw== +-----END CERTIFICATE----- + +# Issuer: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Subject: CN=Sectigo Public Server Authentication Root R46 O=Sectigo Limited +# Label: "Sectigo Public Server Authentication Root R46" +# Serial: 156256931880233212765902055439220583700 +# MD5 Fingerprint: 32:10:09:52:00:d5:7e:6c:43:df:15:c0:b1:16:93:e5 +# SHA1 Fingerprint: ad:98:f9:f3:e4:7d:75:3b:65:d4:82:b3:a4:52:17:bb:6e:f5:e4:38 +# SHA256 Fingerprint: 7b:b6:47:a6:2a:ee:ac:88:bf:25:7a:a5:22:d0:1f:fe:a3:95:e0:ab:45:c7:3f:93:f6:56:54:ec:38:f2:5a:06 +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBf +MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQD +Ey1TZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYw +HhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5WjBfMQswCQYDVQQGEwJHQjEY +MBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1Ymxp +YyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDa +ef0rty2k1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnz +SDBh+oF8HqcIStw+KxwfGExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xf +iOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMPFF1bFOdLvt30yNoDN9HWOaEhUTCDsG3X +ME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vuZDCQOc2TZYEhMbUjUDM3 +IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5QazYw6A3OAS +VYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgE +SJ/AwSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu ++Zd4KKTIRJLpfSYFplhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt +8uaZFURww3y8nDnAtOFr94MlI1fZEoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+L +HaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW6aWWrL3DkJiy4Pmi1KZHQ3xt +zwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWIIUkwDgYDVR0P +AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQ +YKlJfp/imTYpE0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52 +gDY9hAaLMyZlbcp+nv4fjFg4exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZA +Fv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M0ejf5lG5Nkc/kLnHvALcWxxPDkjB +JYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI84HxZmduTILA7rpX +DhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9mpFui +TdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5 +dHn5HrwdVw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65 +LvKRRFHQV80MNNVIIb/bE/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp +0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmmJ1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAY +QqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS RSA Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS RSA Root CA 2022" +# Serial: 148535279242832292258835760425842727825 +# MD5 Fingerprint: d8:4e:c6:59:30:d8:fe:a0:d6:7a:5a:2c:2c:69:78:da +# SHA1 Fingerprint: ec:2c:83:40:72:af:26:95:10:ff:0e:f2:03:ee:31:70:f6:78:9d:ca +# SHA256 Fingerprint: 8f:af:7d:2e:2c:b4:70:9b:b8:e0:b3:36:66:bf:75:a5:dd:45:b5:de:48:0f:8e:a8:d4:bf:e6:be:bc:17:f2:ed +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBO +MQswCQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQD +DBxTU0wuY29tIFRMUyBSU0EgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloX +DTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jw +b3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJvb3QgQ0EgMjAyMjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u9nTP +L3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OY +t6/wNr/y7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0ins +S657Lb85/bRi3pZ7QcacoOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3 +PnxEX4MN8/HdIGkWCVDi1FW24IBydm5MR7d1VVm0U3TZlMZBrViKMWYPHqIbKUBO +L9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDGD6C1vBdOSHtRwvzpXGk3 +R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEWTO6Af77w +dr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS ++YCk8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYS +d66UNHsef8JmAOSqg+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoG +AtUjHBPW6dvbxrB6y3snm/vg1UYk7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2f +gTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsuN+7jhHonLs0Z +NbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsM +QtfhWsSWTVTNj8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvf +R4iyrT7gJ4eLSYwfqUdYe5byiB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJ +DPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjUo3KUQyxi4U5cMj29TH0ZR6LDSeeW +P4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqoENjwuSfr98t67wVy +lrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7EgkaibMOlq +bLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2w +AgDHbICivRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3q +r5nsLFR+jM4uElZI7xc7P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sji +Mho6/4UIyYOf8kpIEFR3N+2ivEC+5BB09+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU +98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- + +# Issuer: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Subject: CN=SSL.com TLS ECC Root CA 2022 O=SSL Corporation +# Label: "SSL.com TLS ECC Root CA 2022" +# Serial: 26605119622390491762507526719404364228 +# MD5 Fingerprint: 99:d7:5c:f1:51:36:cc:e9:ce:d9:19:2e:77:71:56:c5 +# SHA1 Fingerprint: 9f:5f:d9:1a:54:6d:f5:0c:71:f0:ee:7a:bd:17:49:98:84:73:e2:39 +# SHA256 Fingerprint: c3:2f:fd:9f:46:f9:36:d1:6c:36:73:99:09:59:43:4b:9a:d6:0a:af:bb:9e:7c:f3:36:54:f1:44:cc:1b:a1:43 +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQsw +CQYDVQQGEwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxT +U0wuY29tIFRMUyBFQ0MgUm9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2 +MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3Jh +dGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3QgQ0EgMjAyMjB2MBAG +ByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWyJGYm +acCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFN +SeR7T5v15wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSME +GDAWgBSJjy+j6CugFFR781a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NW +uCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp +15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w7deedWo1dlJF4AIxAMeN +b0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5Zn6g6g== +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA ECC TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA ECC TLS 2021" +# Serial: 81873346711060652204712539181482831616 +# MD5 Fingerprint: 16:9f:ad:f1:70:ad:79:d6:ed:29:b4:d1:c5:79:70:a8 +# SHA1 Fingerprint: 9e:bc:75:10:42:b3:02:f3:81:f4:f7:30:62:d4:8f:c3:a7:51:b2:dd +# SHA256 Fingerprint: b2:fa:e5:3e:14:cc:d7:ab:92:12:06:47:01:ae:27:9c:1d:89:88:fa:cb:77:5f:a8:a0:08:91:4e:66:39:88:a8 +-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4w +LAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0w +CwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0 +MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBF +Q0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMHYwEAYHKoZI +zj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6KDP/X +tXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4 +AjJn8ZQSb+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2 +KCXWfeBmmnoJsmo7jjPXNtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMD +aAAwZQIwW5kp85wxtolrbNa9d+F851F+uDrNozZffPc8dz7kUK2o59JZDCaOMDtu +CCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGYa3cpetskz2VAv9LcjBHo +9H1/IISpQuQo +-----END CERTIFICATE----- + +# Issuer: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Subject: CN=Atos TrustedRoot Root CA RSA TLS 2021 O=Atos +# Label: "Atos TrustedRoot Root CA RSA TLS 2021" +# Serial: 111436099570196163832749341232207667876 +# MD5 Fingerprint: d4:d3:46:b8:9a:c0:9c:76:5d:9e:3a:c3:b9:99:31:d2 +# SHA1 Fingerprint: 18:52:3b:0d:06:37:e4:d6:3a:df:23:e4:98:fb:5b:16:fb:86:74:48 +# SHA256 Fingerprint: 81:a9:08:8e:a5:9f:b3:64:c5:48:a6:f8:55:59:09:9b:6f:04:05:ef:bf:18:e5:32:4e:c9:f4:57:ba:00:11:2f +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBM +MS4wLAYDVQQDDCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIx +MQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00 +MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0b3MgVHJ1c3RlZFJvb3QgUm9vdCBD +QSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYTAkRFMIICIjAN +BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BBl01Z +4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYv +Ye+W/CBGvevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZ +kmGbzSoXfduP9LVq6hdKZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDs +GY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt0xU6kGpn8bRrZtkh68rZYnxGEFzedUln +nkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVKPNe0OwANwI8f4UDErmwh +3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMYsluMWuPD +0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzy +geBYBr3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8 +ANSbhqRAvNncTFd+rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezB +c6eUWsuSZIKmAMFwoW4sKeFYV+xafJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lI +pw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU +dEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB +DAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPs +o0UvFJ/1TCplQ3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJ +qM7F78PRreBrAwA0JrRUITWXAdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuyw +xfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9GslA9hGCZcbUztVdF5kJHdWoOsAgM +rr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2VktafcxBPTy+av5EzH4 +AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9qTFsR +0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuY +o7Ey7Nmj1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5 +dDTedk+SKlOxJTnbPP/lPqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcE +oji2jbDwN/zIIX8/syQbPYtuzE2wFg2WHYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE----- diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py new file mode 100644 index 0000000..5c67600 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/certifi/core.py @@ -0,0 +1,119 @@ +""" +certifi.py +~~~~~~~~~~ + +This module returns the installation location of cacert.pem or its contents. +""" +import sys + +DEBIAN_CA_CERTS_PATH = '/etc/ssl/certs/ca-certificates.crt' + +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("pip._vendor.certifi").joinpath("cacert.pem")) + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + + return _CACERT_PATH + + def contents() -> str: + return files("pip._vendor.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("pip._vendor.certifi", "cacert.pem") + _CACERT_PATH = str(_CACERT_CTX.__enter__()) + + return _CACERT_PATH + + def contents() -> str: + return read_text("pip._vendor.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("pip._vendor.certifi", "cacert.pem", encoding="ascii") + + +# Debian: Use system CA certs: +def where() -> str: + return DEBIAN_CA_CERTS_PATH + + +def contents() -> str: + with open(where(), "r", encoding="ascii") as data: + return data.read() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__init__.py new file mode 100644 index 0000000..fe58162 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__init__.py @@ -0,0 +1,115 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import List, Union + +from .charsetgroupprober import CharSetGroupProber +from .charsetprober import CharSetProber +from .enums import InputState +from .resultdict import ResultDict +from .universaldetector import UniversalDetector +from .version import VERSION, __version__ + +__all__ = ["UniversalDetector", "detect", "detect_all", "__version__", "VERSION"] + + +def detect( + byte_str: Union[bytes, bytearray], should_rename_legacy: bool = False +) -> ResultDict: + """ + Detect the encoding of the given byte string. + + :param byte_str: The byte sequence to examine. + :type byte_str: ``bytes`` or ``bytearray`` + :param should_rename_legacy: Should we rename legacy encodings + to their more modern equivalents? + :type should_rename_legacy: ``bool`` + """ + if not isinstance(byte_str, bytearray): + if not isinstance(byte_str, bytes): + raise TypeError( + f"Expected object of type bytes or bytearray, got: {type(byte_str)}" + ) + byte_str = bytearray(byte_str) + detector = UniversalDetector(should_rename_legacy=should_rename_legacy) + detector.feed(byte_str) + return detector.close() + + +def detect_all( + byte_str: Union[bytes, bytearray], + ignore_threshold: bool = False, + should_rename_legacy: bool = False, +) -> List[ResultDict]: + """ + Detect all the possible encodings of the given byte string. + + :param byte_str: The byte sequence to examine. + :type byte_str: ``bytes`` or ``bytearray`` + :param ignore_threshold: Include encodings that are below + ``UniversalDetector.MINIMUM_THRESHOLD`` + in results. + :type ignore_threshold: ``bool`` + :param should_rename_legacy: Should we rename legacy encodings + to their more modern equivalents? + :type should_rename_legacy: ``bool`` + """ + if not isinstance(byte_str, bytearray): + if not isinstance(byte_str, bytes): + raise TypeError( + f"Expected object of type bytes or bytearray, got: {type(byte_str)}" + ) + byte_str = bytearray(byte_str) + + detector = UniversalDetector(should_rename_legacy=should_rename_legacy) + detector.feed(byte_str) + detector.close() + + if detector.input_state == InputState.HIGH_BYTE: + results: List[ResultDict] = [] + probers: List[CharSetProber] = [] + for prober in detector.charset_probers: + if isinstance(prober, CharSetGroupProber): + probers.extend(p for p in prober.probers) + else: + probers.append(prober) + for prober in probers: + if ignore_threshold or prober.get_confidence() > detector.MINIMUM_THRESHOLD: + charset_name = prober.charset_name or "" + lower_charset_name = charset_name.lower() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith("iso-8859") and detector.has_win_bytes: + charset_name = detector.ISO_WIN_MAP.get( + lower_charset_name, charset_name + ) + # Rename legacy encodings with superset encodings if asked + if should_rename_legacy: + charset_name = detector.LEGACY_MAP.get( + charset_name.lower(), charset_name + ) + results.append( + { + "encoding": charset_name, + "confidence": prober.get_confidence(), + "language": prober.language, + } + ) + if len(results) > 0: + return sorted(results, key=lambda result: -result["confidence"]) + + return [detector.result] diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e5792ef4fb5c770884f4ff0451addf099f056c4 GIT binary patch literal 4590 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 Hi(Fp=4h&OD literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e87d05143a5ca16ff5a53e4989eb1297b7c72966 GIT binary patch literal 9660 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 BL{b0% literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3256676c0e81b1959573fb754cd64fef3d3475a4 GIT binary patch literal 5040 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94816cdb9eb6e6038972c107bf380a3374664dd4 GIT binary patch literal 3900 zcmb_f%WoUU8J}G~v?eL3wJTOJ>u4dvmRVaQl=P80MG8lDi$1Idk_!~dVX@jBN~Di%dDRSY14-U{GC{R=`8ldi}F@X1>R7zSl1QK0I7tc%J`F_kJ_N*uQ9zKG`m@{|bmL<}z3F*&=gwH{)wfeNoql z&ia{Vb}_3lo&A8hxd+V6Kh+b-#oW499r~0ObDwG$t6E&14P7r-UXZrrH*IId3;1=< zkvJ)Jm+Ht#G$dmWNCWFF#@V9gvPIq17BjBSvu?)CHnK3CUCg;9H;*~*4)Gy3!-ueM zU~MQ`cTa&n1-j7C-2&($FLhVN2id1xn~THi+3Ik7`Z!Yiw}4VG@mw<_x`EkDu3*a0 zT;sxPtedvEw2p&_-){53;btQg*lXEBdQRK7g^69|f|~jLP$*>_N)jo#!cCV;?np=p z?r?97M^{Wwns+?kHyfP0rd+XQmpe2$tiq-v!Kic{hGnnWo^LPt{E8U{JduL5ZA!1n zYvwJOJ1pR=*ugT7U@Po~IHgGJt;E^Pk+F)(Va5{C#)q+yAaO>LqtwhCD+`)s+NMT5{LJ))?pUD+m zoc!ruVHkVm0j^9R-k!O#qOQp<) z2%8jz2+1N~0d=Uh5I9s!P*b9@9yQ~<;Ib`(Ij7rzRYS{Qq~HEuF>J9-ZIfB*fCiUV z)SA_!Ry|pnPxn`ilH`(e})ARnE zc4m#Ovub8e5X4*6Qk=J%C{jNjv7ELL7A2>J3`^Hk8kWxkK|tf2Y8!De;ZjZ`tU=^_JJFTVQiTQK!qGEb7V2L&5L9d}^(A0_z2yw5*NF@z+)@J^T(K z{vn3@Y_~lAF#F5m?u!!-vmY6ibpE6=wNshyRHkWne7Ag-m_8|=+bK_V%2R(f{!yM$ zVn*rR!aIdumG+^GIlTnJf*hjKtp;L?NsP&}+Doi-hr(_3cGuYxZLXS8!4miuVS@7L zi6+XJA~InL$_+7wVPky2HC1?CCvM8S`|QAFJ~qb13)qY`XK-=+2nJ9bY!ysLA6FyM zO*P&_st>w}Og~cCYOfIbdfvJO+%Kv!>G7RlE2C`bYCKS4gRP#RBTLd7$ofrv1Ju3D z34A$aC9!y*4Q#Tz+Mtqm^Bcp6wr(A~Loa^)+b*!`&|F+}d^?IPNuM98V&jptAd}6w zlyEdqgrSIwDyO^rltIkYp-=CzxP3*Gx=k+{e0|&ov{m@u?z2K z|5AKXd^Xp)G}{@Q{b02-_68JICjRj1?_Pa;uT%Nvv(--JT2g^VX~(F1XjC5E?il9B zDAdt2JEIew(TQziLS^)!;-e~2#dj9ON$I@6LH$m0r% z`l?QXYjs=W#^sZ64`BWophREBpuoTT_B(Gs`quvqzv1J$bfxFrny|`^$RL-sH;uJXMsw3hI=D1+dtFi6V>#Jm0x7)VgeVGkemK!>j zC8#pQI1SWL;|xA@#RXbUsbwUExJ;DM5@CxAxvoGyVai_8iHC@thDrP)`git#eWrgU zXY5@W%8hMTX7(87XYM{t`=_;BZ7*NUnU7}o7^cUw`!wyH)pF+_FYGf+dwC4+pYPHv zKeDH5xv59zKgZ$Jeg+$#(MAG+45LR-#Nx7}0($VZh%axcE*a-|&~8Ra3sI220g?VS szA(O<`06$BZ5&VyMN~g~8BNph=T{s3H@o^zHu0bQE85iO41<#WFM<)!-2eap literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c25637ed31e7498af4ff8059c373e63d83f1a89 GIT binary patch literal 811 zcmZuvzfTlF6rR~%xBNIz0vC@&5{*i(fQiyr5EBJNV<1Ga@S4fo&TzN7JG;)zkz8xE z(#DFjqcvLicW7m3F4L&7vLit)sGQk7Of*cgZ@>4w_h#Sw_I;{WM}awCKRWT81HgCd zoSa|?j#drW0~Ao0fD|@i>NFh)7!=cna}4J;T?ibo4ygMaP>;FPe;>T_%KE0)t1#c% zM)<>04M$hEA8oDSl{;%I_wH`4Yk#|&C#(VW$aXJhbTy7tR2G-k9XdxH1NHy|O-Mo0 zc?g)xJPLQ5y5X5mon`y>3~~$fZ`3=5^9wF86~SLrv8Wd|Ms#f@qcLwkP(-l}5_MwE zY;TKtEJ}!!Sdmu3^mys)kVs`hk4c+73{6BGB5=a^(Dyh=nGO@e+g;LTn~jPNF}5Ve zdKBX{qus>fag3jKNm53HRZ_b#S9%g-!g*#s5ocVQL{}>D795!baQqB)vXmi7WJ);N z64_%GDT(f9txUy{L>n1zXREgsZb})qdCIs#i@S{Pq9ksiyr()DUs}9+4ar!sg`7lB zOp6lbagMM_qnSW<9Lf}m3SEjx(^3~|b;*08Vj3xo-79I8d;tbCv;C!gw}1ULJ_s)j zYW0J#{vUVld_US>>UZ8u9)$CQiL)<922(Sys$%Bky4z+ud}RhtHXDHsR4+F_M%cwD z)DiwkqV{YnV%qveb^Pu%%KY*`~v2`xo3Wj0{_BKFYs&o^$(YS Q0|O4Hz}&^p-fVHtAM>%?VE_OC literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..716711d071f7a2069b99e1f11474788df4798993 GIT binary patch literal 1418 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0a8373097fe8c6108e3d6e8a4b094694439e528 GIT binary patch literal 4588 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a93542744fef0044c08a7c62d98b653d26844080 GIT binary patch literal 4405 zcmbUkTWk~A_0HJij1wp01hdWq5<>{Zu!)-o(B+wsWOwjbuKN1l7J3;Km6@X0)zygwy%%o77i82h5S=o_d z(~hWv!p@X4%|$s1b17H4B3hB=qddi3DR;UuT1nxGR8`s&^-!2kRi|sBH4I|WX(YR^ zBe_!X+;doSMuq9Bpyvref{e+HsfmpGgt4%O!Lj~x7X}iVjuYcq9XNfel$zEO8Vs%L z&&UZiKB`N)G9<+&6RHwqO-})&WYcp2XU6S8AmgJ7`!lMpT-Hl;y)~3g>4{U*y3#)> z;Za2=ata2C8Y&@<4v9m<1B2&dmeE%4EFha#0r)BGn}VW@jH0aUo<$Rk%wG3I9R=6{ zuv6h=MsZEBvh%w0UO_loQKWJuR9Dyf|X_!wbU})bYVr$Vl_rK(r#qK$$#_L(EWm0K)TIAb4(6>3Qk}f#UR~w38cI1 zVfPB!a!9n7j`XS`w*UaQD4~O+SysI^yQ8x!q$PBveM*X5lH!UMno3NCL>MM#a3}`;CM$YK z$;OgXQ`GA^rlxTn%s*p!t97Cs_Ts+-9PJ(GzPX0h`0BX|gRg(YH?B1t_)U1F;pnXM zM$KB=iIukAS?42u_gbK5C2)vgzjBAF@UiDn{4FOh^=XV$Jlp&0L?QW ze2mchAa#Yk;&{34B(b2ZfuyVCxi{->)%Nn)ZU!rwK*#JPdYE>U^ z(Mdd`tP!0WbK?)M*y~+o!vVE#-W4v#$}F|%gB&@l-7k)n9mjwOyge`Sk|f%cwU=wj zarX*-#1LHtxK#Abbj>q2-uXXlV_?0G8Sp6jh3Om78(Q`nO<>n2PLyM0CrQsQ-xHZF z>@1vLc`Nad{pCD_=!uW-DFQILho)OmvuOoGMQ2v3 zP@+L(0~nKNVse8cBf}%6E0u}Ip&H<1Wjq_l0V3NXgy|&v0qLetz|bjpVu=W*Xr#mm&}QYuZop@}rP+v>b_A%J0V(7fS{1h4ygGMv zSqLukLFfU`3qb==;ovfV5c;qEfyMe2|Nc$ncmIHyJ3ZSstFJe;{OZJKC-O}_MpMrh zk(H(+v*%ZZrq6TvmQJIk^P$kW>h&#D-KxracNyMYtG>glzWThc&G5AWurPFMXw|p< zshg{u`uOb2H1l zmEN~hGUUe4r2b0)-Xy6fizPiMC^S28Tp3b(|iuXrJVpqkyY$D>7BOf zyu+Z=bfl%rPy&*Qi}*c#go*H-Fg=B`onjhUCAhxj_(XE1vDERmUNs5$+F9u66)1E0 zb$?Ud|F+?OJMTYg_>ZpmkI#Bm39Zxccjo=QhQD{ke`?mVCe+OzTimfCbS(27w6Y4a zxCWGlgSF({L25x!g!mw)GjcXXV2>#NI4h+r4z1T|S!?nT`rx&qPn!-X%Sz%CS(V9= zORt%3@{ur>rfJy1b%7 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ed82fe0cd8de37159cea84b872fb2fa63fef514 GIT binary patch literal 12104 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 Fz5&jUR3HEV literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb179562e0780d36661278f2eee031ad26f0fea1 GIT binary patch literal 27226 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 None: + super().__init__() + self.name = name + self.iso_code = iso_code + self.use_ascii = use_ascii + self.charsets = charsets + if self.use_ascii: + if alphabet: + alphabet += ascii_letters + else: + alphabet = ascii_letters + elif not alphabet: + raise ValueError("Must supply alphabet if use_ascii is False") + self.alphabet = "".join(sorted(set(alphabet))) if alphabet else None + self.wiki_start_pages = wiki_start_pages + + def __repr__(self) -> str: + param_str = ", ".join( + f"{k}={v!r}" for k, v in self.__dict__.items() if not k.startswith("_") + ) + return f"{self.__class__.__name__}({param_str})" + + +LANGUAGES = { + "Arabic": Language( + name="Arabic", + iso_code="ar", + use_ascii=False, + # We only support encodings that use isolated + # forms, because the current recommendation is + # that the rendering system handles presentation + # forms. This means we purposefully skip IBM864. + charsets=["ISO-8859-6", "WINDOWS-1256", "CP720", "CP864"], + alphabet="ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّ", + wiki_start_pages=["الصفحة_الرئيسية"], + ), + "Belarusian": Language( + name="Belarusian", + iso_code="be", + use_ascii=False, + charsets=["ISO-8859-5", "WINDOWS-1251", "IBM866", "MacCyrillic"], + alphabet="АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШЫЬЭЮЯабвгдеёжзійклмнопрстуўфхцчшыьэюяʼ", + wiki_start_pages=["Галоўная_старонка"], + ), + "Bulgarian": Language( + name="Bulgarian", + iso_code="bg", + use_ascii=False, + charsets=["ISO-8859-5", "WINDOWS-1251", "IBM855"], + alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя", + wiki_start_pages=["Начална_страница"], + ), + "Czech": Language( + name="Czech", + iso_code="cz", + use_ascii=True, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ", + wiki_start_pages=["Hlavní_strana"], + ), + "Danish": Language( + name="Danish", + iso_code="da", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="æøåÆØÅ", + wiki_start_pages=["Forside"], + ), + "German": Language( + name="German", + iso_code="de", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="äöüßẞÄÖÜ", + wiki_start_pages=["Wikipedia:Hauptseite"], + ), + "Greek": Language( + name="Greek", + iso_code="el", + use_ascii=False, + charsets=["ISO-8859-7", "WINDOWS-1253"], + alphabet="αβγδεζηθικλμνξοπρσςτυφχψωάέήίόύώΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏ", + wiki_start_pages=["Πύλη:Κύρια"], + ), + "English": Language( + name="English", + iso_code="en", + use_ascii=True, + charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"], + wiki_start_pages=["Main_Page"], + ), + "Esperanto": Language( + name="Esperanto", + iso_code="eo", + # Q, W, X, and Y not used at all + use_ascii=False, + charsets=["ISO-8859-3"], + alphabet="abcĉdefgĝhĥijĵklmnoprsŝtuŭvzABCĈDEFGĜHĤIJĴKLMNOPRSŜTUŬVZ", + wiki_start_pages=["Vikipedio:Ĉefpaĝo"], + ), + "Spanish": Language( + name="Spanish", + iso_code="es", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="ñáéíóúüÑÁÉÍÓÚÜ", + wiki_start_pages=["Wikipedia:Portada"], + ), + "Estonian": Language( + name="Estonian", + iso_code="et", + use_ascii=False, + charsets=["ISO-8859-4", "ISO-8859-13", "WINDOWS-1257"], + # C, F, Š, Q, W, X, Y, Z, Ž are only for + # loanwords + alphabet="ABDEGHIJKLMNOPRSTUVÕÄÖÜabdeghijklmnoprstuvõäöü", + wiki_start_pages=["Esileht"], + ), + "Finnish": Language( + name="Finnish", + iso_code="fi", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="ÅÄÖŠŽåäöšž", + wiki_start_pages=["Wikipedia:Etusivu"], + ), + "French": Language( + name="French", + iso_code="fr", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ", + wiki_start_pages=["Wikipédia:Accueil_principal", "Bœuf (animal)"], + ), + "Hebrew": Language( + name="Hebrew", + iso_code="he", + use_ascii=False, + charsets=["ISO-8859-8", "WINDOWS-1255"], + alphabet="אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ", + wiki_start_pages=["עמוד_ראשי"], + ), + "Croatian": Language( + name="Croatian", + iso_code="hr", + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="abcčćdđefghijklmnoprsštuvzžABCČĆDĐEFGHIJKLMNOPRSŠTUVZŽ", + wiki_start_pages=["Glavna_stranica"], + ), + "Hungarian": Language( + name="Hungarian", + iso_code="hu", + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="abcdefghijklmnoprstuvzáéíóöőúüűABCDEFGHIJKLMNOPRSTUVZÁÉÍÓÖŐÚÜŰ", + wiki_start_pages=["Kezdőlap"], + ), + "Italian": Language( + name="Italian", + iso_code="it", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="ÀÈÉÌÒÓÙàèéìòóù", + wiki_start_pages=["Pagina_principale"], + ), + "Lithuanian": Language( + name="Lithuanian", + iso_code="lt", + use_ascii=False, + charsets=["ISO-8859-13", "WINDOWS-1257", "ISO-8859-4"], + # Q, W, and X not used at all + alphabet="AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽaąbcčdeęėfghiįyjklmnoprsštuųūvzž", + wiki_start_pages=["Pagrindinis_puslapis"], + ), + "Latvian": Language( + name="Latvian", + iso_code="lv", + use_ascii=False, + charsets=["ISO-8859-13", "WINDOWS-1257", "ISO-8859-4"], + # Q, W, X, Y are only for loanwords + alphabet="AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽaābcčdeēfgģhiījkķlļmnņoprsštuūvzž", + wiki_start_pages=["Sākumlapa"], + ), + "Macedonian": Language( + name="Macedonian", + iso_code="mk", + use_ascii=False, + charsets=["ISO-8859-5", "WINDOWS-1251", "MacCyrillic", "IBM855"], + alphabet="АБВГДЃЕЖЗЅИЈКЛЉМНЊОПРСТЌУФХЦЧЏШабвгдѓежзѕијклљмнњопрстќуфхцчџш", + wiki_start_pages=["Главна_страница"], + ), + "Dutch": Language( + name="Dutch", + iso_code="nl", + use_ascii=True, + charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"], + wiki_start_pages=["Hoofdpagina"], + ), + "Polish": Language( + name="Polish", + iso_code="pl", + # Q and X are only used for foreign words. + use_ascii=False, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻaąbcćdeęfghijklłmnńoóprsśtuwyzźż", + wiki_start_pages=["Wikipedia:Strona_główna"], + ), + "Portuguese": Language( + name="Portuguese", + iso_code="pt", + use_ascii=True, + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú", + wiki_start_pages=["Wikipédia:Página_principal"], + ), + "Romanian": Language( + name="Romanian", + iso_code="ro", + use_ascii=True, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="ăâîșțĂÂÎȘȚ", + wiki_start_pages=["Pagina_principală"], + ), + "Russian": Language( + name="Russian", + iso_code="ru", + use_ascii=False, + charsets=[ + "ISO-8859-5", + "WINDOWS-1251", + "KOI8-R", + "MacCyrillic", + "IBM866", + "IBM855", + ], + alphabet="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ", + wiki_start_pages=["Заглавная_страница"], + ), + "Slovak": Language( + name="Slovak", + iso_code="sk", + use_ascii=True, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ", + wiki_start_pages=["Hlavná_stránka"], + ), + "Slovene": Language( + name="Slovene", + iso_code="sl", + # Q, W, X, Y are only used for foreign words. + use_ascii=False, + charsets=["ISO-8859-2", "WINDOWS-1250"], + alphabet="abcčdefghijklmnoprsštuvzžABCČDEFGHIJKLMNOPRSŠTUVZŽ", + wiki_start_pages=["Glavna_stran"], + ), + # Serbian can be written in both Latin and Cyrillic, but there's no + # simple way to get the Latin alphabet pages from Wikipedia through + # the API, so for now we just support Cyrillic. + "Serbian": Language( + name="Serbian", + iso_code="sr", + alphabet="АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШабвгдђежзијклљмнњопрстћуфхцчџш", + charsets=["ISO-8859-5", "WINDOWS-1251", "MacCyrillic", "IBM855"], + wiki_start_pages=["Главна_страна"], + ), + "Thai": Language( + name="Thai", + iso_code="th", + use_ascii=False, + charsets=["ISO-8859-11", "TIS-620", "CP874"], + alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛", + wiki_start_pages=["หน้าหลัก"], + ), + "Turkish": Language( + name="Turkish", + iso_code="tr", + # Q, W, and X are not used by Turkish + use_ascii=False, + charsets=["ISO-8859-3", "ISO-8859-9", "WINDOWS-1254"], + alphabet="abcçdefgğhıijklmnoöprsştuüvyzâîûABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZÂÎÛ", + wiki_start_pages=["Ana_Sayfa"], + ), + "Vietnamese": Language( + name="Vietnamese", + iso_code="vi", + use_ascii=False, + # Windows-1258 is the only common 8-bit + # Vietnamese encoding supported by Python. + # From Wikipedia: + # For systems that lack support for Unicode, + # dozens of 8-bit Vietnamese code pages are + # available.[1] The most common are VISCII + # (TCVN 5712:1993), VPS, and Windows-1258.[3] + # Where ASCII is required, such as when + # ensuring readability in plain text e-mail, + # Vietnamese letters are often encoded + # according to Vietnamese Quoted-Readable + # (VIQR) or VSCII Mnemonic (VSCII-MNEM),[4] + # though usage of either variable-width + # scheme has declined dramatically following + # the adoption of Unicode on the World Wide + # Web. + charsets=["WINDOWS-1258"], + alphabet="aăâbcdđeêghiklmnoôơpqrstuưvxyAĂÂBCDĐEÊGHIKLMNOÔƠPQRSTUƯVXY", + wiki_start_pages=["Chữ_Quốc_ngữ"], + ), +} diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/resultdict.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/resultdict.py new file mode 100644 index 0000000..7d36e64 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/resultdict.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + # TypedDict was introduced in Python 3.8. + # + # TODO: Remove the else block and TYPE_CHECKING check when dropping support + # for Python 3.7. + from typing import TypedDict + + class ResultDict(TypedDict): + encoding: Optional[str] + confidence: float + language: Optional[str] + +else: + ResultDict = dict diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcharsetprober.py new file mode 100644 index 0000000..0ffbcdd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -0,0 +1,162 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Dict, List, NamedTuple, Optional, Union + +from .charsetprober import CharSetProber +from .enums import CharacterCategory, ProbingState, SequenceLikelihood + + +class SingleByteCharSetModel(NamedTuple): + charset_name: str + language: str + char_to_order_map: Dict[int, int] + language_model: Dict[int, Dict[int, int]] + typical_positive_ratio: float + keep_ascii_letters: bool + alphabet: str + + +class SingleByteCharSetProber(CharSetProber): + SAMPLE_SIZE = 64 + SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 + POSITIVE_SHORTCUT_THRESHOLD = 0.95 + NEGATIVE_SHORTCUT_THRESHOLD = 0.05 + + def __init__( + self, + model: SingleByteCharSetModel, + is_reversed: bool = False, + name_prober: Optional[CharSetProber] = None, + ) -> None: + super().__init__() + self._model = model + # TRUE if we need to reverse every pair in the model lookup + self._reversed = is_reversed + # Optional auxiliary prober for name decision + self._name_prober = name_prober + self._last_order = 255 + self._seq_counters: List[int] = [] + self._total_seqs = 0 + self._total_char = 0 + self._control_char = 0 + self._freq_char = 0 + self.reset() + + def reset(self) -> None: + super().reset() + # char order of last character + self._last_order = 255 + self._seq_counters = [0] * SequenceLikelihood.get_num_categories() + self._total_seqs = 0 + self._total_char = 0 + self._control_char = 0 + # characters that fall in our sampling range + self._freq_char = 0 + + @property + def charset_name(self) -> Optional[str]: + if self._name_prober: + return self._name_prober.charset_name + return self._model.charset_name + + @property + def language(self) -> Optional[str]: + if self._name_prober: + return self._name_prober.language + return self._model.language + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + # TODO: Make filter_international_words keep things in self.alphabet + if not self._model.keep_ascii_letters: + byte_str = self.filter_international_words(byte_str) + else: + byte_str = self.remove_xml_tags(byte_str) + if not byte_str: + return self.state + char_to_order_map = self._model.char_to_order_map + language_model = self._model.language_model + for char in byte_str: + order = char_to_order_map.get(char, CharacterCategory.UNDEFINED) + # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but + # CharacterCategory.SYMBOL is actually 253, so we use CONTROL + # to make it closer to the original intent. The only difference + # is whether or not we count digits and control characters for + # _total_char purposes. + if order < CharacterCategory.CONTROL: + self._total_char += 1 + if order < self.SAMPLE_SIZE: + self._freq_char += 1 + if self._last_order < self.SAMPLE_SIZE: + self._total_seqs += 1 + if not self._reversed: + lm_cat = language_model[self._last_order][order] + else: + lm_cat = language_model[order][self._last_order] + self._seq_counters[lm_cat] += 1 + self._last_order = order + + charset_name = self._model.charset_name + if self.state == ProbingState.DETECTING: + if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: + confidence = self.get_confidence() + if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: + self.logger.debug( + "%s confidence = %s, we have a winner", charset_name, confidence + ) + self._state = ProbingState.FOUND_IT + elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: + self.logger.debug( + "%s confidence = %s, below negative shortcut threshold %s", + charset_name, + confidence, + self.NEGATIVE_SHORTCUT_THRESHOLD, + ) + self._state = ProbingState.NOT_ME + + return self.state + + def get_confidence(self) -> float: + r = 0.01 + if self._total_seqs > 0: + r = ( + ( + self._seq_counters[SequenceLikelihood.POSITIVE] + + 0.25 * self._seq_counters[SequenceLikelihood.LIKELY] + ) + / self._total_seqs + / self._model.typical_positive_ratio + ) + # The more control characters (proportionnaly to the size + # of the text), the less confident we become in the current + # charset. + r = r * (self._total_char - self._control_char) / self._total_char + r = r * self._freq_char / self._total_char + if r >= 1.0: + r = 0.99 + return r diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcsgroupprober.py new file mode 100644 index 0000000..890ae84 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -0,0 +1,88 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .charsetgroupprober import CharSetGroupProber +from .hebrewprober import HebrewProber +from .langbulgarianmodel import ISO_8859_5_BULGARIAN_MODEL, WINDOWS_1251_BULGARIAN_MODEL +from .langgreekmodel import ISO_8859_7_GREEK_MODEL, WINDOWS_1253_GREEK_MODEL +from .langhebrewmodel import WINDOWS_1255_HEBREW_MODEL + +# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL, +# WINDOWS_1250_HUNGARIAN_MODEL) +from .langrussianmodel import ( + IBM855_RUSSIAN_MODEL, + IBM866_RUSSIAN_MODEL, + ISO_8859_5_RUSSIAN_MODEL, + KOI8_R_RUSSIAN_MODEL, + MACCYRILLIC_RUSSIAN_MODEL, + WINDOWS_1251_RUSSIAN_MODEL, +) +from .langthaimodel import TIS_620_THAI_MODEL +from .langturkishmodel import ISO_8859_9_TURKISH_MODEL +from .sbcharsetprober import SingleByteCharSetProber + + +class SBCSGroupProber(CharSetGroupProber): + def __init__(self) -> None: + super().__init__() + hebrew_prober = HebrewProber() + logical_hebrew_prober = SingleByteCharSetProber( + WINDOWS_1255_HEBREW_MODEL, is_reversed=False, name_prober=hebrew_prober + ) + # TODO: See if using ISO-8859-8 Hebrew model works better here, since + # it's actually the visual one + visual_hebrew_prober = SingleByteCharSetProber( + WINDOWS_1255_HEBREW_MODEL, is_reversed=True, name_prober=hebrew_prober + ) + hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) + # TODO: ORDER MATTERS HERE. I changed the order vs what was in master + # and several tests failed that did not before. Some thought + # should be put into the ordering, and we should consider making + # order not matter here, because that is very counter-intuitive. + self.probers = [ + SingleByteCharSetProber(WINDOWS_1251_RUSSIAN_MODEL), + SingleByteCharSetProber(KOI8_R_RUSSIAN_MODEL), + SingleByteCharSetProber(ISO_8859_5_RUSSIAN_MODEL), + SingleByteCharSetProber(MACCYRILLIC_RUSSIAN_MODEL), + SingleByteCharSetProber(IBM866_RUSSIAN_MODEL), + SingleByteCharSetProber(IBM855_RUSSIAN_MODEL), + SingleByteCharSetProber(ISO_8859_7_GREEK_MODEL), + SingleByteCharSetProber(WINDOWS_1253_GREEK_MODEL), + SingleByteCharSetProber(ISO_8859_5_BULGARIAN_MODEL), + SingleByteCharSetProber(WINDOWS_1251_BULGARIAN_MODEL), + # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) + # after we retrain model. + # SingleByteCharSetProber(ISO_8859_2_HUNGARIAN_MODEL), + # SingleByteCharSetProber(WINDOWS_1250_HUNGARIAN_MODEL), + SingleByteCharSetProber(TIS_620_THAI_MODEL), + SingleByteCharSetProber(ISO_8859_9_TURKISH_MODEL), + hebrew_prober, + logical_hebrew_prober, + visual_hebrew_prober, + ] + self.reset() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sjisprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sjisprober.py new file mode 100644 index 0000000..91df077 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/sjisprober.py @@ -0,0 +1,105 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Union + +from .chardistribution import SJISDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .enums import MachineState, ProbingState +from .jpcntx import SJISContextAnalysis +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import SJIS_SM_MODEL + + +class SJISProber(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) + self.distribution_analyzer = SJISDistributionAnalysis() + self.context_analyzer = SJISContextAnalysis() + self.reset() + + def reset(self) -> None: + super().reset() + self.context_analyzer.reset() + + @property + def charset_name(self) -> str: + return self.context_analyzer.charset_name + + @property + def language(self) -> str: + return "Japanese" + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + assert self.coding_sm is not None + assert self.distribution_analyzer is not None + + for i, byte in enumerate(byte_str): + coding_state = self.coding_sm.next_state(byte) + if coding_state == MachineState.ERROR: + self.logger.debug( + "%s %s prober hit error at byte %s", + self.charset_name, + self.language, + i, + ) + self._state = ProbingState.NOT_ME + break + if coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + if coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte + self.context_analyzer.feed( + self._last_char[2 - char_len :], char_len + ) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed( + byte_str[i + 1 - char_len : i + 3 - char_len], char_len + ) + self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if self.context_analyzer.got_enough_data() and ( + self.get_confidence() > self.SHORTCUT_THRESHOLD + ): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self) -> float: + assert self.distribution_analyzer is not None + + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/universaldetector.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/universaldetector.py new file mode 100644 index 0000000..30c441d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/universaldetector.py @@ -0,0 +1,362 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### +""" +Module containing the UniversalDetector detector class, which is the primary +class a user of ``chardet`` should use. + +:author: Mark Pilgrim (initial port to Python) +:author: Shy Shalom (original C code) +:author: Dan Blanchard (major refactoring for 3.0) +:author: Ian Cordasco +""" + + +import codecs +import logging +import re +from typing import List, Optional, Union + +from .charsetgroupprober import CharSetGroupProber +from .charsetprober import CharSetProber +from .enums import InputState, LanguageFilter, ProbingState +from .escprober import EscCharSetProber +from .latin1prober import Latin1Prober +from .macromanprober import MacRomanProber +from .mbcsgroupprober import MBCSGroupProber +from .resultdict import ResultDict +from .sbcsgroupprober import SBCSGroupProber +from .utf1632prober import UTF1632Prober + + +class UniversalDetector: + """ + The ``UniversalDetector`` class underlies the ``chardet.detect`` function + and coordinates all of the different charset probers. + + To get a ``dict`` containing an encoding and its confidence, you can simply + run: + + .. code:: + + u = UniversalDetector() + u.feed(some_bytes) + u.close() + detected = u.result + + """ + + MINIMUM_THRESHOLD = 0.20 + HIGH_BYTE_DETECTOR = re.compile(b"[\x80-\xFF]") + ESC_DETECTOR = re.compile(b"(\033|~{)") + WIN_BYTE_DETECTOR = re.compile(b"[\x80-\x9F]") + ISO_WIN_MAP = { + "iso-8859-1": "Windows-1252", + "iso-8859-2": "Windows-1250", + "iso-8859-5": "Windows-1251", + "iso-8859-6": "Windows-1256", + "iso-8859-7": "Windows-1253", + "iso-8859-8": "Windows-1255", + "iso-8859-9": "Windows-1254", + "iso-8859-13": "Windows-1257", + } + # Based on https://encoding.spec.whatwg.org/#names-and-labels + # but altered to match Python names for encodings and remove mappings + # that break tests. + LEGACY_MAP = { + "ascii": "Windows-1252", + "iso-8859-1": "Windows-1252", + "tis-620": "ISO-8859-11", + "iso-8859-9": "Windows-1254", + "gb2312": "GB18030", + "euc-kr": "CP949", + "utf-16le": "UTF-16", + } + + def __init__( + self, + lang_filter: LanguageFilter = LanguageFilter.ALL, + should_rename_legacy: bool = False, + ) -> None: + self._esc_charset_prober: Optional[EscCharSetProber] = None + self._utf1632_prober: Optional[UTF1632Prober] = None + self._charset_probers: List[CharSetProber] = [] + self.result: ResultDict = { + "encoding": None, + "confidence": 0.0, + "language": None, + } + self.done = False + self._got_data = False + self._input_state = InputState.PURE_ASCII + self._last_char = b"" + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + self._has_win_bytes = False + self.should_rename_legacy = should_rename_legacy + self.reset() + + @property + def input_state(self) -> int: + return self._input_state + + @property + def has_win_bytes(self) -> bool: + return self._has_win_bytes + + @property + def charset_probers(self) -> List[CharSetProber]: + return self._charset_probers + + def reset(self) -> None: + """ + Reset the UniversalDetector and all of its probers back to their + initial states. This is called by ``__init__``, so you only need to + call this directly in between analyses of different documents. + """ + self.result = {"encoding": None, "confidence": 0.0, "language": None} + self.done = False + self._got_data = False + self._has_win_bytes = False + self._input_state = InputState.PURE_ASCII + self._last_char = b"" + if self._esc_charset_prober: + self._esc_charset_prober.reset() + if self._utf1632_prober: + self._utf1632_prober.reset() + for prober in self._charset_probers: + prober.reset() + + def feed(self, byte_str: Union[bytes, bytearray]) -> None: + """ + Takes a chunk of a document and feeds it through all of the relevant + charset probers. + + After calling ``feed``, you can check the value of the ``done`` + attribute to see if you need to continue feeding the + ``UniversalDetector`` more data, or if it has made a prediction + (in the ``result`` attribute). + + .. note:: + You should always call ``close`` when you're done feeding in your + document if ``done`` is not already ``True``. + """ + if self.done: + return + + if not byte_str: + return + + if not isinstance(byte_str, bytearray): + byte_str = bytearray(byte_str) + + # First check for known BOMs, since these are guaranteed to be correct + if not self._got_data: + # If the data starts with BOM, we know it is UTF + if byte_str.startswith(codecs.BOM_UTF8): + # EF BB BF UTF-8 with BOM + self.result = { + "encoding": "UTF-8-SIG", + "confidence": 1.0, + "language": "", + } + elif byte_str.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)): + # FF FE 00 00 UTF-32, little-endian BOM + # 00 00 FE FF UTF-32, big-endian BOM + self.result = {"encoding": "UTF-32", "confidence": 1.0, "language": ""} + elif byte_str.startswith(b"\xFE\xFF\x00\x00"): + # FE FF 00 00 UCS-4, unusual octet order BOM (3412) + self.result = { + # TODO: This encoding is not supported by Python. Should remove? + "encoding": "X-ISO-10646-UCS-4-3412", + "confidence": 1.0, + "language": "", + } + elif byte_str.startswith(b"\x00\x00\xFF\xFE"): + # 00 00 FF FE UCS-4, unusual octet order BOM (2143) + self.result = { + # TODO: This encoding is not supported by Python. Should remove? + "encoding": "X-ISO-10646-UCS-4-2143", + "confidence": 1.0, + "language": "", + } + elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): + # FF FE UTF-16, little endian BOM + # FE FF UTF-16, big endian BOM + self.result = {"encoding": "UTF-16", "confidence": 1.0, "language": ""} + + self._got_data = True + if self.result["encoding"] is not None: + self.done = True + return + + # If none of those matched and we've only see ASCII so far, check + # for high bytes and escape sequences + if self._input_state == InputState.PURE_ASCII: + if self.HIGH_BYTE_DETECTOR.search(byte_str): + self._input_state = InputState.HIGH_BYTE + elif ( + self._input_state == InputState.PURE_ASCII + and self.ESC_DETECTOR.search(self._last_char + byte_str) + ): + self._input_state = InputState.ESC_ASCII + + self._last_char = byte_str[-1:] + + # next we will look to see if it is appears to be either a UTF-16 or + # UTF-32 encoding + if not self._utf1632_prober: + self._utf1632_prober = UTF1632Prober() + + if self._utf1632_prober.state == ProbingState.DETECTING: + if self._utf1632_prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = { + "encoding": self._utf1632_prober.charset_name, + "confidence": self._utf1632_prober.get_confidence(), + "language": "", + } + self.done = True + return + + # If we've seen escape sequences, use the EscCharSetProber, which + # uses a simple state machine to check for known escape sequences in + # HZ and ISO-2022 encodings, since those are the only encodings that + # use such sequences. + if self._input_state == InputState.ESC_ASCII: + if not self._esc_charset_prober: + self._esc_charset_prober = EscCharSetProber(self.lang_filter) + if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = { + "encoding": self._esc_charset_prober.charset_name, + "confidence": self._esc_charset_prober.get_confidence(), + "language": self._esc_charset_prober.language, + } + self.done = True + # If we've seen high bytes (i.e., those with values greater than 127), + # we need to do more complicated checks using all our multi-byte and + # single-byte probers that are left. The single-byte probers + # use character bigram distributions to determine the encoding, whereas + # the multi-byte probers use a combination of character unigram and + # bigram distributions. + elif self._input_state == InputState.HIGH_BYTE: + if not self._charset_probers: + self._charset_probers = [MBCSGroupProber(self.lang_filter)] + # If we're checking non-CJK encodings, use single-byte prober + if self.lang_filter & LanguageFilter.NON_CJK: + self._charset_probers.append(SBCSGroupProber()) + self._charset_probers.append(Latin1Prober()) + self._charset_probers.append(MacRomanProber()) + for prober in self._charset_probers: + if prober.feed(byte_str) == ProbingState.FOUND_IT: + self.result = { + "encoding": prober.charset_name, + "confidence": prober.get_confidence(), + "language": prober.language, + } + self.done = True + break + if self.WIN_BYTE_DETECTOR.search(byte_str): + self._has_win_bytes = True + + def close(self) -> ResultDict: + """ + Stop analyzing the current document and come up with a final + prediction. + + :returns: The ``result`` attribute, a ``dict`` with the keys + `encoding`, `confidence`, and `language`. + """ + # Don't bother with checks if we're already done + if self.done: + return self.result + self.done = True + + if not self._got_data: + self.logger.debug("no data received!") + + # Default to ASCII if it is all we've seen so far + elif self._input_state == InputState.PURE_ASCII: + self.result = {"encoding": "ascii", "confidence": 1.0, "language": ""} + + # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD + elif self._input_state == InputState.HIGH_BYTE: + prober_confidence = None + max_prober_confidence = 0.0 + max_prober = None + for prober in self._charset_probers: + if not prober: + continue + prober_confidence = prober.get_confidence() + if prober_confidence > max_prober_confidence: + max_prober_confidence = prober_confidence + max_prober = prober + if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): + charset_name = max_prober.charset_name + assert charset_name is not None + lower_charset_name = charset_name.lower() + confidence = max_prober.get_confidence() + # Use Windows encoding name instead of ISO-8859 if we saw any + # extra Windows-specific bytes + if lower_charset_name.startswith("iso-8859"): + if self._has_win_bytes: + charset_name = self.ISO_WIN_MAP.get( + lower_charset_name, charset_name + ) + # Rename legacy encodings with superset encodings if asked + if self.should_rename_legacy: + charset_name = self.LEGACY_MAP.get( + (charset_name or "").lower(), charset_name + ) + self.result = { + "encoding": charset_name, + "confidence": confidence, + "language": max_prober.language, + } + + # Log all prober confidences if none met MINIMUM_THRESHOLD + if self.logger.getEffectiveLevel() <= logging.DEBUG: + if self.result["encoding"] is None: + self.logger.debug("no probers hit minimum threshold") + for group_prober in self._charset_probers: + if not group_prober: + continue + if isinstance(group_prober, CharSetGroupProber): + for prober in group_prober.probers: + self.logger.debug( + "%s %s confidence = %s", + prober.charset_name, + prober.language, + prober.get_confidence(), + ) + else: + self.logger.debug( + "%s %s confidence = %s", + group_prober.charset_name, + group_prober.language, + group_prober.get_confidence(), + ) + return self.result diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf1632prober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf1632prober.py new file mode 100644 index 0000000..6bdec63 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf1632prober.py @@ -0,0 +1,225 @@ +######################## BEGIN LICENSE BLOCK ######################## +# +# Contributor(s): +# Jason Zavaglia +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### +from typing import List, Union + +from .charsetprober import CharSetProber +from .enums import ProbingState + + +class UTF1632Prober(CharSetProber): + """ + This class simply looks for occurrences of zero bytes, and infers + whether the file is UTF16 or UTF32 (low-endian or big-endian) + For instance, files looking like ( \0 \0 \0 [nonzero] )+ + have a good probability to be UTF32BE. Files looking like ( \0 [nonzero] )+ + may be guessed to be UTF16BE, and inversely for little-endian varieties. + """ + + # how many logical characters to scan before feeling confident of prediction + MIN_CHARS_FOR_DETECTION = 20 + # a fixed constant ratio of expected zeros or non-zeros in modulo-position. + EXPECTED_RATIO = 0.94 + + def __init__(self) -> None: + super().__init__() + self.position = 0 + self.zeros_at_mod = [0] * 4 + self.nonzeros_at_mod = [0] * 4 + self._state = ProbingState.DETECTING + self.quad = [0, 0, 0, 0] + self.invalid_utf16be = False + self.invalid_utf16le = False + self.invalid_utf32be = False + self.invalid_utf32le = False + self.first_half_surrogate_pair_detected_16be = False + self.first_half_surrogate_pair_detected_16le = False + self.reset() + + def reset(self) -> None: + super().reset() + self.position = 0 + self.zeros_at_mod = [0] * 4 + self.nonzeros_at_mod = [0] * 4 + self._state = ProbingState.DETECTING + self.invalid_utf16be = False + self.invalid_utf16le = False + self.invalid_utf32be = False + self.invalid_utf32le = False + self.first_half_surrogate_pair_detected_16be = False + self.first_half_surrogate_pair_detected_16le = False + self.quad = [0, 0, 0, 0] + + @property + def charset_name(self) -> str: + if self.is_likely_utf32be(): + return "utf-32be" + if self.is_likely_utf32le(): + return "utf-32le" + if self.is_likely_utf16be(): + return "utf-16be" + if self.is_likely_utf16le(): + return "utf-16le" + # default to something valid + return "utf-16" + + @property + def language(self) -> str: + return "" + + def approx_32bit_chars(self) -> float: + return max(1.0, self.position / 4.0) + + def approx_16bit_chars(self) -> float: + return max(1.0, self.position / 2.0) + + def is_likely_utf32be(self) -> bool: + approx_chars = self.approx_32bit_chars() + return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( + self.zeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO + and self.zeros_at_mod[1] / approx_chars > self.EXPECTED_RATIO + and self.zeros_at_mod[2] / approx_chars > self.EXPECTED_RATIO + and self.nonzeros_at_mod[3] / approx_chars > self.EXPECTED_RATIO + and not self.invalid_utf32be + ) + + def is_likely_utf32le(self) -> bool: + approx_chars = self.approx_32bit_chars() + return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( + self.nonzeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO + and self.zeros_at_mod[1] / approx_chars > self.EXPECTED_RATIO + and self.zeros_at_mod[2] / approx_chars > self.EXPECTED_RATIO + and self.zeros_at_mod[3] / approx_chars > self.EXPECTED_RATIO + and not self.invalid_utf32le + ) + + def is_likely_utf16be(self) -> bool: + approx_chars = self.approx_16bit_chars() + return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( + (self.nonzeros_at_mod[1] + self.nonzeros_at_mod[3]) / approx_chars + > self.EXPECTED_RATIO + and (self.zeros_at_mod[0] + self.zeros_at_mod[2]) / approx_chars + > self.EXPECTED_RATIO + and not self.invalid_utf16be + ) + + def is_likely_utf16le(self) -> bool: + approx_chars = self.approx_16bit_chars() + return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( + (self.nonzeros_at_mod[0] + self.nonzeros_at_mod[2]) / approx_chars + > self.EXPECTED_RATIO + and (self.zeros_at_mod[1] + self.zeros_at_mod[3]) / approx_chars + > self.EXPECTED_RATIO + and not self.invalid_utf16le + ) + + def validate_utf32_characters(self, quad: List[int]) -> None: + """ + Validate if the quad of bytes is valid UTF-32. + + UTF-32 is valid in the range 0x00000000 - 0x0010FFFF + excluding 0x0000D800 - 0x0000DFFF + + https://en.wikipedia.org/wiki/UTF-32 + """ + if ( + quad[0] != 0 + or quad[1] > 0x10 + or (quad[0] == 0 and quad[1] == 0 and 0xD8 <= quad[2] <= 0xDF) + ): + self.invalid_utf32be = True + if ( + quad[3] != 0 + or quad[2] > 0x10 + or (quad[3] == 0 and quad[2] == 0 and 0xD8 <= quad[1] <= 0xDF) + ): + self.invalid_utf32le = True + + def validate_utf16_characters(self, pair: List[int]) -> None: + """ + Validate if the pair of bytes is valid UTF-16. + + UTF-16 is valid in the range 0x0000 - 0xFFFF excluding 0xD800 - 0xFFFF + with an exception for surrogate pairs, which must be in the range + 0xD800-0xDBFF followed by 0xDC00-0xDFFF + + https://en.wikipedia.org/wiki/UTF-16 + """ + if not self.first_half_surrogate_pair_detected_16be: + if 0xD8 <= pair[0] <= 0xDB: + self.first_half_surrogate_pair_detected_16be = True + elif 0xDC <= pair[0] <= 0xDF: + self.invalid_utf16be = True + else: + if 0xDC <= pair[0] <= 0xDF: + self.first_half_surrogate_pair_detected_16be = False + else: + self.invalid_utf16be = True + + if not self.first_half_surrogate_pair_detected_16le: + if 0xD8 <= pair[1] <= 0xDB: + self.first_half_surrogate_pair_detected_16le = True + elif 0xDC <= pair[1] <= 0xDF: + self.invalid_utf16le = True + else: + if 0xDC <= pair[1] <= 0xDF: + self.first_half_surrogate_pair_detected_16le = False + else: + self.invalid_utf16le = True + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + for c in byte_str: + mod4 = self.position % 4 + self.quad[mod4] = c + if mod4 == 3: + self.validate_utf32_characters(self.quad) + self.validate_utf16_characters(self.quad[0:2]) + self.validate_utf16_characters(self.quad[2:4]) + if c == 0: + self.zeros_at_mod[mod4] += 1 + else: + self.nonzeros_at_mod[mod4] += 1 + self.position += 1 + return self.state + + @property + def state(self) -> ProbingState: + if self._state in {ProbingState.NOT_ME, ProbingState.FOUND_IT}: + # terminal, decided states + return self._state + if self.get_confidence() > 0.80: + self._state = ProbingState.FOUND_IT + elif self.position > 4 * 1024: + # if we get to 4kb into the file, and we can't conclude it's UTF, + # let's give up + self._state = ProbingState.NOT_ME + return self._state + + def get_confidence(self) -> float: + return ( + 0.85 + if ( + self.is_likely_utf16le() + or self.is_likely_utf16be() + or self.is_likely_utf32le() + or self.is_likely_utf32be() + ) + else 0.00 + ) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf8prober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf8prober.py new file mode 100644 index 0000000..d96354d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/utf8prober.py @@ -0,0 +1,82 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Union + +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .enums import MachineState, ProbingState +from .mbcssm import UTF8_SM_MODEL + + +class UTF8Prober(CharSetProber): + ONE_CHAR_PROB = 0.5 + + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) + self._num_mb_chars = 0 + self.reset() + + def reset(self) -> None: + super().reset() + self.coding_sm.reset() + self._num_mb_chars = 0 + + @property + def charset_name(self) -> str: + return "utf-8" + + @property + def language(self) -> str: + return "" + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + for c in byte_str: + coding_state = self.coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + self._state = ProbingState.NOT_ME + break + if coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + if coding_state == MachineState.START: + if self.coding_sm.get_current_charlen() >= 2: + self._num_mb_chars += 1 + + if self.state == ProbingState.DETECTING: + if self.get_confidence() > self.SHORTCUT_THRESHOLD: + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self) -> float: + unlike = 0.99 + if self._num_mb_chars < 6: + unlike *= self.ONE_CHAR_PROB**self._num_mb_chars + return 1.0 - unlike + return unlike diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/version.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/version.py new file mode 100644 index 0000000..c5e9d85 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/version.py @@ -0,0 +1,9 @@ +""" +This module exists only to simplify retrieving the version number of chardet +from within setuptools and from chardet subpackages. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + +__version__ = "5.1.0" +VERSION = __version__.split(".") diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__init__.py new file mode 100644 index 0000000..383101c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__init__.py @@ -0,0 +1,7 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console +from .ansi import Fore, Back, Style, Cursor +from .ansitowin32 import AnsiToWin32 + +__version__ = '0.4.6' + diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d9f99329367190cb39e8ca3c580b856da34ee968 GIT binary patch literal 514 zcmXv~yKWRQ6t(BIkJ(KTfdoCxD9RXi5fzG*5Fr{O1Vl(~tjUboIQTKLy_+l@U%@xz zSD-{RmQW&5AUdRUskkQO7Uw>W>~rt=I-QOY#O0Snj4?t#V{;qH|KjQe6rWLtIxLaG z6sv?LDy6B)Xr^+SxH+k9`X>6k!=J}Zq_fD41v-jeRoeJHVQ^#7PosqoCLiXHP9xjArZ{UBL)lFUhe{oVdoRMw3_8obc8=^4c63FIq=l z8oe<4d)vFtiG@~Nds45sUJ)sdNWb>SM$hXA6VmHMF!SHE15a4*^aEN+fI6;VP30Q={2ivQNy*I zbgo>PPgmCsYE)f|rmGDToNgoCq;}o4>ucbtH7IvM`-WXNYECv%v8?Izy6ZNlRn@9z zKeMmeP0O^6Y=f?;XsI}^ZaGz>Y3Vp%c#*o!Nr1b=1@s@c zM<^j%t-fK^T{U~%s$W-Y_GPu%a@QO6_p)c+RUO;4 zPBo3{Rb$O^)TZ53b?9a`sLHu9HVoCMJ9f6&VsV^acN;n~>QEG24BpN`fHrv&9s4r1 zvvOzh=GooKUNrqkPIsVzjPN0XgY)rIs0{PrfAML=Gp^TC6M7rT(N4+GN|rUsb$qEMwEn?MiE|d5a)Ri^Z*P!%uK|Opk_m2OhOy3 z*wFl74W7PwI~e0#ViDLZ2y7MtY!OmR%t$Pp13l#N8%Nj+L4UrqxA_m<{y;+lFOW+= zC**TsOSm$S|6pQ}em@bYaQmR!-?`xTTf)PhBt942VE-5BcX8qQvnBR_-QRx%a{GOm zwER@kGYXSTo3e=G8q{?@v)y$TT(exmbtwyiaAUfZFY4Dndt9HiR71+-HAKDxw*Al-Af#M6guW-+^Y+2#?QDc zOga*mTv)tN-b@TdhK{$O8$E--6aUqPk$*`T!P&*-3%TV&PpEupvOQnse-%vOCBeuu zx%~MNPSEkrXc)sg!$3D5L7(B{V(9oB?0Ta&gI5SdDSbt!Y%NOMqd z$b{%mu+H^>XCXa}fT>4+fiR8mAp&0|REwY1oGpYC@H&{}nAU>wMn@ijE|_Qd{1khS zqP{ z2){?r5&nQ+0F)mb*hd~yW=bF=CM_2WOqp9Q7RxMfu~;fCUSRS}X|>3L3%R*sc_qi> z{KZ_EDHqPouN0ZGT&xsVSiCepcW#9bp%;J4qTGa`(D2*%kZ6waHh*ZeW4)iQ7ST#@ zt2fn)W}Ff&e=I&VnEMwt51&R6aC>kr0&J7VhmN$12>C&67Hb8Bxj~J0DLxI5!;|d= z^qxoHwKCR92#fvNjRjv8I9C|S#wHx!kK(J#uBhW1kue3&H1SGHwr$oS+7#@GoY8JU^ZG#r!<< zEC0sxYkCvLL5(_Cc`6Bl@JyBj`Dug%$J)~4KpY{`mVO8b!ZCys9jtNahC;&hPM}Mm z=pKp)XLb&C2^8I_lrZ^4t4pBho=gc-etoKYQWlas;VyyVS&T%Ix2D?i_u&b@NZvZy PR=y9XZoPIRz`6P-K^8#E literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0c937c667c5979d36046c898acc4821ce818d2f GIT binary patch literal 16443 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 I|74*514+YOt^fc4 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f18c334806e159dca880eefd620e4d2eb4c1bba GIT binary patch literal 3572 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff99cd66dc4878c2ac94a2fb50edf5af94c946d5 GIT binary patch literal 9110 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6bbfa74846de4905e8be63e54a1b32f1252fb07 GIT binary patch literal 5489 zcmeHL&rcgi6rT0&+6x8}LTE6iEoqXrPJj*IhL}WYO>hVeI29pEV-A+XZi$KQrL#*? z)2QN*N~x5pM}!n9RH{@W`Umt6=&3!q1tndzYNQ;h+z{lHQ{S8MZ#JoXY{eYjoA+kE zH#0lC`@NZ88XM~glqWyCk_WtmJVm2WNsVyImyA?=E}(qc>$h$d=M zN=nNyIqi}mH1{2%d34vB$o;V< zeUGNxc$W~WxQH&U<+}?cD`bU0-7~bK0hg^z>ZWxzVd}P&P9!tWT;LCgwltKOzF|w} z4657mB`cfK1A^@hXUycJaV44Q@0-pCWJ<^z+ee|Y1Ph1>8i~0y;SPz3nhT_)i6CW7 z0_oOdke;kN;IS1J5~eU+h=HN-LE=KTe+02aZj;&aR`6l5@=Jx9#65(}x(l_EUdv-G zrQln`MM4p3iOja0;|lUsk+bBX5DmEPri5wg)QTEbX&?$W=*pjKIJ~469oiA>y*NwEUnh7(V$fzGtV^*KGOm)JzW?0E-Qyn)lGsf^x zci1$OGnurWvDBWMdgi8@N?udvvetDY)8EtErhnajf>tWo_Bit;=%@O@%$`VRjSD%sc^Nk!-q2UE?FgpHuwzhret2 zzWv+w-#WT}xUsGbK2{nW8a9-{Z9tNW-NxNuyZr%FmWTzC7c0L{gv5(AtGL5ut;PM% z650sZfNXpD!;Kp>IYak?k2Zr~Zg|Yxy${_jAo$a!t=L1c^aLF?Ze*)b-RxApq#1=ISwU@ra#bwPFmjJkY(S(0}QA_yZV=15s{B>%; z7e9_NyjTO?;q%a>8t@IJaSJs~t^tp4WmVDKHQ;eqtBU5S0gvxeRne3h@D6``t*!?A zYgL1TsnyqjcVHIHTLZq`Vf=;~@Es2L#v1T%IN+OVz;`;}_tb!Y%K^W)2K-?Md~*q( zC4qg>1=&B}}pFIRnB^>Y>E>OHPba5cbHh^Y{Vhd4aM;UNwWad?QsLmVC&NC#SM zFK01EVblXtwdJ9)@YxHtI1w4P<&lX~h5RjE6@e(aEqaoxK{4 z+VYk2qmvO^o`_tEOxlfOqa)`hS%`S#W82G22!#f3V}8B*he- zVje@^MA3=jAPUSa=phuCCD0=%-bV2b3OM5v8bE=kZ+Z+x7m98aJt(TrIE?r3g#S|B zt87#u$p4J@rm9(s@!qLw)?!Swqc7vV;wiU+%Pv&&DdWA**OgNhY{+5J&FbtTZzXriL@S% z+x78y+R)}xSZ|2O@xq6=*>>i5Ul2FWZDZ3rPV;!H@0M-Uk;9Uos@C)TMU+x+DKKSs;${Yc) zDGGw{l+^!44*g9I=g8qd$&n{b&EL#^HM{72T&O&EpBDP=e)gPz+WbNwQp4SaUq#>V PlJe!bO_50LY@mMtdc=)G literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f646a9abba85bffa51da3b39371d97c5b7f6090 GIT binary patch literal 18125 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 T%TBDN5w0)uZ(}F6(g6P#8F;-| literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73d473b013a4d30838706d77cd9dd3db210be2d9 GIT binary patch literal 11770 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce4b8c28eecae18690b9a3d2fbde9428b6dab249 GIT binary patch literal 4926 zcmd^D-EZ606~7coiIS*Tv4hNxDc5N}T*bAi*iEvf!3rt1*KBA~GzQve4+3LyjvP_a zNIIE}G}uE1q)35o7>X2#A6zs*XE<2*2MhyZ?5TU1CJoAn03FtcA#aXdK!Bfi&ZS6w zM0T=uPum52?z!h)9bVqwIlp_%KSd%T0`2c#`m#R^5b`w++{QPQt(&0SB`Q&w97$vE z%Q3ny?bF#b%Mg`Sxg4kSX!e$ zsKPCx1~uOr>pUgBN9$2TS6?B-5PU>q*BTFJNQo&i@$eHIpO5=&VOBG(vx=$NoUUZ^ zPkdkpbL66BXo~)>p%janVTY4>Gdo*&H=CcFc*5Wk-VPK?b5|5Yw?l?j%qeqP=5opI zbM=CeozLc#92$d;v)S28w&rkS13>ssED$FFJmYjQ4oSuU2WD~0^z`0)wZ%v#!5QJK4{%xk7x z%ogPgEK>`HJXgpS3`JLD3l7$loqc3*FkUR#Le|VE8Q7W82m1-apV<%M=j3s?|C9YI z`zqn1weVOuJXQ(If9B*Zm?Lv2+~yujk{)sSHE{tNK84DhrZVIgoyp2N_WP zAO#vN!IBUU+M#pwco6$G5b*3M2!8;j6$JN?-)C-g1;KKoo7`a58hdnmhFFjbPILX7 zAPo6WJVCa!gabq~IYf>Vlez9=$R+TJmY=bg$aTi(fhWc}Lxj!_DW<6z)~vCp(StYe z&n~CRk1YKiCoJhf^W`%0Qj8z@dYQm z{JDSe^Yo|bij=5|$GaNqJS?vh5KmETo0#}_l6#X!*oQ~>}vI2Id~gxxXXCLY|?m8!dL?&^dmV+&=7e$ZdDh<0 zoftuaUAP9gW>szJ=q_-zta}Nao(JI(qL*c(%b}IYdp*zL!WKfL>IM?z_SQjg`LUQB zU_Tv9M)=`w!n(#?(pX@Xl<|#b;9aYTpqIVU65^G zdKg{51Hxlf58MQRd#ch;ud?0gw1rctiU0$>j)L1Zdc}01X?YWBh;C_#sn$$PC=465 z2DN)NNV^^Cj9pW+x!UzcDR_WDU^2{(LYqZxBF80npWOlypQXeEECO9X0O{Eh4$f8N44di@xLcPVhe zQ;+u!elk_rd+;e?Bd4l|PX5lS96Cb>kD~i((cyA*_L>rb*&3nuy?fo$~(X4 z^RepeXy5+%ooW7TTDPZZxTQiv-f{|ny{O?yIax~+7U|qLisA^0&a91fW^L_ zt;OCb$KI&LX3DXdO6<+5c&_P%{UWp)sz{SH>2z5-U6Yb!DOr)uR>i3oSyK;k!D*%r znLf=trLs{po;@)YWd9iCr+NeD;~`tfWb%rxWioaslhF(6Vh(jNllkeQl5=JZ2@msS z6!6J~7{^eIp^#A=2eC>F3>8`#8~ag1fz)Xt9Jk0O$MK_^BF85-MV6n~46!f-k=P9J z{Hd)yJU`7iF7SuAEBs)S$;6`uf@%wk`K*QT85p_-hqkZ+-)*eIb^LBaKa3a{9X7vc zWpidHwNo0WK|=p0qkEh{S0_+-o8K1BIEV0k1L+ShVDk)!4VGb;uSw`Da_FDrwXewR sU&-vIpJ5W;_~Xnp^UMBk2=*KAF@zIuFa3ob_!}quVrGLS+yUzSUllr)FaQ7m literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/utils.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a47fc1f3c884df0144d8943d67ea720f60e0e5b1 GIT binary patch literal 2510 zcmds3&2Jk;6rb4-d+j)B8`lJtk~&IDaiCtM6$GJzUf=Zp}Cq)A7{&HPgAK+SX;YTJx^B&WZf!ag|%1d913J zKLu~NTD7XG24;rK)Us>4Ot0vwXL6W%*0XG$uh#sS#dXiC-PqgSem+DJurZFuxj@$T zq?@3S;H)V_e_!dxbwFA;Ujt1-iV0+=?SrC7azZS^jp5D7-cg7Y@ zul4oU$Lka8QX058+>Fy4G3%xJqV8?;0IOUFVQ`nt=ZD^agf2U<$=&Yr+ z$Ao0DmtbMI$s(_DKgK=7o%Mp+WNcYP4KKLggXC#Zce?>L9PfFy#b}b5Ra-Bc+T|L< z^B#bYkAVnT?awTY+#6XqyU|kC_6^ie)hDA0S>AUr1faS-x!J@ka;wl^*#G~cn;itd z@G%J)4IdWJfly6jhmf^xyrx^c;xSl`ZX?03gV_+8ZX-BhtYRJnz1r_ec$#7$uy}36xC3ij;gcd|jL-bM!gE~q#CLE8kU-;(>u8Ws!ps)F zF7axud_`v!mWQ!#z{gDx!9{p9{91kdi^q_r45x{5I^H@pVP2Ne~PY*v(4nLCtX&}56bO!3*{5Y(1D_3}% zuA~Yh;tz6RQ2tQ?ZB;q|;Od}UIGR`;jw3Q67f!@i)i~g}l%I_L8$gO>7=gZExeTAD zAYua=!mJi#0DL$*JOqyJ$P`=3$uM8O&ql!*Lx9&nG(<}2@8rm@WMG{n*Gb}OOrZN0 zm8S%%r;0!`i|?SGX(&Y4`$Ujvx+#LzK&_DqusD4+!a!^EN7zss8;UTQ9tp4~0%$>k F`UlL5<8lB1 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46ba55117dc25ee5f1cd5aaa7cec9fedce2337cf GIT binary patch literal 6634 zcmeHMT}&I<6~6Ohdu)##H{m}if!#!jR|^iXO;Tmm2ojc3ZD0$I()eX$JOkLVJ?xz! ztPxTBkXE2Z3ei5KQCDjEw2QJ*Q>jubwJ&{bUvOb%I!d*vHZSxEN>;7%(w;N+j0ZEo zDod-C+WT7bD>0tAuJgnviCNTeG2=C zD|1hTl|!;{<1`^uV2Q##s%*-T0#jgKC5mu@&>-+b{{AZe>*P-N40(^cPG*>>e;rp1 zML9!=E1G^OsVN4ZOQ!QiK)aEix|+`_nigdZ{)(EqVR)vJdTMf=g(bM+S~`D8&8pOF zjp~K0V)&phuFzbnB8*wt$MHJ`k3%5|MkWasg_fB|(1%54Zxib$BzPF}DBcVlY;DPW zt+of&ayA$K5lRO|_BiVXG}h}{CPgYxx?x{0lTTdd5? zv69_C$IdYsTvbz~3hoKf{nn7B#9Q1P$po$5YCXqg#OjyTl35ER4B2&^3-kbHm0z~U zx`?$)wU%vAf^CwhXJ}o8!F3-JFA@Pr)K9T(1ZR46ou*K0 zbV`d|QS%e(<-U$yO-oPYb4p&1b>39+H)GlKSZu1GPpbKz&aUp5me!SyDUc#Lp=hzG z^i)iOX|hUV7Jg#zV}Wx8MG}5>P8AFR8-w|@K3fN(qF;&Qh}C-t1l7iX{D3?=()j+_ zd138%$GosA99w(y^1Qz!yj~PuUlqRjaf(tauJIOK70FpkkZrPcS8ar^H9hrc4(px+j_Q9cEpg@$vazTwJ=iDqMhBrTUY_`jgw{t%;GlLkmMI z;;|=U-QD4Z;SVNDt)0cz&c}y~tvyAt=edXVf8)788js&s@2T6Q*mv!(CV#E|QZ0)8 zp97NthPJ~mbdQI>0k`4mAPyuR?u@f@oMa_pkZi#hx+=4nUl@(5Ul@_h0ul6x)vnf- zvoYgfeYM|e?Gksl$4~clE@t73pWq_g*SQ&tr)t)_t~0ZEOYU-=L2IZfvbV|$Ov*R3 z9)%g}|AMlP{-5HBQMAi=R5T+w#dD+RLnumIiO?FDSgIV+Yi!W)Lrg&f%x25x=uJUW zk!cI8wu7?6#zt^@Z#KKr#;lPTuKWQ1WyI= zZg?RK!_a!UNV+PVb{KT2Ia+LvmYUBLo6kHx@riJ*Y%R`kin)jxPX7iCS8HK(+aX)1 zbcU3gfXcW9RLBP_d+wUoF&W!hK#)rL0(qosChMA$I&7mrmiUUxMvIL{ePy4GB2d(+ zi5!E+{LX|5?ZQq3CwXfQ7v8w8c=mG-=MU`6No{bw@|6RZ%zkV? z?tNtdR`D_MC}a55JiM&P3Z;9%jrJm7Y^+g?yz~+f6H)ymBfBE1VLPIR^P}cov#;!q zENc#eOPc}mvIu+l#@T8MM=VEs`3cM+Xrn! zf*Fs&X$8#?rm{&La-1BzMe9kbYcpwmlD>wMx{;hiaskOjB>hMRfh?7yp_wUD^j9Xq zhSmc3zD@q>2`mR+UGbdQWPP6K7Dv1%H?XyNn0O_smKPJn|1%WxwdqoCs2_NTARll za$6&tEVN9=U>+9g4F3g?q-ca5S-`Jgt9;9zb_ zg~v!r;U5AX2Mr&1U3_9RpBK#6gmPkQD(O4O@*O1YNboJ;h45w>FW;iq;1iTbwCg}N zIEG<9BY{sztVm)TJjb+u%18ep5T?6Gx}JL}!vq#jKPOmkWH`c$^Rs{An*YoTKYVKg pK3o2|F~bqz$lnQrE8B{E8?tPFp<(!16fTh8hI)hC?*k0be*@ZCj=BH< literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansi_test.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansi_test.py new file mode 100644 index 0000000..0a20c80 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py new file mode 100644 index 0000000..91ca551 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/initialise_test.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/initialise_test.py new file mode 100644 index 0000000..89f9b07 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/isatty_test.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/isatty_test.py new file mode 100644 index 0000000..0f84e4b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/utils.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/utils.py new file mode 100644 index 0000000..472fafb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/winterm_test.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/winterm_test.py new file mode 100644 index 0000000..d0955f9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/win32.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/win32.py new file mode 100644 index 0000000..841b0e2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/win32.py @@ -0,0 +1,180 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. + +# from winbase.h +STDOUT = -11 +STDERR = -12 + +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + +try: + import ctypes + from ctypes import LibraryLoader + windll = LibraryLoader(ctypes.WinDLL) + from ctypes import wintypes +except (AttributeError, ImportError): + windll = None + SetConsoleTextAttribute = lambda *_: None + winapi_test = lambda *_: None +else: + from ctypes import byref, Structure, c_char, POINTER + + COORD = wintypes._COORD + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", wintypes.WORD), + ("srWindow", wintypes.SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + def __str__(self): + return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( + self.dwSize.Y, self.dwSize.X + , self.dwCursorPosition.Y, self.dwCursorPosition.X + , self.wAttributes + , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right + , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X + ) + + _GetStdHandle = windll.kernel32.GetStdHandle + _GetStdHandle.argtypes = [ + wintypes.DWORD, + ] + _GetStdHandle.restype = wintypes.HANDLE + + _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo + _GetConsoleScreenBufferInfo.argtypes = [ + wintypes.HANDLE, + POINTER(CONSOLE_SCREEN_BUFFER_INFO), + ] + _GetConsoleScreenBufferInfo.restype = wintypes.BOOL + + _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute + _SetConsoleTextAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + ] + _SetConsoleTextAttribute.restype = wintypes.BOOL + + _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition + _SetConsoleCursorPosition.argtypes = [ + wintypes.HANDLE, + COORD, + ] + _SetConsoleCursorPosition.restype = wintypes.BOOL + + _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA + _FillConsoleOutputCharacterA.argtypes = [ + wintypes.HANDLE, + c_char, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputCharacterA.restype = wintypes.BOOL + + _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute + _FillConsoleOutputAttribute.argtypes = [ + wintypes.HANDLE, + wintypes.WORD, + wintypes.DWORD, + COORD, + POINTER(wintypes.DWORD), + ] + _FillConsoleOutputAttribute.restype = wintypes.BOOL + + _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW + _SetConsoleTitleW.argtypes = [ + wintypes.LPCWSTR + ] + _SetConsoleTitleW.restype = wintypes.BOOL + + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + + def _winapi_test(handle): + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return bool(success) + + def winapi_test(): + return any(_winapi_test(h) for h in + (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) + + def GetConsoleScreenBufferInfo(stream_id=STDOUT): + handle = _GetStdHandle(stream_id) + csbi = CONSOLE_SCREEN_BUFFER_INFO() + success = _GetConsoleScreenBufferInfo( + handle, byref(csbi)) + return csbi + + def SetConsoleTextAttribute(stream_id, attrs): + handle = _GetStdHandle(stream_id) + return _SetConsoleTextAttribute(handle, attrs) + + def SetConsoleCursorPosition(stream_id, position, adjust=True): + position = COORD(*position) + # If the position is out of range, do nothing. + if position.Y <= 0 or position.X <= 0: + return + # Adjust for Windows' SetConsoleCursorPosition: + # 1. being 0-based, while ANSI is 1-based. + # 2. expecting (x,y), while ANSI uses (y,x). + adjusted_position = COORD(position.Y - 1, position.X - 1) + if adjust: + # Adjust for viewport's scroll position + sr = GetConsoleScreenBufferInfo(STDOUT).srWindow + adjusted_position.Y += sr.Top + adjusted_position.X += sr.Left + # Resume normal processing + handle = _GetStdHandle(stream_id) + return _SetConsoleCursorPosition(handle, adjusted_position) + + def FillConsoleOutputCharacter(stream_id, char, length, start): + handle = _GetStdHandle(stream_id) + char = c_char(char.encode()) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + success = _FillConsoleOutputCharacterA( + handle, char, length, start, byref(num_written)) + return num_written.value + + def FillConsoleOutputAttribute(stream_id, attr, length, start): + ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' + handle = _GetStdHandle(stream_id) + attribute = wintypes.WORD(attr) + length = wintypes.DWORD(length) + num_written = wintypes.DWORD(0) + # Note that this is hard-coded for ANSI (vs wide) bytes. + return _FillConsoleOutputAttribute( + handle, attribute, length, start, byref(num_written)) + + def SetConsoleTitle(title): + return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/winterm.py b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/winterm.py new file mode 100644 index 0000000..aad867e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/colorama/winterm.py @@ -0,0 +1,195 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") + + +from . import win32 + +# from wincon.h +class WinColor(object): + BLACK = 0 + BLUE = 1 + GREEN = 2 + CYAN = 3 + RED = 4 + MAGENTA = 5 + YELLOW = 6 + GREY = 7 + +# from wincon.h +class WinStyle(object): + NORMAL = 0x00 # dim text, dim background + BRIGHT = 0x08 # bright text, dim background + BRIGHT_BACKGROUND = 0x80 # dim text, bright background + +class WinTerm(object): + + def __init__(self): + self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes + self.set_attrs(self._default) + self._default_fore = self._fore + self._default_back = self._back + self._default_style = self._style + # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. + # So that LIGHT_EX colors and BRIGHT style do not clobber each other, + # we track them separately, since LIGHT_EX is overwritten by Fore/Back + # and BRIGHT is overwritten by Style codes. + self._light = 0 + + def get_attrs(self): + return self._fore + self._back * 16 + (self._style | self._light) + + def set_attrs(self, value): + self._fore = value & 7 + self._back = (value >> 4) & 7 + self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) + + def reset_all(self, on_stderr=None): + self.set_attrs(self._default) + self.set_console(attrs=self._default) + self._light = 0 + + def fore(self, fore=None, light=False, on_stderr=False): + if fore is None: + fore = self._default_fore + self._fore = fore + # Emulate LIGHT_EX with BRIGHT Style + if light: + self._light |= WinStyle.BRIGHT + else: + self._light &= ~WinStyle.BRIGHT + self.set_console(on_stderr=on_stderr) + + def back(self, back=None, light=False, on_stderr=False): + if back is None: + back = self._default_back + self._back = back + # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style + if light: + self._light |= WinStyle.BRIGHT_BACKGROUND + else: + self._light &= ~WinStyle.BRIGHT_BACKGROUND + self.set_console(on_stderr=on_stderr) + + def style(self, style=None, on_stderr=False): + if style is None: + style = self._default_style + self._style = style + self.set_console(on_stderr=on_stderr) + + def set_console(self, attrs=None, on_stderr=False): + if attrs is None: + attrs = self.get_attrs() + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleTextAttribute(handle, attrs) + + def get_position(self, handle): + position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition + # Because Windows coordinates are 0-based, + # and win32.SetConsoleCursorPosition expects 1-based. + position.X += 1 + position.Y += 1 + return position + + def set_cursor_position(self, position=None, on_stderr=False): + if position is None: + # I'm not currently tracking the position, so there is no default. + # position = self.get_position() + return + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + win32.SetConsoleCursorPosition(handle, position) + + def cursor_adjust(self, x, y, on_stderr=False): + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + position = self.get_position(handle) + adjusted_position = (position.Y + y, position.X + x) + win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) + + def erase_screen(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the screen. + # 1 should clear from the cursor to the beginning of the screen. + # 2 should clear the entire screen, and move cursor to (1,1) + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + # get the number of character cells in the current buffer + cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y + # get number of character cells before current cursor position + cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = cells_in_screen - cells_before_cursor + elif mode == 1: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_before_cursor + elif mode == 2: + from_coord = win32.COORD(0, 0) + cells_to_erase = cells_in_screen + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + if mode == 2: + # put the cursor where needed + win32.SetConsoleCursorPosition(handle, (1, 1)) + + def erase_line(self, mode=0, on_stderr=False): + # 0 should clear from the cursor to the end of the line. + # 1 should clear from the cursor to the beginning of the line. + # 2 should clear the entire line. + handle = win32.STDOUT + if on_stderr: + handle = win32.STDERR + csbi = win32.GetConsoleScreenBufferInfo(handle) + if mode == 0: + from_coord = csbi.dwCursorPosition + cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X + elif mode == 1: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwCursorPosition.X + elif mode == 2: + from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) + cells_to_erase = csbi.dwSize.X + else: + # invalid mode + return + # fill the entire screen with blanks + win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) + # now set the buffer's attributes accordingly + win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) + + def set_title(self, title): + win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py new file mode 100644 index 0000000..e999438 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012-2023 Vinay Sajip. +# Licensed to the Python Software Foundation under a contributor agreement. +# See LICENSE.txt and CONTRIBUTORS.txt. +# +import logging + +__version__ = '0.3.8' + + +class DistlibException(Exception): + pass + + +try: + from logging import NullHandler +except ImportError: # pragma: no cover + + class NullHandler(logging.Handler): + + def handle(self, record): + pass + + def emit(self, record): + pass + + def createLock(self): + self.lock = None + + +logger = logging.getLogger(__name__) +logger.addHandler(NullHandler()) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2699b44322fef5fcfa50618ac4ff8eb2e31e9a4 GIT binary patch literal 1291 zcmaJ=%}*0i5TCc*F5Q-oB7%s37`-%xuF8djF~LBT1RLXxUN-CQvn+1+mAA`Bi3biG zIA{X#fd2yFuQ2hXCt?g_6B80o-Wp1b;pEJ=rC^NjFmK+xnfcAk`@Jt+U1`8|{8@3A z6o4OeXf3U&)IXuJ3l2EUgCaOekty&5oWv$LDp&Rs4ZmVSC!eGTWwod>P=Lc?cPm+Y z=c#?xjL^Bh*x$ZsX&9;QHxDge232+&c=8U&LCTHz8sc{#MrSLoy`R^l&coZ8Rai^R z!z#T*(1HvG?$hwuW_>nb%HnxjRjHWMqmYH%Qv1&vB>Q(FkXn4B6RJ* zcqaUkn0`Dy6$Ea{FLOUM@+;h5F%Z+JRKo?~Pvq}EFakH^;}y$(ZIyUnRNRVTqMIY6 z;WP>~Ow;w<&@}Uvs#Fm=7SvIYM6n4+YD(%YdyYk!!m{T*wS33p^55eaMep`F9JUF? zgrlHT>HUkzs+vp63_96JpG%8qQKuqyp$dDfAXBLCDUlj*?^UEpZVTxI6g4&`iG`82 z5LdE((s*r9Cc>n9{vv~iFq-k>_P1kj>^k!xSqX;LYC_k(MN(tWVaZB zI`R!5?&k_B^cCKKN%nYEAqK;7s-Cc4=a|f*J)rnzV`*D*E97%{yFPFcwrwA#5W>X< z2Zy(&-_6EV6mpuRwk3T=*+GIzko0ThAPJgk<4tLX@l(X&DxQK~6q|6OGQIaiLs3UD z((tA?%VnYtPn1foUy3p&l1T>0Zltk(PW1+j$T`Z)mMcPrGg1l}bu?KM=_MY{VGb_i zLL-e5av7y9$7%ZfRpVLeVdK9%lD9A~%_&$wQA;q!>fOM4j=TFeGN&42`gZjcaI2-d z*vNPFc5M{WH@2=F>O%+m(1*#d`kf8!2(&}!J%HYwyYI)p!PxKGO-S{fK!%N+s(Nx{ gyR<8I#4kX#mPF%OJeBv_lmZ`>p{bjRkE0CbA7H{35&!@I literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6d7e25dfd0d68a5be0614f4df3104c1b11cd0a91 GIT binary patch literal 45627 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 Fe*i5oEnWZs literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb2790f08792befea7f6cc9b1990b1f9de391bbd GIT binary patch literal 66049 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6f8f0321b5b15fb9390c8057038b1c5ee2f3dbee GIT binary patch literal 24388 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%0D18G+@@%3cFK*XJ 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..fc154594c09094bb2e13c8d3be87a5164986f0a5 GIT binary patch literal 15147 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..11717f8b7a7a18788e4997e39ef1ab0e554aa577 GIT binary patch literal 7704 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..e2081af2a39bf8e193c96dabd818a4eb449acf2d GIT binary patch literal 41821 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 PJ8g{f(Ngz~8zTP)!u#RE literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0edb847741c997908c21115d374461b641bcb68 GIT binary patch literal 1412 zcmah}&1>976n~?ym7QHXaqDE=UW`LZMevFymq3e2VuwP>x}|mr=rU-f@mi@qwllMF z*ZAOrp=)U9sW|^aaQ~9_(vlonVMCy&-b^=!kW=4Ct7LQPfZn`$-}8HK^jo7b4Y*$X zszrZU0Dr4PwahUy`U06F2q7dfB*Y^`@mj1Ux~CVs9vg}2nFVjeR#Nk71T^>@Lh}GZ zirB7w@e)hvwC?uPUwYx}`= zlrofFZuesu-5p4_y6y7~CcolYhjG-*6v605o3_7PyLUer7hi1S>@d2I#~<)63_KEo zr-kMawn(TQ)IFUUAz|j07U~Da@z~q5-bjrJX>ADK6VKiN$I34hdCM|#i!<5hX`n7x zEtOn-qal)?$q~p(1BYY}9wW?EIs&o+9VE7+T*u>Rdq9m!CNET6<%`-k9((X`w$i`d zD@y_cM1S3}b5rztjOR9`Q5s1~xr)zKn0zWI;HM(ts`z|9oM;pERiAdz)#OYt>1eqj zSiF@_Q5wX)5R?iHy%zUh@3`Ap!d&5t#7|v>=^+cGaKFwvnT!JAwzIUGt=(O|BSh3q z6P8N1^@yd9+&JpEy@A}$(wnVMZnz?nY`NzL-}+r9++NgkDVl{DcLN-RkV%*I1G&>H z#-Y_4@H*z-eSe}kt?HInQ6pMNUhTt)-8iiMVK<-KADk>)|7HEz!ly&y$C;CBE6=Xo z9vaW>r9bWZPt%9fkLx4!fOk5p1Vv{RY)3F4j<&vQVanyr2T$QC;TKUZRQDG79MZh5 zYDK(g>L+ZsIa%DK#zEVW8iaj#F?)r-hur_>&8~9F0%mw(d-+$xx+Inom=jWMMy6e4Wzo`hHw$3ZQcvfyYsjgIveg%&(|e zA0o*cBLehH;TX}1eZxUEbTSyGA>B(DzOTrd26mFiv(o3sBn!nF|d=9u5 Gwf+Y)qg0dt literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90e26d3db31076a866a81ab488ed3b1d937af3a6 GIT binary patch literal 19148 zcmXxr1-Mq#)&*c1B%}oC?oI)N?rssK^Ux)ol2U>SC=Dth2nY%Wjew+-0fMB+rWHZL z_}6=$`}0?hCcE zNc$%aVQ#Vz#tVO=e;NHS-FU)6q+QJR4NJuwbQ%ouI9&*1jZGK!^KvY9jG<)Q*LSL_ zkpQGgppPE0_7Ls_S4}whPq;0uVIrNhE0c2*gpdf;1G*VOZl(!gFIS6!JF6A2)-aGn z)V_2oqWi2n_>*{2jT&%WLL>T;jXAOVYBV)*lh7u7BaJn+<$r|MI$gvP Gg+iVYe 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 HzwP-y>;IG! literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9353712e71ed04b006d598fc2b7528b2d1d74e5 GIT binary patch literal 1425 zcmah}&1>976n~?ymEGMqad6^IFD51dQM@AdrGb#dA0<$-PN{FY3|eWtR-%vX%xuUS zAAB%m4GldN=U)i!U(#M$l0y+T4fNET>BbOp>Kkd5ZhGl}-n@Bl-uG|xOQSIjxZeJ( zMSoZTf2u>Z%rP_i8kr*qAtW&*#3MxUTC63yrx&~)8;R+e1#iSwQuAsAGtls z%wxUu<=KOpWB*0)sUHtSBv3oMo`q4`-IBg!Z9mwF zQijqi?S3qywSi>oJ3ilH@*AFY7)Q-a>5Xo+Y5UQ}R}X`6`o%Vm52J^8e2&#H@JI-r z7MerYCZTpv_jG22gqhn~s2>=|V{6a)C^aUewIMtsp1lQ*mDkI_Wg@x7ne6j4P&HOd zMOWWwh~!6d1hUe=A=!f$*y$=B0a^7OBu=DU$764MK#fW!KdZRP7PTKO_TceMrT=%Y zZW0*a05%;vH$}h4cy3b~rIDnREB{=%$)|#108}JgC7;iQ6N93@n$#|unw$wH9W6Hm zi?{PBN`u%Jf>NQO)x!RVXYNjxFjx2@@lzMu^q2)wxZh@-Oh$ol+gaMpHrAF_g^0Ro z!cytBp0f0*8%G_tH;_A7y3)F}?21UTrJf%=@w-g8y{P9>ycTBM4G;<;lWw=8@YE|1 z(drF&9TV7{n;cK8s&f%lqJ`xBKAhN%!`g55{2Tk)N%M=J9=&ef85-YTIJt4}^^Mh` z@y5RXyIucb`f&P1eS{Y9$!wLNn5}Bu5e$f83NKFrreXG@qt4$-;iD_&TNE_5HZi6dliPoB)DK4ssn|HlJ6ie2ye< zjIj~0IHuGg{E`Z&61-pZhTH0@BG;ATE|T{LaIP8V+*uv03uhWJ=U)bY18yU|5}a9M zuK99NshWlA9o}sgB@_--0T&CLOn|t=^=yfAp?)+;`J*XbK|Ygo#seuK#Z_x^3#9_z zgnA=o!bPvue2OAI*muh2+~qe>rm7PQNX~RZ$oZE*ZlA*PDXg5r;ybHBn!jFl&H?u# G*M9((|5P3T literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e2e7f84867c3acdb49544553e405cc00915ed08 GIT binary patch literal 5844 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be820c124297319ac952c5b802b483f1da2243ca GIT binary patch literal 22177 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 KqtXxbiT)p1s_6Ux literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/johabfreq.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/johabfreq.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19292653cb83b4275a6a2ecb6e5baf0a2f549100 GIT binary patch literal 83025 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c41ab993267c739ffc122c03c87656a87ac76ff8 GIT binary patch literal 39571 zcmeI5UyPjBdERGcXO_$5u9gx>ktRijq$!G<*kmPHE+sosX#KGyOQa!EsLKQxtapa= zO5`p*y9z{tic~;ANH?zK6oD9^NEoyi6nqh-y`c&;5qePs=?zzkB?T;&*i_W&HU+1rSe~M z(En*%cdq?ba^9)5Dy{0#%CYL9>ap6P+OeTSL)A*FcBo#fe7({ddaKf^|ESjMIMg85 zNV(y5<3~f+v%_Rpr0mEI*%c>NPL5v5kjbHL{rQ>sg>K{EtH+PFyN#D-XXa)n4@@5D zWMa*gn?nnqVed-yrX~Dxm98k!QkJ`oea>y^fQHi_SLy}rNZ-4X55KqABG*}&bA*~q z(92O3f=4wJLQWi{ECKg_L(aTq&Cz1_-fE*C!T5#VSWR)BtIKfUU15>3C zrXeF3!FbMKc+TB)gRC>)GiN^Q`u?srET3@yfILdhm{EFG@hIS7d`$J&OK`=r5N}^7 zgmB;tN%1re7ouEqNCO-malot`&#GZq77*v!izflYVZj*Ug*L+semDLO`sKdB5%>Si z4c`R)8^mMa+2NVf3r_%rV1SVGNm)K;6)zZA;Im*z)j@_l>~vVA&9xDFp{zQn3=AJ& z4Y`5)Cm$Vz_S%LP1!Y|SfB=2g4bngBBL9x{^$&=IYJ*Hb9yjqibtdOQD-5rSirODNOBhJU^vSTGAsxN z=M4D5mIW{hpu$ikN1Gl6a}?NJJIcUx6w68nRQT~PrFQGVJ@7pes)VWuRY*1B!md(4 zq;tuMvw&v^eF)hA3EX6IZm4qwyI15e&X5@oX8D3NRSChv6xIF>B4H`?AjBM!6sJ~m z0bJH$jyBis#s|Ja^$ib|IX8;7GL}$aBsq=e74}z15Ke)di^ij1E+~~n2!aR;Y5fC& z%smPN)T$Z>*yifQE$_)R`pSH_kDh?mgN*q zRR~mJF2h|V9!kr=E0Ug=&56C**aC6W~!h;cr^9r&XE+aVztl}vy5ICr;1AX>-a^YzNLXHk)IE_Kh zAQY^Ouz7R@8M5HXEU7sZXcR8QwqhOq15!=dC>k-~)G8Q82ZR-a1A-S=XroY8B^QPp zx@CAUyO_XCXs;n21R*C_QboGTAksGZfM8VWt_6Z(hx)&RL~^pwn$x3^Py;ui8p?zO zoCJ;x1cdI4GgzV_o;eGSH0SnEb*W(xII5}=Y|a^0@#F)-Dg_e;2yL=tNdE@WXbDe+ z!2=98hN=X5j@K)zsw)gK1VV4f19IdGUzC1j8gJ-1PuGr%bl2st5TIq9itT#?M~v{gnL0wxS|W!7_e z0GAb%#xyD3sR zC@f1YdI9VV3WgqrBk_ckg$#m9eFed-2Lu^Cfb7;o2*!D|X~J$vrB*$_g{0oMpjEXD z%u#T+2x-GpDKo&i9}LHkqm7U@huf978hM&rV z4^=WNQcqHHF2=S131Jr5KOnlP>-nZ|DBP^hd3YG5(*c1w=*dwnlNqRh$n0EKVu!mx z4hRH{B>{GC8f`6ddHr9aJo{sfX^4D zEDRUy`vK35DicmMC?%9H&>MoY7mw=CX+8}>Iv^)A&t)s!wUYzFzG2AeQ4Kk%#E~mA zObhggl$7c~8C2~M7p4JW8pf)}*^1z{TkaF$Kx&8r3>nJ^|coZ~ntAUX5Kqy!x2Skr*SV?Fr+ZiBX1|i0E znxn%#QYjwBs+&if)gVn7E&&hC<{${Yd13eC435EO#&(5m#ZQM3t>Amm)99uPVd zjE7MWo)AKfU^~dMSaM~9n9E+uj2FNmq=TF@!V=0l0Am!cf^ei6k`9QhYx40k2O~V> zisQZEZ>!|gTIMlG98};ihR&eiR?sVsg1v#6?Qx`SZX>;hfZZ*t^oH%04;;g#!m|S~ z5RC+Y2~|*#_HU2_K-z<6BRILsJh(BT>TVU|d;jSkIF=JS2r%PvCl0;1q56iuT$fdq2C1Rz2uhWv47CKXe0V6p*o&tr6Dhqoy`rOkKwMGkF2bDS zsZ#J#Bhw94KM)ESSPbn5RS29Ru53`i8L|O(P*9qr;;6C*cmu)o$_I`x?4NuA%t#f4 zHmXI(tp`*#Djbm9dag+iDj)+*A*A=VBwYR=!$_Iw4Sa(2&=*2Avpp!lguMjE4Cl%h zcAA1YJM^Fz;yxF9&_5u0MB+I>9u-0%L@z8%91sSTExVA^dw&7eD9Aw}M9&Kf$bO`aK$3mfuT#VmuZB~4WVs7m_ZI4p)&9?u75ybfIUvZD;j&hGeTRKLKPF*4E5-h&)o;2 z3?QzqY=*s(+D6U=hR{Or2r1lR?tR8IoIcm~YGL^qzqqQJeY`xX+nt+P`UfNr+$_;4dQKr1z_yinWgTdhls1gFfHonHB!}^t zb_UE+Nnimmp)UJ8Qgpx{!i;@Jimks5OJhBm_tx%GhTaR5CS)le-{2t=Io0IN%S zz0eC7BZ~xpCDjvR&Ka%)Z=i85gTz7Qa6G|!c98RI@W`o3k2b=L>mQJ0s?oEGS5_#? zg*JNu-)@I2s17TURNODs8@MbA9LqpR5T3#P1EP_-o?BrKPXmO|%?oYnDr+OBF2g_y z#A%TFAnMtM2hMLiaITUnq>2nxA!Q1jfoCrsz2<HyXb zj+~?PsJfDXb4KU@M^y+c>T@_bL<$LQR$T|)KqEnbxC9}p3VTJbs_-B%7w8EKx%~qY zR&<`a2#TD4KpgDwLc;18l0f}^Hd{WFY#U~)NQB??S zKrEP}U=`0n7Eo<9&{O3ud7)fqSsPUcNpD;50J|HIYN(=$Ha#Hbz+otWBLpH?FOTdW zkdmz1tp_be9KGrnj0Y~{=rAXhxq-bxT$qBOk`Irn3$)Fg9p(mlVGja*2xXyw`e20U zA*lR-+yFfr@hCvy!UUUBtGPfGLUHoR;u38mX8{5`z)5P`^4_QKSf0 zZViyqzvH+MZvzNJLIR_1t#@eX5Jz+RicS|bw zMS@VZH+U{uPY5|fyqxR5;zG$qlzByW{ExiDIvq;K^2R~1gkBCK%mt#cYOhq}ycrZ2 ziJ>sC3bR$76LPRnAan_ms=yBQg*H_c%UbS$loVDH20+Uwwc>%Zs#bGSF@u6tNW&t^ zalLd>|O5!QzT!p~GDZ`V{1hLT>+n=m7~2 zp1Fae;Gv4AVU&{a$SE#V9S&cp+Dkz&p$2+FIuK+IW~JeYbCtAFU=U&Gb*Xqkm2G88 zL$8paAd{v8Qn1J{AZT>}1&leSV9W&qAryixb9xFAEQ^EKC_+030~H~rtZFZwU<+l# zt>S3_mx)vIfC0r>ra4?@ z76Yl9BF?1(A*3o8DhKHSCd9J?D%*nRj4(3K?Z4sx1zJa04IUaap}Ksi{@cZ|Q7(eE zGM2Jjm~k7B(D4fimzR;3+CLzd%`TwE1JZ*Lh$euc0;fl4jt&SckRuFLLUXh!HxLNc zlhigmL$#E(g&BmQ*ET)pl5@k@&M;Ci1v>JK{sBpAoi~aFJ;{Ph3wTo5Ylk`8LM{}1 zPQ=0IAUeR=TQ*XH+x47L*5S0Ws=1(LdD2|%ACQe6i=N}b`2;Y49AI$f@HEJD^Bf7p zDjrn~q!?xh21fx>z*GaX3M}BM1s1E0WCjG}9BGa=LWSdL=mo+On5zLN0b$Y6KOo?g zJQj1oa|*N=Aci4F72_rw;<;t6m;zJ?Cd3r_J8GdoI50;+`oL_6hjH_)s!Bn4g7HEQ z0z1$GPOu}vWyq}u6llZHFa+)TMvjbp)DAD|D!IzKqz2Xv7sGGH|GrK z0|Y7x;(!?1W)6DS0mKjjm@%}e84s``!wjpE;z4Db9#z}IjEqYML>6%(@zftYSM0t8 zPbwFIE)5-ofHCI6ngTtf%7Dc%^oD}#bwBi!;i928gwJK+0g+i2JoN>SWeEv0GPr+( z0+Jqtgi-DwqX>gma%IMqSS6<}3&C^k7F?KG@Gx{>%!NTzVG%5;Roa+KP6dX{2zw<# z=mqCoJf#t;c~$8t!5A9NF*!V@K{W#y3fLt&nVeyG9B88oGoI3&h9^B9RJI8b2Ni~@ zOLbQjE?5sLAQ)Ker3zT#3JQxBtVeIb+`mSS|2aJsmJv|Z2r0`^WhG%NC^*R7m(Sgj z8J4gCc6@q|89|FIWrNV-d4;vv8@$~1X%EQl(JP_-TmHXJ+p8B?-04to1MmU?VJMIb zI5#=a-$n@4Kz$N~kwKNXfnM(DA5b`6dd#^Tau{-WOg2X-LFn)uqz9EOdd|RO#qdE7 zPDmI;VPF-oJ52}}QVM!f1wISDrPW8(jS!3{JrKc$Id|&;i5%#mChRX@jEmI^RSIR* zKoDlQp{x!9wdlzlAkfo`QI%dOcvSXEZyPxe0}m>55>SxY6T!11L#ETei=W(>f3AHm znRhDf%Asnja;Vm-9vW)Z4%J&jhZ?Q=q2X5J(2CZbt&z9thepz$E7PAVTchpK_Nw;S z;o9d?Z);V1yfxffad@~jma2EOhT9W|YpwC*uTK7*$-g7{ceN&N>KbmZX^*$ZdmM-B zts!<+n#SfF*m!=er;jz?9|Z{^E2~Tvg9lOJN;Z~C2!`x4OJ@q zZLB-oX)nCmncX*CP5J&$UVB1RuDzVhJC#=TRHfOkRgP3rRG_J4JU^{ks^!jJG?b!x zKRQ);Ie%`2pC4V1tCbU#ckBCl@BE3Be0p-YJK9{BTbMf9?6i+|S7qCrpE`E@XuErN zbGF@XrD}8jm6_ws>F-Q+nzOGS>#lAdotj_h#dDojyVGqnTXVDRZljYnW#Qdww?5xK zdU!s|-}1B0?(fVUYww<)nm;x*yZf7+xtH713-h~Qocs3N!p!vi?ic1}kIX&!wJ+|S zpPxB0d#pXXuzS~Uw`YHQ_tBYe?>>HF;X8A)5AAy3!QJyS3+*o+pPGJU>PUNj_wkwI zyPFBtn(OS&!dmTx-7g=Xo?Uot*YOkGk!Evdc4nd3Jh^4SS?%g0-bfH4LS4NE)j&wqY?Y0I|!=jNq#8&i37X=LmNqi06HKXxrORmxBFaH_-; zZB>t?6Mnfk#oXQdIoYV>(=IBUw&$BqyExxgKJA3>{hX+D)+BFoxU)9>(Vf`$(hJS~ zPdA^~d+@2R?|<KLr&B$VE5&wMe|_lG(5c!hsnU5rA&PAhajKdSL#^t` zl-gMN(e(@VTdCvq`sJegj%Y1_SyH(kKlQ>W^!+AIGsp^vAj z{r8qbE7z-2rz)>iU(RpO@Y5Q~Q>SYlbK)t%rEF3Xe_qKtM_Toyuqd%ZV?|gv3wQM0 za;5oUI91Q~wc2aFw`Wh=&~)vq)Y6pV#ERXn{@#TX>D4mX*w_27{^eH-|JdZ<izpJ ze(^W{)4hAo>_7d~+k2MA#^2s>=7rOHmnK%f{mA!TU)r_%y~%fvy~ z&aeG=^6|yV$3I;E_|kooXY2oBbZNupg#38z_Qkc^f4pI520XTy{`_#VuFMa))aF*e9)15dnx6o|Q&b@l%JI&VA!qmz82OUiSzL!razsAxj zO|1FB8)x3g@1I8()4wJjy*Tpdt&7XgDQ}ZM`neX@nM?~$o$)7S>oJ$UGOD~tc~g{i zolL)$59Ie&$@}3zWwO@$r94^h?d9DsJiG7N7t-7O$-M{n#+$M7$^H8dHoviN|38W^ z^X?t_hNb&*c&62!oo=7pF>rHl!u^}Hp837Wyji)NZouPb9zT2Ve1|vHJjgkb?RuZ(%F)*o%|ip`C>NrW%CcR*_=&2 z+fL^1Hu$a8U77!w>U5?~bcbeU7dpS1YxQr>%^mIR0(SW5+|)wn!Q7Wu-N_5-d^wx^ ztFX7Y^wO_%vUx52d;Ux^Z&jA=UjOF)rPX)6`RvlBdoR{EEN!^wVtw7x=Hzd@Rv&Jx zyS93?vFYlDO^sb=Upn{j`N@l)e`s;@m#$V);)6B(@t13_<>>08)y7xOB|rUfb-3QR z_gv#zCH--AtlC(2_Th8u&ab?<^?}7r4_-}(b??1;;lKxmTJ+ zKDJjCpVy7_c|CSI{enC+p02}+?wSM7J$oR0U~`~1y}ybpo{#Des_#@53aMw(*Vgyb zk@Xc+dtcbabD4f!)%JB)9(d;2rw=yMtjVEnWB%3S?G7j29p`sX`kdv_x&C~1S2ml| z>En04+3Y=~Z}tu{KO-maDN$T}X8w6X%;#T%fAePL$0OrsRxFNexHPhHX>3K{II^|(DCFO|H(lyt>;HSIB!SiW>WS*9^a$j;@_IV_Q?=>z4e?6S-${Q{ z%YV+Vo~@>1Xxvm82C-$fR;|o#Zmme)UbWW9D?784*2)s(ILOh~=#Aeyv$fW$*M}Ce zCVoy;``>o0ReT?9OUhQEte*JCGJj`7`ev%%pm7`}ts0+3@*u2N%~* zp5JqE{X-X5KXiHG&g-?_6PG@yE0cG0_M|^Lk7V;`HuaHiOw^* zkhi4sm2C2dYVW1{>)Fe1-gKRN7k9dS^k}o0F3i&@%U^?jqj!b#Z@*49|1K9YQ-fA-SbozJkQ@l#^tLugrX|Z%bk{!h(i6{pGrl3Wrl#I&vUt&@OmSELEwkcXO*dlb`i)Ds-;tKsZ_vHyC zSLp7u+dmw;_uP}`pS!gEu?yQj+WuI&J!hU-8ef0*OCOH^3c=rYUASgz-`jL)^2rbC z$$YBovTt%_yeiAyi1;n?n|Wul`7e2u_b2mK@7%d>a-=)FaN_vP?2%4> z;(B)w6+3ds=T5!{%n1U1HuBZpm)T>TR)XXwYW}fgt`1eJ)t~OERCg>^?z=j2u)4N( vv9kK7$7^4!R@eQclIq`2QyZ3(pH+@=u literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11aee6c3665e8956c5cf5a55e15487fe82831be1 GIT binary patch literal 83144 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 x%EJ#fuagEPh_e?KrfJR~U)_1PHx6 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba33ed9acdd6eb79c7f11457d73a5f16e5b4b5c9 GIT binary patch literal 105273 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f82abe4fb62ac11f025b24bc7324478336209467 GIT binary patch literal 77699 zcmeI52YejIb^ifYfSm+;6Pwscu&Fop-g^f@3`l|vAOQBJE~?m;EZedycT28vZwJS* z9mjDT+i@H_aVREEHgOu0*g4W&F8Tj{@6GehQgo8TT%RRzB9A4yYGJYc6M%o z`$p%^9g6T@^z~LXhn^@d`T@cI>+cIcesFV9(NjgP$QAD`+E=`>xVWgM$hG2M*ZRp~ z*G9ChXgksNq9v{ft);F5w4-Pzsp;&xpr)(z=_WPZMSF;rNqtYzUhIke^p=)BQrB0? zU8LMkM(r=<0a6|)I!JV|^t?{WL!{48DYusW50jeVQXWBlj(fUZYDY@#C@GH?9V0bk zW&3ebGhS*sh)xilC|WK$N%lQidQK6YD%(zD`MF}(+I7GmY8Uy_8qwvS8{XRKxnjmt zqRYs&k&)WU*6l>wi&}IUM3=osO6@4=H(J!9%hqwynv5>%CrW*}>|>I|c(QCeMRcmP zPNO2ajARF|c(r{wj&56tE=RTK(#Pgq)?Zz8Ioiubm;Uc3N8DeI{&Lafcoto5Ytdyn z^e&G)8C}+lk(iB@{UyE2ag*Ltr)cx~l%wdnHPuhhF?ba8Y+bh$Un3>sY?eSg_=(z~p;=yJRva*WBm%X-TU=W4i)i?-UDZk@x*R(hUDo7}E?Yaxv0N@Q@VG9GF1NPmGJcjB*f;52)+D1# z@0oWwjzyO}M=&lqyvsI=Zs=XskC!}PnSmqPoPj+pGw>)a@3K#LRnw?y-n9r@bUXfm zy~|^@s~XSta?xd`ig!0O>0OO3<7zW9_OTfm$_ek< z%uM5J(d`s?ms?xj<&h`7%T}9_ai7=5yWEe>ce#hlMVEc9EV_)=7>QysGqB#g%cIGk z8Q5wwG8_k@t9e&vWpUPx^KNH}F87ek3@?o?Yb?4fUwL%7wPgmDuZ4FxPCoCl&8})J zTXb0-FYf?E3FTbgW&LDnog!+{MSa4%mJ^cR?fM7K3_SW*Cc4~H@~Xy~T+!t_GuKs( z{aY5@Zb4??*7-97TYq2P<$EXBoPqnybyZ`1GVih`^e!`q&B)khd6)IMyvvB1ciF#O zoN8A!)-=bv%oO}%dAED&T|_RK8TeXXS#<5r&7#}m4;)>3CVyt2(~{n$i*jAnSRZ(coyTUvfq|TxawnoN# z2>#(J1J}rG&g-MgbC&pWdzH8v+>+|9kLpDL>&aeTvd9J{K;vxYuFbh+oYvh}rCKW8*- z4H6@Zd&B7RxO1&R@+d4f@O+ZdW$REmM)(EgVdBK$qG4`eo2@}|8;dSSwCJ*&f0c|Q zOp^Coa+Qpu*-8U_%fHkd?sa`7x;$(1F3T2Omam0(F-{O&j%vAq$C`iEz@EwIvc|4! zjE-eq)?dx6fn(Wqjd4wW_QTQbvmcf%x{Pc1$J}d~w~IuV<0aS6dDgkEYpl<8U1NRn zlQkYiPw`j$$r{hW*3Y?5`(%yv$@Oz?JxH80SbUlMWR3mpy2d>Zm!9FL6Rgd3UE^N! zXI{1@uWPKCEN79-yr`G!8Xe1wo7=nnUJ_j%xp|kzXrE57-11K+xYebz1|G3_mt|Yw z<(>yfoGdeN>tu9UW8USq=3QHt+VA=yGez3_Sk)S2ebpcUkT#uWL8)S1#|ePkdD)C9i7Svtvz+|whcy;mmMt^zm~v$X_OX=)lqI?vSI%`AU(LbsRc(Mom)lu% zc^2kfmVdu_H;gX#*1R=HZk1eV(CA`>%dGHnA9hvaS=*-*tWQRlHRfIRw3P<#DOcW& z=eu!q2TF7~QZl-%Nv`m+=1ON|*cSV-CmJ}CWd)51ozpJ=yHUf@=lI31IMttHMZl508P$! zS)a_i-0C_xf6EN&U2bd7i?Ak~k#QT#yByK(h1oBE-sM(yRbx-PpWvSClQq`AbiT`+ zz$=?(Udy|Ko9$g5V@smTBej(VY#Vr&Bbj&E>vG=Z`B-$>KbLpeC!cpUy4*7P=>%)+ zlQovjyXQYoB-Ngyuz;$C7`(%hs#U z3_P0Ty&A{Oze=Xw)#%zB!Tz;qn|Fsubh(AicUiV48d$dIa-S`kk+Hwst6?1RF6;7V z2JX$~418S&N#3yNa$8%+VcF)p+{)&=DEq4#d)U1i<7dx{usl|>%s9zfHfLbHd6(tn zoPjkqBV##!l8o~(W?W{4IP+R`he~w0MH|^${#7!bg=GeoJIgV4740UPYevSNm(F)t zmwXzB`?qJE7?)gmH;gW0Z_#CWxEy;p-__`{E&q&+$B`?#theZLoLuV+=p&iIR>?HF zm*(BpL3BCl<)X{I*?gDBZqa3YlF?<&rQT(o-K%k=eO?QVjtx$f22XQ5*TH1_^uTXME#`Chwz&%`TX5bij?V&a!<548@F8d_&u11$fhb#xR=(7CM zdo^xv-enK-F87qnyR5ev8TVmpWGve%4$GEznQt}Q=3L=*BVLjjcogj<((UD)buF%H z7*o8rbLm?N8iS>HVWa;r=4D7dxF%6J|v$qpRPvIDm@ z|FS;3qu@R)_p+QnJ8+cbtc*359r(&6@7LIB*?|#dj4d-*$+E*pi7t=LqRX;Hm*rg1 zWqoIHcUO6jc9U|hD;s;hTy)vqyy!AU$@{gJMi-+cW@Q{bf9~b>c1OV|+N_LQCGRL$ zV`~j8+x?nF)&8}63XAS2i7xk(j4o@ExtF7wcUf+pcX`zD8ZRmNoGELP*@0u*tc+#L zy*whz4vcg%JFq77F1NJ1VV3Q)OYU#1T;+0oy2jT0xtFc>=?{(u^9DLs|Jq8K#@V7f zTB6JClF{YS=3mL-nKm!F94C%0&oftaS#NV*mMyy6+M>&Pi!S${j4sdM<)h2(?aIb- z{yPfpHJ5i;Z%;yE1c|P>R%d0GdUs3^U2dJ6^RmV=1It(HT|@)>=`C5rW@Q`?#{{(% zbk^j*va!{o%dL{pWxT`avbTAcZO!v8d)f**_hDBymhFCxuZ&&U^txu(H;ZjND;q1( z>SvK#oZ1Y`?ohvh-Pma9HEiE&!Jl4<1ILde_Uu|^5$g*tFofvqR zdrr<77-@?x%NE_{cvqv#t>PIO_h8;-OD^wnY>O`Y*xfMeEi67PUM@3ebUEgw_iC)OJ7JD$ zcN46)%)qT}6_RCJCu1MW3@lsTMMO!n=H}g{x3J*<$1N-yO)bDePuH; z9>wK8b!Y#pi7s7pE%L6sj)^N-F7HkWyvrj`&KY>VmUmgU=(1=2%)p4g+?;`1C+EAY z$@MH5M@nAR7@eUqU(J_*)jvm*CM*ICq$Q7S2J&}&(@{}(PjKBGw^(q zdAIq|WzRB+z@?x5uq8QX;C^h4jFD@JcNrD7@e2JrFWXgZ zS`b|xp?O!M%bF6I^_Gfulv#SN&(?Sb$r%~X{nEP$*4dna<>Z_}qsu+p?-5{)%^6ru z&KX#fj4o=tcR7k()!5JG3@l#@@8W!MRl9U1X6&OY$I?n7*G3{|a|WJ!@=lmFmKhk! z{F#BRWpWiU@8X>BD%$h?JVTo^uxxpk5g8!IVly(<43^_c&KX#r%nTeSoHOtkaz&SC z@d`y(e9I%XI|-KCNn9)|@Mv=VW*i>9eJ0Fu(zmQJ-(pOOEbDUJ1#=JmC8o)&z6zIhpeJuR|`N)TDL zBt6SWHSZ@axvgEzShkG7a=w`u{cIV5{a#7W(hrsmxNq|;%QjD7A6r{see<#b&+5`U z2-aC-IgtD&pa(|bb-?C53^DMWz zmUj;v&mzmRMV94!o@Lv$o+Dt)%U#8SY{1vnu3&tBCbI#L&mzm7&GRgK#!ohJjO6T# zzOySBTXT7q^~oz3N5%a+G#ObOwa>F`OL~?yxt@7seFqt9g6Kq1`}~)!lcgrPQjR0| zU1YKKC4XdokSDS{<6LtCo>Q(o%lcebFxJPBCD|2>Wt(}ioV;sbP4aiU@f>aD#iL8k z4d{e?*??_!*T5|;8*m?X1!KMa>>u}ZxyYhFB5SMUm&W#5MwUm@vdD4^i!9GQ8CkYk zWElbTEF&;f=3&XmvQHRUvUye`OHF<<$$eaDWHDM0+2(nc$7EM9mXlX79!)OKvOZU2 zc|P$KjN>J<0c-3E#&R+nu*POx+=D$u$};={P5#6mYVu|S_O@9U%j4udZjt4PEy)IP zWW)P0i*W2&j+myYv)?qbaW-hFXY)mt4(_6#Imi_(_3X8ZEIsQ# z&C&R7KjBjw?uL#fzL zKcP{gy+nJ8juss!I#IN(Xn)c6qLW2$6}?Tgjp!iJDeiV0#T``1x23{UMJI^fDSDUa z0MSXJ(?zcrEfei6dY$M<(N3a$-3;ubo9Ilc?5U%0iD-Ax!J<7xXNh(d?JT3r7M&wH zOmv{=Skbwn^W1#wX@Oe^UE~(~JuPud!OPro=nA(Iy2`DFu5oLj>)d+i2DcHq$!&&i zaa*AkZX2}HZHHDl2d#EHpgY|zXpP$q-Q)H`_qqLkPqnTNe8AO18(bswpgRQJ>kdPY zxTDZxa-_#aPl&D;-7k7l^pu;6o~PX!=-qA|bg8=sy4G#*d%D-%2fp7u0DaKi3w_A# zgf4Vvpo`tZ(5>zWbep>$y4_VmovVS?xs%X(_XzYJcMN);tAak@_CX(Z4?-Vx5BWVk z=1zfM<4!|g>yATT=N^YXAxHXp(I-XUAo@npH@T}+k+3)F3-B-YW=DrI3bN4mqU%0PBzu~?K z{Y&>P=wG>SL;u=+2l`#t3i>y$4fJo__n?30+CtB|7og|dCg|V0cF=!t?V;ayC4Nu; z=t{x=kC~iYg-##KfkBVu0LvbxB<{zZXk5G8w721gQ17qbqV?F`o7-`nwTG5)=U84a_d8{O?S_;+LLRz=m^suljW zu5Mk;f7{@1Tl{VJc=7Y?Kh$1-3;9OW9xWMNT3l3go;72N&-Gqavw!E_>X}Cyt7q-1 ztY1~#xTMxq?>*lZ&F5QD22=V{N+|q==I5tThEXt7(fO&Aa>}ig)|6W)lPNb)Zla8% z^rVzg##6dbCQ-Um`cXzv?xb8txsft~QcCGYxr5S`(up#bGLh1Q(u>lDau=mHg}iVagH8QOYsOHz>y`CnzT=rzoc>cT@hFau4M@lzS=P zrQApP8_NBZzok4td64ow%0raDqnx3fr94b|f$|9D9OY5U-%}o=`~&4Rl-E+ePk9~X zA1RMh{)zGgC8E5Z@+9RAls8iTneryezfhi{{DAUi%D+;cru-Y_EtG$!JVW^p%3CS_ zNqHOPJmu|_|DwEu^52wqQvQeXF3P(p@1gvV@+{>!%8w}TrM!>we#!?ZAEbPU@;v3k zl#ft8O8FS&34wxj?x{ z`7z}ul%G<5M)^7A7nEO8UZngAa;*L81*<09IAzkgHmi44*6eH=_|fSQxhKlp8)fc`GWSQB2cpb_QRbm2b0*3>9AzGfGLJ@?$D+(@qReZf%c`C}hIm$d8W!@5Ho{2JVjWTbGGH;JE?}#$*j56|vr#4!WtyVQ=c3FPqo%u~rhB5Md!wfNqNe+!rU#;?2cxEkqNX!Z)5B5I zBT>_%QPX2l(`%xp*G5gRi<%yfnx2T7ULQ3*88y8jYI6OnnTqU^_7h^wdZ>5sjjZ8sBEaJsj1jo-Pl-N-*B#^a&O(P z%I(#SOUJZ5H@vQiw>^ej@kdsv9RZZ1+b}S6{omy1u?OUcz%-SI?iZpknci zrSmG5ESo)N@wxtZO)K!4R!GCF`7>5ltX@{JY~}1ZE6?>vv`POD6wxt7FCH!5RlBdc zyrHsTU*-Pt<@L3@tE(Ct%2(EIuWhWUYA9b)yMJfx?3oj0G&I!g+`q4Se`EPX9N(ex zy*1m*>y9?=s@*?z;^ZmiIM3<{b(K|n5Tb^1oJP4njdFh)<&04y>?R?aSaBS}O&X$y(nVfCSVpP)M z%#3VX7PIY&N;?AD3zQUdB207_Ti81STq! z3rtd&EHFi3s=zdb8w74txJlqjuS* z9ycj&_P9lHtH%n(Z5}HXw|lHobRMe}cPL)iskp1SsB@RIojND}@7U=q{tqfIp4Q&2 z#rWCTt%};Vr}s-^@0Z5jFAcp9h`kSpy$^`J4~V@Fh`kSpy$^`J4~V@Fh`kSpy$^`J z4~V@Fh`kSpy$^`J4~V@Fh`kSpy$^`J4~V@Fh`kSpy$^`J4~V@Fh`kSpy$^`J4~V@F z2)z#sy$=k%4-CBz480Ewy$=k%4-CBz480Ewy$=k%4-CBz480Ewy$=k%4-CBz480Ew zy$=k%4-CBzY=QUDdrm7g|J*jI`DbL2_z{!T{1S#&zxn62QS;AlSJbv$I_r^nMQnad z_TtPBh%-MR&isHl^8@0{4~R2AAkO@NIP(MI%nyh&KOoHfz%cUz!^{s1Ge0oQ{J=2t z1H;S@3^PA4G(RxR{J=2t1H;S@3^PA4%>2MG^8>@o4-7LuFwFeGF!KY$%nxiq=11=Z z?bQ4WOVZ4rX8w4}j|*UEeqd;RU}%0|XntU5eqd;RU}%0|XntU5eqd;RU}%0|XntS| z%#YrSO4R&|JEZ1M&0k>VU(!L%zqC_o{?z;hX8vWJ)cni4q~=e}Uts26p=aBBY4`~_zI%E4;> z?L$)Yr{*s#^IxbMqULwQQuC+gFD&zCtB0xicZ^8QpPIkG%)fJlnt#{G)cmRW3(WjA zBh~!7N2fo`BId`>IHW(!B0tj;KF^P+gwOK>!{_;d;q&~!@Oge<_&h%_e4ZZ|KF<#f zpXUdL&+`Mr=lOx*^ZdXTJkO8bdq%7I_l`|7f13FVEc5RhtLEQ7J~e-8{sJ?9?RYhR z-Ne-Vsrd`c{0Ang`Rga8=1p&lA1p?e}S3*;1o6gp=qi4Q}Y*?`43N1 z^B=h}HGgXU0yF>78`b>BZcf+v#r#>_e7pe|KHdNfA8!DLk2e5Y@OT4y&$vs?KXXQL>i*RI1?K))_{sx*HAQyz ztknMa?v3=fl-t)+AT_?~lmEpz_=*p)|J*sL{Zsqnn@$SJ{@Ho>N*l5N{CTPUQ~MX1 z{TJXXoW%YM7o_%2?O$m2UxcqL6Z+qHGV*mB)((IpR|3b_D8`gRIZ`_dDKed0M*?-dpZ~x7k zQv0X&FEsma+2rlNbxUgh)c%EL|B5Z%{@W^2`=|CVH2YUpc>8a!OzofAztHSoRq5^T zs#5!>_AfO1S66xa@2F1gpW45`?0;crwYUGSoyF6OOWU0-DLpee+nU9wqyzlWmc?wl zqSB6l_5vmPGZCc%9kivRKqrOH0$mik3UpKGF3>}vOrWPiFM-|)eFXX{^b_c>FhF3S z!XSac3fBn?Q5Y&POyI(Bff3qry}(F?Q39hC(jWA)r`-zS2fea0Tk*wD{jP83aq>7MP+iRbZOJ4FWeR+$3V9q$xLsTB5V%uY?h=@e1k6l> zA3|l`nduQflt-Mcco9FOM4ane@k1NL`5y5QJ8_{$Jk(2E>=6$+5|;u~zo+?~W=sA4 zD*P=DX?~YH7yq(0B)v4h;~__8|J3i3v+4YOMmC+#@ZIqLg0mSPwfOvfd z5U?{Gk|z~1`y9@0P%bV5Z|u?;`>!Ve7_2a?^glw z{VE{7Uj@YXtAO}^6%gOA0>b-MV0ga@4DVNg;r%Kwyk7-|_p8A0eiazruL48A1H<_Y zF#IiSz%ai9!{5RN41Wt7F#IiS!0@-Q0mI+I1`K}-8!-GWY{2lhumM}}TiDQ>`91Y} z>UZRKxS=roo~}m*eoyaLu{4$5uZHs(SR$U!0OI)!AfC?v;`t09p3eZ{`3xYQ&j8~2 z3?QD*0OI)!AfC?v;`t09p3eZ{`3xYQ&j8~23?QD*0OI)!AfC?v;`t09p3eZ{`3xYQ z&j8~23?QD*0OI)!AfC?v!ubp^oX-Hm`3x|e&j7>u3^1I}0K@qVFr3c-!~6~m=QF@? zJ_8KrGr({@0}SUgz;HeT4Cgbza6SVJ=QF@?J_8KrGr$(iXV5#nUrp~<3+;Y2ozJBC zy+GDyE}lNAPtx4oHM??ps~K$uw>sOV?cEL8)+|O+Y|A3nYTC9#eS3itg;IeI3LOPH zDRdU-qR>^Kn?iSi9tvdwJr#Nh^u{kQKhrwfN1(4lKY{)V0|W*t3=$ZuaGk&qg`on& z1TG907@;lK3yf44B`{iHi~zqL9!D@vfM1slm>|Hf4+fMA@at3olLh$ol7OiK{JK5B z4Fdf7F~Cg%_&O%SEdu;{8o+G=___|l9RhbM+$DgyjWo?D7R1~}aF!zGHbgvIAg!3& zNb5XB%xwf0C}M6SxJVIm8^I-tnA-?0Q^edxaD^h~HiD}ZF}D$1qlmeU;5tRjZ3H(c zVs0b2NfC1!!7YlI+Xz-DVs0Z?sff9aV3i`~HiFfPnA;F9?o`CwMp|nWcYEBUxYy%8 z#r+;@73(}6P^|aZpxEf~pyDBqhZT=_JgRui<8j3k9#1Ns@_1TtdMiEV8S?JKgiCRj z;%tv|6z6)Jr#N5n;sV8mzIBn}VvkD{mwH^LxZL9k#g!gcDXtc5m0hE_*0-)xT<>v% z;zo~~6gPX^qPW##h2l1km5SRvRw+7<)rvbj?o`|**eYA2xLfh!9>u-B^FGD>9%~ir zJRVT2_t>D==<%T9A&-X@k9a()c+BH*#S5$4bTR9;+0c-?mzDhi~1fxXWXW;%<+76!-dV_bKjIyjZJP z=Q|%ztoPWU*y!<~;vwBOdsy*^$D@kJJRVm(;qj#6DUYWWr?=4;Wkws#&mLzf&h|J* zajwUCit{}#P+aJ7k>X;(9@!;|OMUAy#pQ|@S17LZt*aDQdt9Tq*5f+G^&U4UZuGcG zakIxQid#KaC~osuskq%^m7?=lt++$5M|P*;E{`>eyFKnv-0Qd9r?}r^tzwpUJ%toPWU z*eKXNdr=wnXzO_Peo5xDU?H;QXoyTg$9fIAnI~8~N)*8j#9``8j^|()QzsFj|I*$hw z>peCoHhMg$c*x^n#Up}c*`tcb6fYiEJmFhUDxUIqT5)=N|Nhlmmzjb+va=Ltdz_;< z*W*0J`5qT2F7&uaaj{@oc8TIr-?~h3xyKcXD+Rk}S1GRcxJGfU@4QZNy~hoT8$E7P z+^l$Ui{e(nvTTLoHjkBx+x@mxiq2!T;tr2H6?b{8QQYlukK$gB`xN(ktW~V@ctEk< zV}oL&$Af|w4hfcJ4=Wz=cvL$dBR*V~J+64d<4MI++WEq1#p$>i#iZ!Mj1sy|4Faeq~2(&G#Yxzp*j#!x!wTsd-CHs0p=4YmU|KsI9GOt_gkk6KdX2 z8~Q5tQ|zxeKyjdAo#G(H!HQQY4pAJcI85x06i0&6&nRW16|Yeoqj;_2b&A(3 zjt%2vMB_DmgW`m6qvYJE`4cr~QWz^alNHA)P9gs>db&w#Pu1LMiZ?4x*CS^r-l93T zD$Z1#r8rwl>lJTPoTI(Y)%5L(cYu%8ikAF0HvVl4eAYXMJXVXQUDV|)Qgz9xtS;wR zSzSsqbz!Nxg1B6X=b zSG7;o&2-JD#y+iXzb@+1QmQUzU0Ge~57nOCyVQ5@k{(HE1@BT`q;B53y;oOiM(@(@ zR8?!5;?0WFb)4>9+D+A^*UIWrzert_HFf1Xr#^P?_J79eavr_)E}ul|U2X zGn}YSo21yicNyC>{a)X!wNrKJFIAWNmDQ!x)s=LY$Y3giuI_+t)up|j;x3=Jt4sa= z?YN6Z9(VPe<&17pEvKrkt}f$D_+4aJ7~WR~(_oW|4vN(OPni;uuBuE^WJaIrH&adV}Hw#T!+% ziJDHm%du0)kCB1n)3{5G)Vt*L?yAPPi{q}0NdM#SvGN~(-}tn5>r`Ei_BjLTqPWXg zislUDyLUOKt5s1~m)0&v5OzG1_WR>T*<1>QW~^XP|ZWE@f%lrPOC+oUhLrx~fZQWp({pW>+uv3O*lPCDOZ?5TIDlg=3=NBSA19&mN3Re8Qkt3@+1@_T<(qqff(=+C`N+PzEK z)un#rs~V-Lck@xfqc0A2?_Sjlb?Lvdx|F)Qd=5XEs~W9(+$EjHU2@X6OV4@lQp3GV zy6A3#e2)yIXX&S3SzYS5x{`i+&fvOcf4jOvx>c8NN>6hJ`gC>qbbD5pT0PyXQD?kf z^Zj0pvB^d5^~&yq>A|mRw2`XISmX=@Cn~1ir8Mtd+Dg@>hR+!& z^|(vgXJoWqW$JZH5UBT#GxZkUB%*{H^BJXm(GqtB#TIb5tMN9K5DNnn)SLeLTXe(dU zC@u0X=i%xyQlIaVcJIou($6UUUi(#z_R={6IqqFkmo{$DGcTGmP(Qs_%haXJS8+^T z##!WDQx_~!mv-jpxQk|FHfKOppFJ>R$gXO`bL!GVWpyd7tS+M%rjAeJF6Y$Ue3#>{ zWJX4N6AK~(qsvDIczUl!j;l-hnavsWbBvl=#6@A#HdA**7j+qb8X3r`)6cK6x{TM= zMX~g5LQ1=Mmm|`9HFDg$^jS0`%SQ(4r{0yEg1ZUY^7$@lU&Z10be)0HG%}D=dA{pW zmszgetEKbZkzLe9n^awLD$mF`-!w8X>i@}nmwsGbiwxxWoiMH5s8OOf-sMP-yLolV zzY=xnwJ0)3zRbux9+6eMN>+H~GZTHyyw~!_b&NG-la~_ ztN}$_U5?FP*EptV)HP#b-On0O*w%6Q+r2wR)nzPwwNGDzq&=VU zau!A2rA|8I#U-hCX*ruUa75+EKxvV>^k1Yd^((1MnR}Pxd%CVM&Z0RP?G!~`sUIV+ ztvlje)J-G9wY~5zJyceg($u@;_;rnRv3Dt{ypBVu&l*U(x}+!edQL{|X?%nIUsLQ9A#@)PkDJ@!qq-UR#QQy@ioq3m|Q}0sVQ)Hly zt4q4Fy6Px7qnq@5m_`OhI9(%XWp(-PrRp+bSC_tBUF}J)U>0xPyXl4 z{`b7gImow7FkK~M6qV;>)X!hls6AC(TluLl%B8Dhw34bzJFYHiS69*!8S;M3RL;CR zR@LP*^OXkrbalzUl64%5yR_imC4G5hpq9@WC{MkcSC^XUN&}^pSIMM)!73TY_?&_M zX6TAS(JC4J&DPR-{d{lJ&nJDOffnu{|7mrJxBfiXW@jE5#`oI0v^q$8^Em@K>5Po? zNUv&q-j}P(ajq`qt}glMRgLyibvb{J45WP>hjiC9NOJwz59#STepi?NZdDb19f$sA zYgF+$19eh$8S&-nnul?(&469q8*=K>lFu1P573_7yYxIrbJDm=Z+=xXbvf4Wg~_=* z?vi(nwv?{paQ?0?ZFpp$zQ2t_{zN^0pYKxYE4)(LgLkPtL;EiBF0FfHAno4esJYtf z?TVSY9uFjDX%xo0*>lSYsxB>gWI!#C45VFM(y4dpv!@vuM`hlnAAc%LN#!{MO8Xil zM@`W0tKUtKldhjLmMPRp)*xvm^)5%I>T;xem->~x%dwZMn?+vJIrZ+1sxCdax};rQ zdK;*pR^_W2eY$t4UvxJib)=tB`n?>jI(cN^=*p3S_R`3}=>D(bZY6cmH6M4gtJ=g| zWWY^DYmnquR+sa2bxHRecd6y-(zdHh+9LzHdSoC!^)4+Gy{9MVpRAJ6O6pzOa_^#6 z`s|0C*%~)I?o!|HCP){}89X}RV@rG7o%D>=-Vkx*VIu-7GR#Dvb=2pRu}(uD9wk*3lXtdgop0 zdt{)WR9((Lvy0qtUm!m!I zlCFGJV~mxrYLt#pB`?3K(VMGFt;*h|rmKq}NXqIdpY z4eqNrwB_%Z(x0!9QKzzZsd<}zGp;1=Cf>DA`WYz97*hW-=I&xIM^v6SPZErRd=@_ErKYd(l0R3!i#~@;2tvX}_{f z?N(h{tsEWbKYhQS@%F4P+IV!Jjnu#7$T$Vlw;C8r(K{o>>QdWRa_Gkom zyn%G8E;&U{f3j$Rs;oBfd?H(Gn9)UDT1wR=r}F)psY{)r=fdP)K5rneci!dv{i;UV zuWGcA-mjru{*I!Ox*X|omt%a5m-iUd~$eYI9+0Vecj5PHwJ@@Qg zjw@1^{B%Y}PO2`76wMi^nZFxmB<@{wO7A8p^>?HxovC-{Id1&iKkFz-^wXJphA#|TaVlQmv){hbEtd1N5p-^U@} z|7{9szZ1@Tm--$Vs8i%!)73QgtJ-bds!L1h-30wqjttcAt-7>SSzStfC5LqSY>gaO zm$YBiM3IW>qE@OdqxE$L(zEn4a&>9fBLl~}y5v_@*L>{i&QWz~yD09`QcvpgU3KsF zPF-phMF#TIRUC3WGH`}PGcxjBU8x_{CD$VZBP@yx)baT)X^#w?wMPa<(9^wI<*V9U zRhPE=>b1h33zIWIqme&bBWIA-sr+1+^YE)0^^5;KE47NA3v-04%UPv&!j#H=kf5(K zkoFaH+Uz=KKqr68lp21o#*syH2I{BtU2t#s@$yK`ZO|7C%qGI9IW>1m9B5oRY z@93f~uIaB=6@L$swkxkQQ0kF^w5v-y#WON$`Mnyo{9cW7^Em@`(zr`aiE4rq75z?_ z9G{U%x`((+EB-DS{kgj2d)y`8BLl5>H)rtqf?w0%Pv1@GSo-K_J?_$vt4lxbUGhCL z(5J8Bkal&c<4@f=_VUO;-O<`x@6@FYzgHve>e7#^OWNbEw4cOX`tvzMrO3eX{!Rn+ zi=JGP?{AEd?s`?TxL|j}QQa=S<;ZjehjXe^J&Nvv8JR1~vF=;a-T9XCEB|*XjHSr8 zwAM8$Ft*BX>2XZzTUxIi6=*HJ3wF2KlXl)FBP#S>Sw`Ryfwa%WNDtB2QCV5)Tz-#6 zUeR2Ee2)m6WAEm<9MyH6OKwrTMIDa_)Ny4=`!fmBMG>K^vKC$QXzJ=rtXt3W4f1F} ze?^`p-<75B)U%Xct}Lp#vK-;Dmb$Ji>GXL7M|m_L?a_cSc8#^V2z+i!TRuPFY&;rJCyfSt+C{rKQS|=%*P{VxkF$AY$@gbxq}{WO#g!%9_2~|^eU*_K)Af4mo@E^A6^vesJj=1EXQk!G zH16wMz-L>2SJs_p>D6~`qFvwphOws6fKpeMQlHP#f=2_XBT;9Ro>%2)z|pQOM;G1c zlJCl*gGU37%|`=x8fU5P%F;@Dr%Qdmf^kNvXDKbdf@L!;;()&yk}1oPey2dYMbUuK zq;ms~O4n`~y(>#Qm%k@Q-e~<6yJyKsJxf0xXX(N36lmL{0qIG)!r{u2llLs#qXFqO z8jzDJOO8ha(ylC{@ihd-difec76EKl(B0gCw)`H9w9gGl`#l)xqI)ouaAm3Eo|XJA zo@Jcr6^x^Ob%s*+ENNd^B<-s+q*G<3g@QX=M&ioSherbx^jR1AzK5nMOY1&2ptO=_ z(Uxdnp0&R-&(ex}mT{z>rCpyJP&17Nj3a$VffoGVLy*qmEUfa~Eu|g}Xw}ya=-=Zk z`5q0(_kV4|vH5#2%F?+3BlndVYP)C2_gNRM_b$##3%NLJsuCIOKULOd2s_ySwIn0Uqc}lvn&K?Q>54-@>E|wG6BX}P9Huy4ajs$? z#VLxD6=x_;QoKQNfZ{!hqZMZ>J_l6q-m83+Vx3}N#W9Ng6`!kkpW<-E>l8;S4pO{Y zv7h1y#i5G#gWA(I$_FYAR=h)Tp5pb2^@_JC&R4ujae?Bsinl8+R9qDL$mg**^c7qZ z`o*4>hW^5rg#m)g!$83mp-ymR7$mqV3>I7+t`b}mh6t_=Lj~7`VS?+!)q)$saKVjX zgy5#IOmK4;DYzw!65JX_$DX!@YlLqPV+41EYXx_PMS{D+b%MLY^@4kJqz#ID6&n@z zDehMcdVOsQ!Met;}Rq(+uQ}CfMOYl^f9eX+*>V-cXZWBBc<_Mk* za|IvKk)Bh0p5pTrU!eHH@FJ=6;_wo|mxf0LUlv|2_=@mK!B>S>3%(}2R`7M<^|7Zn zgf|L*Q+TuBTf$of-xl63_>S;S!FPps3%)13SMYt|{em9|9~68nd`R%a;Uj_{4IdNy zc=&|iC&Q-%KOGJUekOcY@N?nwv8OMDFAD!s__E-a!8;^i!rvU; zA^67dHo@12w+OyAyh-rY;SGYX42^;>4;uv^4SNM&5?&|xqHt93XToa)e>S{I@aMwq zf?o-*5d3O*nc&Zdm&TrcA-q`lFNPNi{!(~>;4g>g3;s%Yp5Xa#PVn(?MDS~2mEf<2 z1A@O6HVFQD*e3WJ;Ss^FhdTtn5zY$!W@r-pW@r%nt#Bsx^xNTK;ok~-1iu{)3w|e@ z7W|#CRPcAhDZ$?h4+;K$2!ekQ9u#~coD_UA+%I?`>=yjPa6<5pLWkgYL%ZM~hc>}K z3G-r4-wXE%|I_eX!9NSz1-~D53I2IlF8CK=zTjVm^@4vDRtWxe*erN4>=gW)@EpOv z4eJE|E-V)O`><8;AHo*FAA|=4JHwjT(^FxA@JnI8;19z+g8vwn2>vLv3jR}AE%?u2 zhv2`2X2E|A_X_?v>=XRAuu1UWLyO>lgtdbI8CDAZSGem9wXdy}tHiVQ@7i!Ty@fl& z@n-pZN8dg*O_cYMKYg3}Ht}~q`O{zi40vtrCkB3epzT1uLvl~mO&?TSQ*)7=8MPP2 zY-~P$@MzP5QyonU4>h)JZ0cCu5}J-)yjF@YP6g_Ln}HjEJAj*jS-`V^dx7D=9AGps z8@L6y4VVc`0d53t2c`k{0k;Bo0W!3ji+zD>fbqZ(pbsz-7zbPrTnDi4`o;dhP@o@h zHNdv(7di8bb-*BC7%&mI8@LC!3K#?258Np*W5~t1q@N8u2bcsr7nlr;044x4fC0dC zU=;9Q0&VjEj%Zr|aGq`S*M?ec^8xy6qmMSuy^SHYX>ao+b&)C;Irid0U=gqwSOP2s zmI2Fw6~Ibh6|fpu1FQws0qcPcz(!ybuo>6_Yz4Lf+kqXxPC)jouerDz*aI{Gdx1t^ zAFv+?Kof8PI0zg9nt{W>5#T6r3^)$70Ik3SKpW5wbO0xSlfZ+(L%=EEH1IHR1~>~m z0-OV$2Ydl|KJag*J_5W7_%QHl;6uP`fX9H>0v`lk2Ydi{J@9_u4Z!<=Hv;bk-UPe{cr)-8;N8Gm zfp-CK1KtU|9e4-uMc_-omw}%FeiryS;48pafu9F{0r*AWmw;aeeg!xWJPv#f_*LN7 zfL{lG1Nb`d4d6F{Zvwvs{5J3{;M>4=fZqXr7x+Ek_klkEo&cT%E&zWB{1Nb7;E#bn z0lo+PDez~&_klkL{sQ<*;IDwc1}*}B1N<%Ucfj8R{{Z{|=meeuE&)FT{t@^Q@K3-$ z1OEd2EAV6B-++Gy{sZ_=fzyMRZ(KX;uDhOf_pG}w4BT|0?MQR`p@ZMtcE`KkHRK2P zZ#uC->darTaM9u=OP4KQv2xYwHEY*x*tlu)mhC$l_w5f&2M!);K78cpvEwbRZS5T= zPM$h_{sre>dj8et-+cZZ=ihn${pUY?{!{0_bp9*nzxw#QAAir|pLzVVUwil0-ZNw1 zg(3STe784sG#qa{)^wrnXyfsNCmIhnT^K=rLq|(POIv7aYdF@}dSRFsHXI|?Ul`MI zs+FV;O~*q^+idy?O&zn__r;O4wzceQYHRB&XMADgrp+5xF5kGUVb%OK zOB+_NU9@=Bg^8k614=dA-QdLwm(AbMuxV|>+6{{qZ@6$>K@C0jqcwa)H9tO6f2ieH zQ+<15`?1F3_3PSN4ma)ZXs_SUvahA1d4GHT>XzdNTNW*tHNU;R`QY(mO~*UxXUlL; z)*o%&SKoT7<50`-+h@<2TQ8^GG^@37{}EBFy#$E#MVsAv>c@5W zO7g_fru*7P*4EU>|7+U6E#HIAzO}Wr->q5FIc(m%+A)2e=r^FVW?t=}0Z-Hoesq3k zUy|R`WPg&K18N3cCHVuD)EOD1WU!H|lngO4RLL+SS1TEAWQ3BDMn)+aZR8pyV~kv@ zrYf0c+-oJzRdSz^`<2YA zH8#Ii=>nm2wb1AyE4{SX=#p4@snKPz@^YgqV(Chwt77SDqibU6TBGYCU2k+lth~|a zrbss%-4f|mqub)K+l}ssbf?i>k?uCSC(;I^dn0W$x-ZiGMnj}cMh_T$>Y&j>wKYSB zJuzfx@&B(HV)>WOS?#`R{LV#vYWfc>y|3;ny{|I-^m~7CA2a`wex>>6cb0*lYkSoM z{-yoQ{L2QE<}b}})2pg6|MCH5{uOnl`AhRxnfX`NnfX@@F3n$>zsk(Nda#*)&5+Xk zrTMGO{A-7p`PU6A&0m_o%FMss*7!R&3@^=Jn!n1-zj3%l{!JrG^OxqYGV^a9Y3AQD zx-@@j{wg#7*3oAEZDUIFm*%fB^KTzx=HGE$Y5vmuRc8L3*O~cujV;Yzn!n1-zk95i zf6w^R{H6J;%=``G&HQ^Ol;$tZUuEWRoM7hPH?cH-Y5wXm|5N)Xn)$=z()^|QtIPbI zO_R<12W~3OUz)$l%zyAEGykD!rTI(qSDE>nr+I6}rTMGO{141F^S9kr(_gpksmr$Qsmr$QsmpKM zBP83lM@Y79kC1HJ9wFJbJwmc=dxT`$_6W(g?Gch~+an~~wns>|ZI6&_+a4j=wmm|! zZF_`d+x7^_w(Sv;ZQCOx+qOqYwsnb+Y}+0o*|t4GvTb{WWZU)#$+qngl5N`~B-^$} zNVaW{kZju?A=$P)Lh{@82+eQXBQ(ElkI?+KJwo%__6W^y+aol;ZI96Wwmm}gTbBsU zZ`&g@zip4u{I)$p^V{|a&2QTyG{0?+(EPSNLi5}92+eQXBQ(ElkI?+KJwmTw+a9Ui zew&%UV{RGw%gA44k^jV8GylmuO7oZIuQKyLc!!z)p*u_Sm*%fB^PjrY%zyf>a?Y>j zm#JVm=bu+AGW_(<`5(T^%zx&dGV+&^zse&2*?Y|VkK9|Dzchc9ng86qX8w8im*y|c zUuEW>f4`Z3!Tj2C(qB&cGxt};q<^9O0PjTiS_X^Fw;wd!$il@;0 zE1p90uXqa0zv3x0|B9#3{41V9^RIXc&A;L)H2;dH(EKZ&La*QzPpQ3Dev(q{ziw@5 z|I+@|X8-jv%~ShtSYJ+l%E?d8{#7yg*(g6btoGluv9y0_|7x@UX8DP2wf~mQrTt6$ zSDXE}%1_Fx{kLr`=l35B3 zrQi8yZe@I*-&wDIDC7I!N9T0PO95Q)|6jh%T*i0(?8^9F#`kjmP82KW?{hlkB}b(A zY53PFWqdE=yG&WD=HKR)elPuA`n|Kix}aQ-Eax-apqBHQa{awrfB%2GK2!dHR&M=W z{*`I=yh%uQzbYhKj}($UZxWI{ZxWI{ZxWI{ZxWI{ZxWI{ZxWI{ZxWJ!-Xt{tyh&*O zd6Urm^CqGB=S@QM&zpqipEn83KW`G6Uw;>x-(5jy{(e5LJ??UtOU1&bO3(d!Oq51eOG#}rE z=Ht82e0&$0kMBbB@m**>z6;IAccJuf4M`$?s{hKS_C+rvCuRAE>0x$RH(yja;Q@X6 zh8ek9$#5ehl#DboO37#=*C-hy+c!VjxAR&h*BQB9$yg)fl#DlWgOUkGZd5YS$Rs6` zl{_^?$xT)=Rmn6XH!GQLWQG!cA6`aqs}g=MTgWUW{C=>IdL{CEs*pKK_@U%!1a z<1)I?=pveb&;+&x*^hyMmI&e+31!? zw;J6R>2{+#BHd|pSERd*?uoR)=-x;hjqZzdztIqBlhFf_9yEF=(q^NFjb1up^k^(S zX7qTZEk;`-eZXj2r0qsKB0XXBWTX!oeMo8F&QnHDNBXeQGm)M(`bebbjLz#DKVJ>0 zoeLseXmpX%+RnvBmniMixzy;gNS7O3A++w%k)11zu2MSU(rTk?V(D6=>mprmbVH;Y zjc&4IJ2xBM5=*xl-4^L~qdQ{dokn-X(%nY)#L@<%dt+&%(S4EbHyR>sGI}5$d(h~i zNSlowj`WDpqw&~dMvoi4)MB(XmOfy#Ez)+Q9g&_edNR@njXtDwROcz9rz3sX=$S~* z8hs?vb4KU&<3kzMIlrI9=SUYCT@>kJqe~)PYIIqo%Z;vxbfwW%k*+qnCepP=*BQOE z-spx{y3y#SNH-hZ66sc>+ald=bVsB+jqZwcx6wV3HW=L-X`|77k?uDdB5g8yAku?I z4@KH+^l+p{j2?~jn9<{rwis=V^Z}!7MlZD+?TDo(jGm12L8A{vddleONFO$OCepJ; zABpsw(Ruyj=WFwz1(7Z^x+v1cMwdjo)abHEmm6IX=}M!kB3*5CO{8m$u8VZN(G8Jq zG`h*?rOigS#L}%sw?(?$=#EHt8r>D?Zlil5Z7{kw(nh2El#cA&Z!|>OWb{C!2aO(z z$2J>19O)6GMiBNLLzN73pfDYm|=aTx)b) zEM0GOLoD5BbW<$dY;;ScTa9kB%9pkq-4T!7X>?btyxZuWSlVE8Z={Vz_r=QljfPm- zWb{C!2aO(zm79$oj-^M89*y*v(c`gli_z9dA28Y$E4Lf%i1dWflaW4X^r1*k89l9Z zO5%lP%LdWdN|S}MvunI$BZ72r7cEVBYnVVTcqtqJL0h?jGm084;pH@?qzYOZS=URzW1->V^!`Tzg` literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a39a5a5e6e40ebb17dac2b3c91f09c2a25ff8a57 GIT binary patch literal 7024 zcmd^>OKcm*8OLXr50MlpQlunXvMkG%Y>BchMzNDRsuEk3I=g>pZQ_x<_I)#LV3#jM|zA1sBt2eT2qCEJR+s2z|J8ZRb^_sUUE*rR8F)*U~JTj%7scbUNq+ti=VxbV8aoU!J!$ z&GF5g`>vDFiaE>H@(5}z544<^HfI{|X!*)w*+&0>X;_B+AwCAnD9$-y&F!XX7OhtQ z>Q_sdw<;wgW96(;uAIS}Uo`Tzm3h5#p<>VHt<2deb*?z zn<>s;$Sf?|Z&k|2M#rAbSo5}VbRn02J6ABQ%)XR<=igF4{vl~ z+3dgrR6DoM_^9`C2AZrPWM8g|`=QvCmRe}{C!wDE{R30oFJv{a5>Xqun=YMbK9$WAk4C8Jj2{Pt7R2Nc|s6GNzPe z#co6mVxzG+H9K#OZ$u&@+f;S6Jox0^x{soEhc>@FG6NLhRmAd&QpGWauLZfAR$#uv z-_9i!SHUWxm)l6c-ADf1dIW*>k3{PF>Gh67wT?sg5<_c&dQc=fuiNjf zeCiXi#AdQKxB5_pH z-dW5Q-4=O#%45X|;Y`JU1A7^hW}7CR8|ER(D3Y82ZJ3r5rXpvWxn*~fkIA;VVkKvr zFA&H0uR-1@7Bwxcn0rtwck5H`VMTk9NtsR!MPy%I7LQbapz~4G7ua2oh+tb?RRX;? z^PdS64}D$*Uf69)-W;H&WTPor54Hw+Zl1sO-0k$;fn&A3$Lj(WAN4+@qTbUM=(!d6 zLZFaSByT^R?r?(k@&f*}o3!}uYsUDHa{phLv~4>9qr6zM-1pr_6~6PQ&&b2t@lDQ7 zQ8tTg>J7?f=#pr5#ympW5y}ozCf|HN5uLSiHogi?xa;zgc@}kau2?hpPU=2IQN9=# z%D#V!;lGM~oAJbIbTinp>f87lSv0m9t_SybsT=;5x{u0m)GgcE-7?kXmb>@3<=%d` zJaEu0hel+%iSYr9-^6%XV!SLdUX~ayON^H##>*1psl;A4VMQiOc1;A4W12|gzHBqhk#PQC>B znBZfAj|n~|_?X~hf=^PCd@1rJ$;SjA6MRhYF~P?K9}|3%I>^^az7Fy+!N&w26MRhY zF~P?KpQJAG?IK?n`Iz8if{zJ4Cis}(V}ei8Zt``LZ#Vgv;A4W12|gzHnBZfAPf`#0 z_K>fKd`$2$!N&w26MRhYF~KLPmwbE4*GoPo_?X~hf{zJ4Cis}(lhjAPedOyS9}|2` z@G-&11RoQ8Oz=tSC*OYZ^^=bYJ|_5>;A4W12|gzHBpo2%0QnA(j|n~|_?X~hf{zJ4 zCio-`lJ6k-2Fb?+9}|2`@G-&11RoQ8k`9q?h;A4VM(g^tulW&B4Oz<(m#{?e}d`$2$!6)em`HqtB2>F=cV}g$f zJ|_5>;A4VM(kS^d;A4W12|h{3$@d)jj+2iGJ|_5>;A4W12|gzHBt1{QZ;|hL z@-e~31RoQ8Oz<(m#{{3G6Xbh=d?(1q1RoQ8Oz<(m#{?e}e3Hh=_igfxlaC2LCis}( zV}g$fJ|_4iO_1+J@=cJB2|gzHnBZfAj|n~|_$0kVzLVs8iF{1(F~P?K9}|2`@G-$B z>6AjkNrjAHC>soEgQ0CO#0`eJ!H_q2`0%Ha?}}u7T8VV1cSYj!-i*?7{l!NDh5W<$ E9{{3brT_o{ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/macromanprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/macromanprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6d095b8c41062b1052415a26529dd818695ae8e GIT binary patch literal 7204 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 GZ2k|9drboX literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d75294b6958a1b0a6320fc33220daa18ae4cce3e GIT binary patch literal 1610 zcmZWpO>Y}T7@qNZ{jnc$lhmo^sjqyFZ(z z26%h^Q^s!=0Qg-b!=|R-=sOM`0}pr*g8x9!&AYuJuR`0&jw|?3_%7g0?*h2 zo{5w_C8^Jz*_f!7{)9ZMLZ;RIZf%uBZA5rBwXohIO%yMZXwV<&V$P=<0LGgfu4L5UQV_^g#>Crztd_^*#wVxiJR~TxF>|> z4T5-BG}uch5a&~H%oN8MeT*^3n9>+CImS$nG3C=tx3~$M(fc+*@3rBK<9=8>1RJ25 zX;dLO!w0J-Q|X|O2rD>_ANsN55K$&EL=eX$LN6ljh6o*qkdBDc67fQo?@JF-rV>O^ zTvb?>BHWRiv!dg;xJ9YsP_gX%`G>E*wAP~lS+qriR%mgD^pG1<>u%JJV&A3KN)&dZ z59*h0Q0jNX0EMwN`v8RxEbOS=u*Gs-sgU%R^RVi4xi>l#B%wk z;Gx(G+Ai&i?;)vhw!cBn@*=*doU+X9@Zj(AQM$&%%ZFgUVD99f6)I0Yd0MFaQkeV0 znAyF(pM-O}EBi)ex3O2ph@pju^Pe z!^n8h_CKjSgCYO` literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f103edb041f567b36e9ef2b0695953cb61938b32 GIT binary patch literal 38667 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/resultdict.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/resultdict.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ccf5a7cf1a383707cb9ba9c931ba31ab69963bb6 GIT binary patch literal 654 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5afdbbf13d576593da7e756a9ecfe607e1fad1bf GIT binary patch literal 2379 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7b2c28b622a9c661f217175cd47ce8c6f1c87d1 GIT binary patch literal 12291 zcmb_CTW}lKbqf#xK>#Gchxh<^(K;Z6U6O$CVD2s? zlcp23^-Sedsp%PKEN3!fd8(u9+&?oHGO>uI?x-jHYvHkueSbDrVcuQQzIp;`NqVAF;)gUQI@C@I(m5r+AT%h$+HydLa@I3qm)0b1fQKW1|8EUnkK-m~1#`9~)-V z0#De~3cI`wy1dK^YpHac!>(S3V?3M|*HUDhoePue?B!^D6)>=!K!6wx$JzB1 z5m_^+e)WCn&zrU(}nA}PnOaGB-yk?T3f7GY znAD0%__#%0JWo>T^~)r6jVI7rnrY266seAx{uUk-iV*DVh-1A-N_wG3M8b6P8DYZZU2 z4&b1#A*ZjJYvCGSw*+grCcc)hf!rw$e zWFakq=q)cF0RZXIBrBcdcoL8Dn$#K6)vF07fUTsH5#&{hEu7>)GJvT;N<l0b zQNFq8$_h{Tq{v2qhXr0_*D3!BAe)qs6uSzIVW5Ts7D58D3IarhPlCvyB;g!UQNZq% zCi6Hp^pB?nD{R>x-|G3|Wx)oe&ibuy zy&BLP-+Ot}^*?`mj8({ELp)(-)zg<;p?{AxI|q1p&9rb%)>QUtj4gr_XlpEgmg!!cV-NbiF) z7it;p!Z52?%baB(ZFm|pocRYu&}v}hu%@5f46Wfl(YjmhstzOE$MME1s%+L*EjoBzYZXJ5d41FG>#>Cq6a`vrjDHF571g!JF z)O#Kjsdil&RYz6~RXgqjJ!+mJY@kA|4DmuFlo9xlCh)2|1Y3s(f!^TBgAj1L@GMa+ zTq?<{C85=n7~;ZWnBc;wj>}iCOoz@aPR`7zu22-ux{yE*J4Xmi1(Bj74G3BsPp!g{ zy{eXga+!rPoPhRFh_XgVwP7!`l!ey9Lg;2RsR=o0#GzK8ffw;e7WnuIMMpaX+;C73 zpTE|(26~}S2n%pB_Q9zZ1G`1&yOO$=5~C5JZ!VQwO--HcIU@+s)no!xUtjMHK6#@r z9=+DLPVJ1r-u{6;Au94c>*2_CIHZNX_2_zE2*9`$>BEHsZPJ(4Z6i&!q)6}j27U$_ zFQL$;_lWxL)%!T0Rk#h=E#|<%eAP5%x^3CFv&!fxd^ad3NATVFxVAy@HY&9ZPl_#V zj@y42^E3z8$NzkE$Ex>JF*8<0JcCH9*{P zDot(JQ?K|&@!g`>U2i(xaD3NA{6I{^kSnNQnMxtD?>iu8F?i>vMdpENK42m}(DA@T zPQpvTfh-w@Ty^F!!Di#p<;dxN6^Ei!++rTPD`ht;x!D~%`+L(x#sC*kHV7{QcKji_ zN)PeXX7f?X4gq`{hv(4ND!E&C?5%$=ZJmW^a~~E3{+pe=sad~8yPjdNWSC{1K+IMv;*wwu2Y^GhTdYUCB$LA;g88Ivp!< zfzl|#PzF^ShEOMe_*=0S5c-@sLCEsciaBRiTftey!n1P4IV0@_N51%sktI1J9R){@ z7ap1OEd^9hPHzDvlk=rcaBvH(FXzfty|A-}37K6rt`wclKeba>qZxRJdA?y9L!&G& zT?e5ge+eU-@;aazL!r-0S-vCHS(7)`ng<3|_zuJDP6mH(+FpQ>zN6R+(#FJ4$~dTSTo#lYVHyD0HM~Dm*HNN-{5r zC-jEcfL_YJ>26(r1~xRy@=|uUa`2+fmX|2r)6c*=ng#YFsRlS^j>pTHFWpICCT(LiNZ5N+`xA58)_?p;r*7g~=&t|_= zFXe0V{5|*`bVvb7$6eSqk#V47N}nnF7Ty=n>otJ*s$R~%^?g`HciX^U7*@QXkBeQ@ zOWF4(oQb5ZFdebEESz2v7xa?Or#dYruBDFS>3Fq(Myaef>DhIJH+u<*L3q7?RUb*`5WP+XduhDV7lF&iI(D2 zXI3DdPYrVni!LuCS~SV^U}$8GM-v~A-U!FjydH>!_$mZdmY0ng!lFo`*U}=7?tsAa zY;=X<$e8mq4jWAZ0-OM+0+hz!!m;B-ia^8{0yf<&+=VUClsS%N{#(VoSb z^S{)XDBAstvEL zB7$l^yD%35dPk`vrMfaz2+7S(=hn_nt2XR|R{B0Wt@&1ltEE<4m^ptTboQmC>Cn{l z()8rg!WGpf!2FRlwZ!OCUDJz`1^{)<)Dmo>K|IYjL$h49Qpb}xv0S1doS8qbT9EZr z`?-ay^HZUjCDo+|CNPkvmO*SUl#N4bP9Iu+TF?${H|qHj41W@IKGfp*h569r#Y?I! zyuJ>>Hd2mVrI?|)tCqhwGk^H2sE-*aqP`c|IEt}Ox-AAPU`w0D0 z@UFmnY+ziTH^;s+24391xoaT*g}d%<=lz*|ckiPb_FnB?P1o%U2R6o2_2##}^R373 z8rhAZp!>rgoPPiGzIzb5TDp~1zrwacz^=Js+wwgO*j0Gta-UT0yT2m0k4f!gKOUAR z=B0`G9sk09`DGZ?J@og3J4;`anqI^aN_m|IzvCa>FCROwGc98oq$So2QkQ}BcLKV< z@JX(Zfv4??LFkiLq@MHpRTqAd9lj64 zy{-2K?hW4U0YBaiGypbt+pfFqQQL{XK7D&?-^D(r-nry#mYsgd>Hnxf4xE+(r+1yF zDRFI*yG?d?O76}NYxmp(KZ?u4m!#oKyY5SmS)a@fN$il!o{`uyN^85^IxMvg%dOK= z>-1jh`Rq2njoaARqA!K(&{80ZUFVQeStC~-->p3U==j7ZQ}U@<>D27r@wsiwzNeE? z(jmD!WOujZ?*8!Wd+y;Mcgy1o()hxzdx26iDzT$7J1w!(l#+3&bzE+pky>Z=S}!R` zss@0hW|+&M>U$*zrHtpY>A=gBRmi1HQfbruF70FXN{1<7ox7Etj{>8gRLNsA(%8&i z;Nni_C8W*&80l`4D%EZ^Q*?s@QdK~%Iw4h^_)+s-|JnVjNd@=PFM0g3 zr(5!LfAl90zp?K*mEZb}y`C5LJzr70O|rL7^7ehk*h{_JHl?!eor$+5?!K~D+4<3| z+bLlzF!Qsj zx`T2+cThn&!7sV}`jYOuhn4m&(A!G`csL={gcy{C^p%K~(#9a!-TsR}bVFF7zIRzt)C`=aZ^RKC>uT!eU3^*H& z%7EFUCBTVc)#u|Z5SO7if6W!YX5#Fxnc)qWXt4qj8n85*!P2yBS-?`XZCPT5XRA{H z9(JaPA=EAN^(j!eraKK=Ca_N}uNB>C0h_Og`~zNn#R^155T1OpRa7W~@tSqZddqx~ z`7_g&HCCUUQ7;)Y+hF|jC@bEw#qf41FLBveP$y7l_<~t5(g5vjwyu{7UEFQiwyb)@ z4k#}ey&%2NULaOc5TErGlVFBle4hL5hvu|y6~jt^SC*gh`39&c#KFh&$7Od{#1Jn{ zg`yYNy|9aq0c!!N;v8r`-IAU$XMsp1=X{vqaO#Z}=F1O&FYQ})iPZf$89j)v9hG3x9y%TsZ>-Xj0o-JoB zRj46>%lf@}jz6)qS)NQ$7Yba$2oL|paV-RRn<1d47&47n3uM~m$fjNY*Mj;6o8YYI z4kNn;zE=Y7brR_WI-EQ>N^x%Um|tMw$_V*K=des1esX1a{RUQYM$v& zu3$g18-W-An9OrC^D}c-=R!*tu1qgpSeTt6FVVsA)J>j{83e?88Wn#9388&GcjmHM z1h%_&-|m>5K7VHNC2S==iV(V{;in;R6mRge{0%-%`Y6Jw>9beQ6AbB)Vr(eR+~fpG zw40rBt)Al&sK*lFS2DjFK!xrDRMw!mvg2w3V+b#i;8q%~ez*gA^U@oaWLKl)YTRk+ z{}_XG-G``u13YzvEyn{>Kbof z%rTm5rJdWhpVc%fRSjSc$-XhkH@4@S*zvR|mA;*hQ+r^hb-%Dv6;N6_-nw{D3h+{?SdFW$N9+{Iy=60Lsl;(ikJS;U2Z@ZM5ww;>39Zw(Rioaj>k4XNJJ^$E_r&Vd} zkQ;}k#^Jrj(QOCNTGcLBo{%a}eC*waX=>`_ny%fNE~VqR+%Y0`jL01mQpbeS(EG9L zX_2|pd)Eb~IT-bo-Mf|DN?Ql|Lvq`&)HbZt`FHD%e^?>c4oI~Fd$mI!$6r5rO3+_%Lm~K$CiKm8IG*`0rZ^M=4H^oOWrQ;mUM-HK66?`8PcB0X!8jp#8MAAc zahOrJy6DX8tZKQIO2x?tG^%!x#C4vC8){KBDH4nzlNT^U%b3_P!w9){LyI?ys+Im` zM=oG1y3+)0Mv{Q+0Ykq+^R)5ai(rNULtzBu>UHLcxy))mU}3#>EXS0(w!0PYdEfQk zo8PM&+_6+Em5tj2?~K1SemA~XdHmL_Qrmjh{GQ`o$GxxY)egXj$JIXN=u_i=QeIMN zyLAbi_6imODrsI(uT<25$UJe|D{Qwe9@H`&C*}50seM%OcReXSWfQSKD0?yV{JdHlxdbuj*bxY8k?Q zztQi1YAd(aAGDt_TaVwpdhg_ift|KNsd4Cl!In>|p3?WhIm?{MTED&dUktvrHN}+- zG?R5))r**+q9xyiOtpy{>u|hkJ7&RVx=$6E2n^MTn!eSPacQO_(}sJjTKQx;A<&<< zD4%5FJMeE<1`2~Usw+1~pu`wZoFO$9 zManZH+vI8&esFM-;f>`Wf#tgZQT9p30vA8zK|x*?L4p9A7tdM_iOB|05CnmeAmCZI zxWhfAs)sZD5hZIc7vKVEK8TBn_%4k!T615R3rW`m<-1a1R*-<;!jc$|UQmkp*dpF<%JfIuLn*m8Dk6dd#qrL$Dtn_V*^OEtx$L9 znf7axze#g{Fg-CaJT$0(&JYh@%E>~So)cv@91F=ZD~k(@u_PNyB<5u{l91R$IJ_iD zLOd+UY$C#@1S!GJCKW;MWkYeE72^>>l8KhvbAmD_NGxP*M2rb6C?y@Tpau#9Dn?O`eM?|0&fIF95x>pw+W z&V_CXY>15}501aTv0-#ckY$0-s~H$R zKW@DEEqHMOz7>8Y=uC+T#;e^5Nur>Lf*c@&+pU91j@JF;;yMxxyUUR~Ypu#alncejEmcXP{PDR5P^^UR4p#)K7TsB3`voTc&Qb8|C~n|E5XG zZ<~_vRd?|no-(9dl1=!%noV9>6eP{TabjFlI8JjcCS*|oEYVzODKZyQxP=6-d2*&> z5F~tKt#WL9di=ul>qF*FDg z-4Rie6>csRi*PbHQz8obxy6tual8O9rU*PobRSI+G}lZ7`P?G zZv|uGY;ZBD%q8MOfq}swd_tjbF%+H;MFlyyC@uy$5aSb4Fbv2HqYskrGBk)zCrE+C zq~wJWq>dFKO~9BHKk(#_A^XK$>MMtPwL*0?ZabPYjZG`e19#?l+xmr#jwcsam~BT> zhV59FH{O488*8m!9qUXV>s62Sf?$k6gTFlT{B!n}m9q?nzAPlS-)*`Yo~ubR+`E51{ZEp!G%}Tmuc00aOy5 z1gN}Wg0Idkk+6V-v3tr36zYK4x?uuvUZkKGCTa8Ar&12^x;_LOqZG@P5Jq&`z%bD4 zC^s&&0HpSU-zYU8OFp5Et<-v6Cfllq~{+-p(qN&`^aHwVW1fQ`N8eF`LIP!oRh z{o^*>H3F0II(PkuUOr%==6{!h?XvWUMu$f`DoH$zK(|cebb^GvgbmgCF`{AdKn-LI ze*_Iz4%A>h;ur!%gSmgpkCkVRsKHG%m`hDwgNwRGJge?36@7KVQ9ZQ~bkSTnb0(%P zqjpvY8|e)wr`n4#W*@p;2O;^dA-hXu8d}l~0kt9UqM?7qmGQK!E9v$T75+RUuPD3M znQ?j3uH&lf_K)#yOP{%2JNK!k_ECU%#~;Z1qRBR)htzUUvREz&UXsFzVo3 z9=M4ereGjPfZlTl0;g_^)Qxg>N1Xc)b?OS{*uj-o0RM(`d zJJjlqpIT{mCo#tz06kx)>g(JXPO1?n3y`m{m9O&^0n4M{ z;Qw0|Oq`x_8;{pomT6CU8#~Oh$mwpMW%WRxud%F{@F56EqnLGKhR4A$_HHC|^8Ott zuxF$7-4##Ie;eS?4|P4(mOGYtfMdzV+sV@z zhtPDqg0Fz%u+cmA=RoO#{zz*+-=D!p{U;3VAdmHC(jOV&zW%DPzhoup&paF?EA@YV zXX@`sTo6OA-^1V%go9WyLIS54biz0*39(`Xl|-L#rw`}7fYWTJ6Y>ILoHUoJ3I>IE;I=p~E(#F4 z4J4#!5Nknw6sE9_Rb)GeQJ~!nWCXf){|QRd1ka}YmVB@P%6OC{rctUX*0ZJYsV<(Pv=-5r505`(1m$uNyZa5nDjBr8{ zx>=bvt{fQXZdsro0_r90{m#%CcoEaEOT855t;kCY+;m{L#7zf=4|P-Gc<7#Np<9|R zi5znrw?$z(vPw5+Gshg3iI7%?7Urf`MUESQ>|kAZW1r4^W!k{R-3XeL2dEzX34(+6DH4q@+5Qv9@mJ{KK@wWO2(6?`n3(~3xCg__D{wEeu3iSFMu#;9ZoW>- z4AgMXp~q~Qh#>H(n&QZXRB~e|i0V)z$Xbawdct}xL-`sP)rTax$Z%|ARC>H}P;G&n6q&dqiN^-&6me8?!4d>jv#>op90e;w7 zj=^kp9Nt6W!hT3UW$YHb7xmW{=tr9~qs@XH{o$@c=k40alP34Z2s4Ssw>t`?$0B>jhRwE(lj zNN{AKq;AYkV)h1RC6^4+_kfscEk=amiA#fojLS2uq7D+wJgIt5ZhLzGAJVmcwbs8~ zd+MNawhFf4S!-pbGwY>lj%9sNBHtC({VLEo2oABTJ0!Y?^3@;$jFYZx4wb*TrYz@`k@tkSdsc*VF^)=(R)qmaQvUOxz zDSLI+;L2+wr4bwKt@$64K;&Ph?I;-*B#%jv9@rsY~=b#}Q+Ex$yGlql2svK+%U<4S~NJ24DFt|Q4GfJLPgbs-uoDDF_EL@t%t zrC|~@AOu1}0S@c{4deg;;zJKMTm(HD{Rzp%l+uzNIH-%F2cJqrF&YFt^vx_+reqcA zfP6Fa-kZ1c=6!za^SKd>7vG54|G?@WY-10(wb)Io;WPoNl(cYald4nh(P5< zJzLOU3f9gyeKSQCu=zeIEMWrVz;r>&>dOme%EWW2^v$e}BO>*-mK;sJjN4{{Grq;d zl?&>^oH{o@GkYU#ebs_eYqR?iEIt7RF-i&=N{X70bZG7>$_Scc!<&>^YYEmec4{K- z$cUP}Aw6jcNGf|+XB*a~bznF4U~fhY11FsGWh716kdmFS_Ja%;th=h{wvg;zKoJjh zv&&lQE?cr+T%t~b%_7m$>{aZ+UXT5EZv*&T*g~dFL7PGaeF~PdFDD?=wj(l~Hrtby zv-n|R5_&AghzO)e2bGQD8YWax)vTU1Rh6(05f=dJN^_&B zMxOdqy_i??OKKVnYfzbB1DlUUDj7JJp)OTT=Te5Dss@vKZ2jk3(VK-ljv6T=pVFi7 zE-N@~8qs)RsbFT)Ms%*AFBfLs8@+58*=0SCbu&743+uO{x$IJOtz_OT=x4{qC!$8y z#G`Ae^lEAu8_~7wT2uu#tw5seA{sWMMKg0@jl0>{T8VVQ=gYy@wivV3It2vd41k~3 z(R0PO*-=xDe5V|J9zOGjxt;LEs&u!rF*sC}0M}5w_uQNAPrbXqjt)1T29#ei9++Xz zKunS~R$Ad>@7es?G~AGT93@0LfgF*DA0`rHpqqgJ13ds*?@js`g3-%eZOU^ijWV_q z0Bp@SeRus#$Ii{px-w8x2EJ2!UT}Lncq5toz6p?;7K?c~_wl6BVl)hX9 z2ia$M8~INAm%90vsIpWtv1*tk&D_-2 z^Nyq9FquR?R`#OT#vrtM8g%J16&tg{v$?&qW{B#&lLFF>eKMJA92jrNWbtrd9FTc^ zpG@Xh2L_XA2zNjJAFsgde_+_|tO={X?Lz2!mJ+Y;(98Jkb2bFNUW;QW9owy#mN;^4kLC<70tfw&zEL@w9C#Dw@YT{aa z7B)9#AfGU+kr8$YAq^qh%JWQ?!LEM{h850I24k)4Zwq6FxClH(9l$zjbccUHZr87> zmmB@VUrl{6RqsDn>p%C$-|Y0iTfNcn_kUTb50BS|$G83C&xhWw4~^G`#_vnN^Zd1| zw?&<(4Nq+QCph(NZRqTNDm%O5A8!Qvwx&Ltst3>4g6DUF?>-&h2~IY8j?{ZjZugvg zG*at1^Ov5J&0e@l^9b^G-MhGX@ymgFI9dxww>{B@KUnu4ulbMf_)k1E>!+q_r>3_3 zQ~SR8!XLa>-Yh>;!fz%dmKb<)V5tBaD&Y>4+{4m>Ca!`PkdX?bx2G34$Hw8O%HR-X z2);Yew^W2Oq=E%VK;TqTZ29Y}e=qlZ{jWBDB!yCA- z)kCLiq0^5})Zd=0!7nsf^G;T;G=iZAq0c6td50VR-l~tk9o)kUqGFK%>nvC)P*v(y z)qFuK=GfY+svj0pIg3Kh123=LyoON)3OY0;W{El=NfQ>*1NI=khg4;CMue-wH4*4f(N zxh4X{(}7(!HBZWN=%M^?1QUNOeur4ZOPywE4H{reO<${CTFnh%#gYcBNaaDFx7ujl zuTdF8e`fF$)5c_YW=TeXwrRj&Nc%A{qBDKZNSc%!ud literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10496ca962f92a4436309d92253ebe4624d217f8 GIT binary patch literal 510 zcmYLGJ#Q2-5MAGgE^-v8ph+`yToIdqXd;9{2n7WJC5Ymhm3Qsiby(XgkN03x!LLaE z0;1+mK!qqkS0V8S5{2q2e94}eVrHJ^N%P*!w~Y;&#Q6QSr~k(KX9-r(@0v>%o6|%l zlSGmUQRH~IqWrJ2_nBOyNx$un2Pa?X>rx{aC!0!vdap4cI9s(49H2IJrKc@;6}(n6 zZ3_scf|>HDodw&NOnGoq$V=g+3Uun7fp`mBO-Y1$PT0bj5OK#`I+PkZrSj3&IY@gZU^;rOVTFzMiI? zv<8CXR=bcC1Nnv%nSbKDR;KzRX$K?m` oPE6;0LdZpLNCxLOlAE_btpB37e$vhFbo1=#4+80Y8zPnE(I) literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5freq.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5freq.py new file mode 100644 index 0000000..87d9f97 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5freq.py @@ -0,0 +1,386 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Big5 frequency table +# by Taiwan's Mandarin Promotion Council +# +# +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table +BIG5_TABLE_SIZE = 5376 +# fmt: off +BIG5_CHAR_TO_FREQ_ORDER = ( + 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 +3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 +1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 + 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 +3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 +4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 +5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 + 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 + 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 + 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 +2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 +1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 +3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 + 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 +1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 +3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 +2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 + 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 +3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 +1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 +5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 + 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 +5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 +1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 + 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 + 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 +3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 +3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 + 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 +2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 +2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 + 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 + 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 +3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 +1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 +1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 +1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 +2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 + 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 +4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 +1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 +5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 +2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 + 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 + 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 + 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 + 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 +5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 + 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 +1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 + 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 + 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 +5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 +1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 + 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 +3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 +4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 +3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 + 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 + 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 +1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 +4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 +3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 +3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 +2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 +5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 +3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 +5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 +1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 +2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 +1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 + 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 +1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 +4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 +3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 + 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 + 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 + 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 +2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 +5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 +1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 +2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 +1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 +1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 +5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 +5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 +5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 +3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 +4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 +4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 +2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 +5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 +3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 + 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 +5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 +5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 +1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 +2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 +3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 +4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 +5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 +3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 +4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 +1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 +1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 +4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 +1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 + 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 +1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 +1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 +3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 + 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 +5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 +2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 +1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 +1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 +5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 + 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 +4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 + 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 +2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 + 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 +1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 +1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 + 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 +4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 +4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 +1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 +3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 +5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 +5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 +1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 +2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 +1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 +3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 +2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 +3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 +2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 +4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 +4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 +3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 + 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 +3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 + 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 +3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 +4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 +3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 +1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 +5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 + 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 +5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 +1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 + 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 +4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 +4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 + 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 +2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 +2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 +3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 +1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 +4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 +2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 +1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 +1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 +2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 +3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 +1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 +5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 +1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 +4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 +1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 + 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 +1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 +4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 +4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 +2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 +1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 +4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 + 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 +5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 +2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 +3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 +4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 + 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 +5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 +5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 +1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 +4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 +4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 +2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 +3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 +3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 +2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 +1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 +4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 +3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 +3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 +2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 +4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 +5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 +3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 +2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 +3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 +1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 +2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 +3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 +4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 +2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 +2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 +5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 +1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 +2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 +1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 +3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 +4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 +2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 +3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 +3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 +2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 +4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 +2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 +3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 +4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 +5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 +3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 + 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 +1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 +4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 +1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 +4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 +5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 + 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 +5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 +5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 +2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 +3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 +2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 +2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 + 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 +1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 +4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 +3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 +3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 + 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 +2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 + 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 +2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 +4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 +1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 +4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 +1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 +3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 + 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 +3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 +5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 +5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 +3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 +3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 +1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 +2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 +5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 +1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 +1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 +3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 + 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 +1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 +4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 +5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 +2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 +3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 + 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 +1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 +2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 +2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 +5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 +5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 +5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 +2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 +2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 +1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 +4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 +3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 +3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 +4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 +4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 +2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 +2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 +5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 +4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 +5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 +4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 + 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 + 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 +1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 +3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 +4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 +1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 +5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 +2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 +2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 +3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 +5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 +1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 +3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 +5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 +1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 +5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 +2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 +3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 +2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 +3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 +3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 +3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 +4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 + 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 +2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 +4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 +3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 +5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 +1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 +5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 + 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 +1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 + 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 +4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 +1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 +4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 +1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 + 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 +3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 +4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 +5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 + 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 +3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 + 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 +2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 +) +# fmt: on diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5prober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5prober.py new file mode 100644 index 0000000..ef09c60 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/big5prober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import Big5DistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import BIG5_SM_MODEL + + +class Big5Prober(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) + self.distribution_analyzer = Big5DistributionAnalysis() + self.reset() + + @property + def charset_name(self) -> str: + return "Big5" + + @property + def language(self) -> str: + return "Chinese" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/chardistribution.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/chardistribution.py new file mode 100644 index 0000000..176cb99 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/chardistribution.py @@ -0,0 +1,261 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Tuple, Union + +from .big5freq import ( + BIG5_CHAR_TO_FREQ_ORDER, + BIG5_TABLE_SIZE, + BIG5_TYPICAL_DISTRIBUTION_RATIO, +) +from .euckrfreq import ( + EUCKR_CHAR_TO_FREQ_ORDER, + EUCKR_TABLE_SIZE, + EUCKR_TYPICAL_DISTRIBUTION_RATIO, +) +from .euctwfreq import ( + EUCTW_CHAR_TO_FREQ_ORDER, + EUCTW_TABLE_SIZE, + EUCTW_TYPICAL_DISTRIBUTION_RATIO, +) +from .gb2312freq import ( + GB2312_CHAR_TO_FREQ_ORDER, + GB2312_TABLE_SIZE, + GB2312_TYPICAL_DISTRIBUTION_RATIO, +) +from .jisfreq import ( + JIS_CHAR_TO_FREQ_ORDER, + JIS_TABLE_SIZE, + JIS_TYPICAL_DISTRIBUTION_RATIO, +) +from .johabfreq import JOHAB_TO_EUCKR_ORDER_TABLE + + +class CharDistributionAnalysis: + ENOUGH_DATA_THRESHOLD = 1024 + SURE_YES = 0.99 + SURE_NO = 0.01 + MINIMUM_DATA_THRESHOLD = 3 + + def __init__(self) -> None: + # Mapping table to get frequency order from char order (get from + # GetOrder()) + self._char_to_freq_order: Tuple[int, ...] = tuple() + self._table_size = 0 # Size of above table + # This is a constant value which varies from language to language, + # used in calculating confidence. See + # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html + # for further detail. + self.typical_distribution_ratio = 0.0 + self._done = False + self._total_chars = 0 + self._freq_chars = 0 + self.reset() + + def reset(self) -> None: + """reset analyser, clear any state""" + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + self._total_chars = 0 # Total characters encountered + # The number of characters whose frequency order is less than 512 + self._freq_chars = 0 + + def feed(self, char: Union[bytes, bytearray], char_len: int) -> None: + """feed a character with known length""" + if char_len == 2: + # we only care about 2-bytes character in our distribution analysis + order = self.get_order(char) + else: + order = -1 + if order >= 0: + self._total_chars += 1 + # order is valid + if order < self._table_size: + if 512 > self._char_to_freq_order[order]: + self._freq_chars += 1 + + def get_confidence(self) -> float: + """return confidence based on existing data""" + # if we didn't receive any character in our consideration range, + # return negative answer + if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD: + return self.SURE_NO + + if self._total_chars != self._freq_chars: + r = self._freq_chars / ( + (self._total_chars - self._freq_chars) * self.typical_distribution_ratio + ) + if r < self.SURE_YES: + return r + + # normalize confidence (we don't want to be 100% sure) + return self.SURE_YES + + def got_enough_data(self) -> bool: + # It is not necessary to receive all data to draw conclusion. + # For charset detection, certain amount of data is enough + return self._total_chars > self.ENOUGH_DATA_THRESHOLD + + def get_order(self, _: Union[bytes, bytearray]) -> int: + # We do not handle characters based on the original encoding string, + # but convert this encoding string to a number, here called order. + # This allows multiple encodings of a language to share one frequency + # table. + return -1 + + +class EUCTWDistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER + self._table_size = EUCTW_TABLE_SIZE + self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + # for euc-TW encoding, we are interested + # first byte range: 0xc4 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xC4: + return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1 + return -1 + + +class EUCKRDistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER + self._table_size = EUCKR_TABLE_SIZE + self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + # for euc-KR encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char = byte_str[0] + if first_char >= 0xB0: + return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1 + return -1 + + +class JOHABDistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER + self._table_size = EUCKR_TABLE_SIZE + self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + first_char = byte_str[0] + if 0x88 <= first_char < 0xD4: + code = first_char * 256 + byte_str[1] + return JOHAB_TO_EUCKR_ORDER_TABLE.get(code, -1) + return -1 + + +class GB2312DistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER + self._table_size = GB2312_TABLE_SIZE + self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + # for GB2312 encoding, we are interested + # first byte range: 0xb0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if (first_char >= 0xB0) and (second_char >= 0xA1): + return 94 * (first_char - 0xB0) + second_char - 0xA1 + return -1 + + +class Big5DistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER + self._table_size = BIG5_TABLE_SIZE + self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + # for big5 encoding, we are interested + # first byte range: 0xa4 -- 0xfe + # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if first_char >= 0xA4: + if second_char >= 0xA1: + return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 + return 157 * (first_char - 0xA4) + second_char - 0x40 + return -1 + + +class SJISDistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + # for sjis encoding, we are interested + # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe + # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe + # no validation needed here. State machine has done that + first_char, second_char = byte_str[0], byte_str[1] + if 0x81 <= first_char <= 0x9F: + order = 188 * (first_char - 0x81) + elif 0xE0 <= first_char <= 0xEF: + order = 188 * (first_char - 0xE0 + 31) + else: + return -1 + order = order + second_char - 0x40 + if second_char > 0x7F: + order = -1 + return order + + +class EUCJPDistributionAnalysis(CharDistributionAnalysis): + def __init__(self) -> None: + super().__init__() + self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER + self._table_size = JIS_TABLE_SIZE + self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO + + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: + # for euc-JP encoding, we are interested + # first byte range: 0xa0 -- 0xfe + # second byte range: 0xa1 -- 0xfe + # no validation needed here. State machine has done that + char = byte_str[0] + if char >= 0xA0: + return 94 * (char - 0xA1) + byte_str[1] - 0xA1 + return -1 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetgroupprober.py new file mode 100644 index 0000000..6def56b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -0,0 +1,106 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import List, Optional, Union + +from .charsetprober import CharSetProber +from .enums import LanguageFilter, ProbingState + + +class CharSetGroupProber(CharSetProber): + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: + super().__init__(lang_filter=lang_filter) + self._active_num = 0 + self.probers: List[CharSetProber] = [] + self._best_guess_prober: Optional[CharSetProber] = None + + def reset(self) -> None: + super().reset() + self._active_num = 0 + for prober in self.probers: + prober.reset() + prober.active = True + self._active_num += 1 + self._best_guess_prober = None + + @property + def charset_name(self) -> Optional[str]: + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.charset_name + + @property + def language(self) -> Optional[str]: + if not self._best_guess_prober: + self.get_confidence() + if not self._best_guess_prober: + return None + return self._best_guess_prober.language + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + for prober in self.probers: + if not prober.active: + continue + state = prober.feed(byte_str) + if not state: + continue + if state == ProbingState.FOUND_IT: + self._best_guess_prober = prober + self._state = ProbingState.FOUND_IT + return self.state + if state == ProbingState.NOT_ME: + prober.active = False + self._active_num -= 1 + if self._active_num <= 0: + self._state = ProbingState.NOT_ME + return self.state + return self.state + + def get_confidence(self) -> float: + state = self.state + if state == ProbingState.FOUND_IT: + return 0.99 + if state == ProbingState.NOT_ME: + return 0.01 + best_conf = 0.0 + self._best_guess_prober = None + for prober in self.probers: + if not prober.active: + self.logger.debug("%s not active", prober.charset_name) + continue + conf = prober.get_confidence() + self.logger.debug( + "%s %s confidence = %s", prober.charset_name, prober.language, conf + ) + if best_conf < conf: + best_conf = conf + self._best_guess_prober = prober + if not self._best_guess_prober: + return 0.0 + return best_conf diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetprober.py new file mode 100644 index 0000000..a103ca1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/charsetprober.py @@ -0,0 +1,147 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging +import re +from typing import Optional, Union + +from .enums import LanguageFilter, ProbingState + +INTERNATIONAL_WORDS_PATTERN = re.compile( + b"[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?" +) + + +class CharSetProber: + + SHORTCUT_THRESHOLD = 0.95 + + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: + self._state = ProbingState.DETECTING + self.active = True + self.lang_filter = lang_filter + self.logger = logging.getLogger(__name__) + + def reset(self) -> None: + self._state = ProbingState.DETECTING + + @property + def charset_name(self) -> Optional[str]: + return None + + @property + def language(self) -> Optional[str]: + raise NotImplementedError + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + raise NotImplementedError + + @property + def state(self) -> ProbingState: + return self._state + + def get_confidence(self) -> float: + return 0.0 + + @staticmethod + def filter_high_byte_only(buf: Union[bytes, bytearray]) -> bytes: + buf = re.sub(b"([\x00-\x7F])+", b" ", buf) + return buf + + @staticmethod + def filter_international_words(buf: Union[bytes, bytearray]) -> bytearray: + """ + We define three types of bytes: + alphabet: english alphabets [a-zA-Z] + international: international characters [\x80-\xFF] + marker: everything else [^a-zA-Z\x80-\xFF] + The input buffer can be thought to contain a series of words delimited + by markers. This function works to filter all words that contain at + least one international character. All contiguous sequences of markers + are replaced by a single space ascii character. + This filter applies to all scripts which do not use English characters. + """ + filtered = bytearray() + + # This regex expression filters out only words that have at-least one + # international character. The word may include one marker character at + # the end. + words = INTERNATIONAL_WORDS_PATTERN.findall(buf) + + for word in words: + filtered.extend(word[:-1]) + + # If the last character in the word is a marker, replace it with a + # space as markers shouldn't affect our analysis (they are used + # similarly across all languages and may thus have similar + # frequencies). + last_char = word[-1:] + if not last_char.isalpha() and last_char < b"\x80": + last_char = b" " + filtered.extend(last_char) + + return filtered + + @staticmethod + def remove_xml_tags(buf: Union[bytes, bytearray]) -> bytes: + """ + Returns a copy of ``buf`` that retains only the sequences of English + alphabet and high byte characters that are not between <> characters. + This filter can be applied to all scripts which contain both English + characters and extended ASCII characters, but is currently only used by + ``Latin1Prober``. + """ + filtered = bytearray() + in_tag = False + prev = 0 + buf = memoryview(buf).cast("c") + + for curr, buf_char in enumerate(buf): + # Check if we're coming out of or entering an XML tag + + # https://github.com/python/typeshed/issues/8182 + if buf_char == b">": # type: ignore[comparison-overlap] + prev = curr + 1 + in_tag = False + # https://github.com/python/typeshed/issues/8182 + elif buf_char == b"<": # type: ignore[comparison-overlap] + if curr > prev and not in_tag: + # Keep everything after last non-extended-ASCII, + # non-alphabetic character + filtered.extend(buf[prev:curr]) + # Output a space to delimit stretch we kept + filtered.extend(b" ") + in_tag = True + + # If we're not in a tag... + if not in_tag: + # Keep everything after last non-extended-ASCII, non-alphabetic + # character + filtered.extend(buf[prev:]) + + return filtered diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ab2767f914143605d2db7eae88ed9f0811e3c12 GIT binary patch literal 217 zcmZ9GO$x#=5QQ6mKm-rs!VcOGxN~K?^}(C> z&F~iU7!3MJ)brlLCST3?*QXON*Q8E{VslG;D{h~r^SwYpl>t^@*!ufH9bnQ~IcK!{E??jJ1qB=tB9-1kfdd glxQ(XnY#Mhn^lyZN;CJ}_=t$+2HtHUgl|Xs0SM7Nn*aa+ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..563683cb9e8b974a646b2c5044e1c5da1f694408 GIT binary patch literal 4034 zcma)9O>7&-6`o!0F3IJOMEO^;VUG;QH4~AH+{SQe!>(gRiGkEMEV)Qo7}Sb86jxgA zva?GmBxuwDQdC9^SiV#$f&eLc=#O;BF^3+~Q-StEB?U}xUDQC(LvD1W^pI2E47nnu z*g!|%?)ry)toAzF#ppB^&pW*WEgbnArjTYB&MtI zix7oG*=$kOqwmYPxSr5sFei6rxqL(yi;^CP)d4c74=kKUh=xQ&2G+Tu2nnbr*2LtH zc1%D>ohneH;^L)}Q7B)4=!vJsc_m$G=tuylLL zNsXU*?FQJf?rL)wj;iyAd5ApU>M&K9#6ljs#lBHvi(EAnJUy?1ZDad zHr(!!4LFlwRjMv7QoD>D7mkuv)vuV#t|bFr=&DPoHfs`3dfRYZFLb@)8n&gGUx^@* z6ud}2zeuP9bNM_B6SpjbjWu(EJIJPL&LP)*x0CUHl8B@7ntZ%neKe! z)+$}OEcTJYRL|C2;`FO!LC)f9@MOzfYXIG*3AYHq!)Ez@eR@0JfR+7uDNQxInU)F{U}6_aq&- z%99JG?GScpxNwj%cv6Nz8etE}?iym$w;oJ{g|kuuaDW(Kap<1X4hiNYhz%$0wH zChlHt4IcO!$@0*$^km@h+WrrIu`IPh;Rnh+W!b$SeHw{AIQzlb$I(PP0z&O5l9gY* z`QDrFUv0(W58k==&fUq!!FX%nz{>pQz-Tix+6u;&=RXe)w}uX`99*Gmr#>Fp92sj4 zr<$pYpDuo8ZeE&dUYu?Y+-inywcuKT1VG=%4ok{-~d9?d&pZRco;D|J1lPM{ z2({y zI6u@|@q0k~1)%*vBQVb(zNh*0OpmyS{3U%~pU)WT_Oy-t>)pr+sP~n7#20I@y^i{$ z-W%*Y)*Jafn8kj-_ue?L-u0J%J^|e)5~>|c9;$!I>EkUZJWMD{3U(PTsLuHj1Ny4$ zDt5{eU}j*p5-UG|&NT`J$!fv}{;)KRELNE)BkNl^p3 z&35Xaq$ko@Pk1>gc~Xg(6;EQv@MH@P;&{QXa6Oz#b&Hw$%QqkugNH4%wmVV6J`fg5 zhcXb`RTt_kPjy_VFY_*~lB7sS0TG^}@f=teQ)WGsN_A3KeeC87Guf^T1@Umw%$jZ; z<^{W*dibT&P!7x`9Sl32f}QC1;8FkGPwgr|0a9hvvRK{FSEj--pxy&>7KvF);~S7# z9DHT^=G&@FElhH9N!fTN9L3eTFFwa`H)J1^u4&Fs1XPF=(`yGlDnW*P&4V2GDA za2NCK7&D^ZfK-Z2s}Nd=RS97DPAa>WOn4!P0Te2b_2QSiH{k{wQZK6MdLCB!?eIbj z1Jf*Q0RXBe^TO6sW~;>_p_FAoPvv^pCk|!T)>8~ebKROJS6DPX1-=1Uo|<||)l)j* zNlKLQfv0dSl&1y96N(PI)eQTT5sN34HN&FkpqquCgMs-WyA7fE!AG%^o3WEmqt~}( zDX!d|Y^z9)JdzG=N{3pZfmZDLlh}brv16ODV{7ggv6HQM;z?rv>hQziM~TtR#AtKy zL_31y!`n!am3F-E+kLIr4_gCA+Cjc5!D_$eXe%_-ik)WyVJ;wp0OZV_t@o{6{bcyh z=RZv~GjFv6TtoyB1x1G*MUHMpj;>9A5jk;p;%O}LAa^hKHHyjVve1GoeD2=4``IUu zhs8kR$aV-F9A3>m%zYD4V)5-b3df&BM_Pl4mGRZH56^!5gWq5H?S;?uzb^b`VLKqk z;@7 za* Optional[str]: + """ + Return a string describing the probable encoding of a file or + list of strings. + + :param lines: The lines to get the encoding of. + :type lines: Iterable of bytes + :param name: Name of file or collection of lines + :type name: str + :param should_rename_legacy: Should we rename legacy encodings to + their more modern equivalents? + :type should_rename_legacy: ``bool`` + """ + u = UniversalDetector(should_rename_legacy=should_rename_legacy) + for line in lines: + line = bytearray(line) + u.feed(line) + # shortcut out of the loop to save reading further - particularly useful if we read a BOM. + if u.done: + break + u.close() + result = u.result + if minimal: + return result["encoding"] + if result["encoding"]: + return f'{name}: {result["encoding"]} with confidence {result["confidence"]}' + return f"{name}: no result" + + +def main(argv: Optional[List[str]] = None) -> None: + """ + Handles command line arguments and gets things started. + + :param argv: List of arguments, as if specified on the command-line. + If None, ``sys.argv[1:]`` is used instead. + :type argv: list of str + """ + # Get command line arguments + parser = argparse.ArgumentParser( + description=( + "Takes one or more file paths and reports their detected encodings" + ) + ) + parser.add_argument( + "input", + help="File whose encoding we would like to determine. (default: stdin)", + type=argparse.FileType("rb"), + nargs="*", + default=[sys.stdin.buffer], + ) + parser.add_argument( + "--minimal", + help="Print only the encoding to standard output", + action="store_true", + ) + parser.add_argument( + "-l", + "--legacy", + help="Rename legacy encodings to more modern ones.", + action="store_true", + ) + parser.add_argument( + "--version", action="version", version=f"%(prog)s {__version__}" + ) + args = parser.parse_args(argv) + + for f in args.input: + if f.isatty(): + print( + "You are running chardetect interactively. Press " + "CTRL-D twice at the start of a blank line to signal the " + "end of your input. If you want help, run chardetect " + "--help\n", + file=sys.stderr, + ) + print( + description_of( + f, f.name, minimal=args.minimal, should_rename_legacy=args.legacy + ) + ) + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachine.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachine.py new file mode 100644 index 0000000..8ed4a87 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachine.py @@ -0,0 +1,90 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +import logging + +from .codingstatemachinedict import CodingStateMachineDict +from .enums import MachineState + + +class CodingStateMachine: + """ + A state machine to verify a byte sequence for a particular encoding. For + each byte the detector receives, it will feed that byte to every active + state machine available, one byte at a time. The state machine changes its + state based on its previous state and the byte it receives. There are 3 + states in a state machine that are of interest to an auto-detector: + + START state: This is the state to start with, or a legal byte sequence + (i.e. a valid code point) for character has been identified. + + ME state: This indicates that the state machine identified a byte sequence + that is specific to the charset it is designed for and that + there is no other possible encoding which can contain this byte + sequence. This will to lead to an immediate positive answer for + the detector. + + ERROR state: This indicates the state machine identified an illegal byte + sequence for that encoding. This will lead to an immediate + negative answer for this encoding. Detector will exclude this + encoding from consideration from here on. + """ + + def __init__(self, sm: CodingStateMachineDict) -> None: + self._model = sm + self._curr_byte_pos = 0 + self._curr_char_len = 0 + self._curr_state = MachineState.START + self.active = True + self.logger = logging.getLogger(__name__) + self.reset() + + def reset(self) -> None: + self._curr_state = MachineState.START + + def next_state(self, c: int) -> int: + # for each byte we get its class + # if it is first byte, we also get byte length + byte_class = self._model["class_table"][c] + if self._curr_state == MachineState.START: + self._curr_byte_pos = 0 + self._curr_char_len = self._model["char_len_table"][byte_class] + # from byte's class and state_table, we get its next state + curr_state = self._curr_state * self._model["class_factor"] + byte_class + self._curr_state = self._model["state_table"][curr_state] + self._curr_byte_pos += 1 + return self._curr_state + + def get_current_charlen(self) -> int: + return self._curr_char_len + + def get_coding_state_machine(self) -> str: + return self._model["name"] + + @property + def language(self) -> str: + return self._model["language"] diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachinedict.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachinedict.py new file mode 100644 index 0000000..7a3c4c7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/codingstatemachinedict.py @@ -0,0 +1,19 @@ +from typing import TYPE_CHECKING, Tuple + +if TYPE_CHECKING: + # TypedDict was introduced in Python 3.8. + # + # TODO: Remove the else block and TYPE_CHECKING check when dropping support + # for Python 3.7. + from typing import TypedDict + + class CodingStateMachineDict(TypedDict, total=False): + class_table: Tuple[int, ...] + class_factor: int + state_table: Tuple[int, ...] + char_len_table: Tuple[int, ...] + name: str + language: str # Optional key + +else: + CodingStateMachineDict = dict diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cp949prober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cp949prober.py new file mode 100644 index 0000000..fa7307e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/cp949prober.py @@ -0,0 +1,49 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import EUCKRDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import CP949_SM_MODEL + + +class CP949Prober(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(CP949_SM_MODEL) + # NOTE: CP949 is a superset of EUC-KR, so the distribution should be + # not different. + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self) -> str: + return "CP949" + + @property + def language(self) -> str: + return "Korean" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/enums.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/enums.py new file mode 100644 index 0000000..5e3e198 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/enums.py @@ -0,0 +1,85 @@ +""" +All of the Enums that are used throughout the chardet package. + +:author: Dan Blanchard (dan.blanchard@gmail.com) +""" + +from enum import Enum, Flag + + +class InputState: + """ + This enum represents the different states a universal detector can be in. + """ + + PURE_ASCII = 0 + ESC_ASCII = 1 + HIGH_BYTE = 2 + + +class LanguageFilter(Flag): + """ + This enum represents the different language filters we can apply to a + ``UniversalDetector``. + """ + + NONE = 0x00 + CHINESE_SIMPLIFIED = 0x01 + CHINESE_TRADITIONAL = 0x02 + JAPANESE = 0x04 + KOREAN = 0x08 + NON_CJK = 0x10 + ALL = 0x1F + CHINESE = CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL + CJK = CHINESE | JAPANESE | KOREAN + + +class ProbingState(Enum): + """ + This enum represents the different states a prober can be in. + """ + + DETECTING = 0 + FOUND_IT = 1 + NOT_ME = 2 + + +class MachineState: + """ + This enum represents the different states a state machine can be in. + """ + + START = 0 + ERROR = 1 + ITS_ME = 2 + + +class SequenceLikelihood: + """ + This enum represents the likelihood of a character following the previous one. + """ + + NEGATIVE = 0 + UNLIKELY = 1 + LIKELY = 2 + POSITIVE = 3 + + @classmethod + def get_num_categories(cls) -> int: + """:returns: The number of likelihood categories in the enum.""" + return 4 + + +class CharacterCategory: + """ + This enum represents the different categories language models for + ``SingleByteCharsetProber`` put characters into. + + Anything less than CONTROL is considered a letter. + """ + + UNDEFINED = 255 + LINE_BREAK = 254 + SYMBOL = 253 + DIGIT = 252 + CONTROL = 251 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/escprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/escprober.py new file mode 100644 index 0000000..fd71383 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/escprober.py @@ -0,0 +1,102 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Optional, Union + +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .enums import LanguageFilter, MachineState, ProbingState +from .escsm import ( + HZ_SM_MODEL, + ISO2022CN_SM_MODEL, + ISO2022JP_SM_MODEL, + ISO2022KR_SM_MODEL, +) + + +class EscCharSetProber(CharSetProber): + """ + This CharSetProber uses a "code scheme" approach for detecting encodings, + whereby easily recognizable escape or shift sequences are relied on to + identify these encodings. + """ + + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: + super().__init__(lang_filter=lang_filter) + self.coding_sm = [] + if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: + self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL)) + self.coding_sm.append(CodingStateMachine(ISO2022CN_SM_MODEL)) + if self.lang_filter & LanguageFilter.JAPANESE: + self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL)) + if self.lang_filter & LanguageFilter.KOREAN: + self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL)) + self.active_sm_count = 0 + self._detected_charset: Optional[str] = None + self._detected_language: Optional[str] = None + self._state = ProbingState.DETECTING + self.reset() + + def reset(self) -> None: + super().reset() + for coding_sm in self.coding_sm: + coding_sm.active = True + coding_sm.reset() + self.active_sm_count = len(self.coding_sm) + self._detected_charset = None + self._detected_language = None + + @property + def charset_name(self) -> Optional[str]: + return self._detected_charset + + @property + def language(self) -> Optional[str]: + return self._detected_language + + def get_confidence(self) -> float: + return 0.99 if self._detected_charset else 0.00 + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + for c in byte_str: + for coding_sm in self.coding_sm: + if not coding_sm.active: + continue + coding_state = coding_sm.next_state(c) + if coding_state == MachineState.ERROR: + coding_sm.active = False + self.active_sm_count -= 1 + if self.active_sm_count <= 0: + self._state = ProbingState.NOT_ME + return self.state + elif coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + self._detected_charset = coding_sm.get_coding_state_machine() + self._detected_language = coding_sm.language + return self.state + + return self.state diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/escsm.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/escsm.py new file mode 100644 index 0000000..11d4adf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/escsm.py @@ -0,0 +1,261 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .codingstatemachinedict import CodingStateMachineDict +from .enums import MachineState + +# fmt: off +HZ_CLS = ( + 1, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 4, 0, 5, 2, 0, # 78 - 7f + 1, 1, 1, 1, 1, 1, 1, 1, # 80 - 87 + 1, 1, 1, 1, 1, 1, 1, 1, # 88 - 8f + 1, 1, 1, 1, 1, 1, 1, 1, # 90 - 97 + 1, 1, 1, 1, 1, 1, 1, 1, # 98 - 9f + 1, 1, 1, 1, 1, 1, 1, 1, # a0 - a7 + 1, 1, 1, 1, 1, 1, 1, 1, # a8 - af + 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7 + 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf + 1, 1, 1, 1, 1, 1, 1, 1, # c0 - c7 + 1, 1, 1, 1, 1, 1, 1, 1, # c8 - cf + 1, 1, 1, 1, 1, 1, 1, 1, # d0 - d7 + 1, 1, 1, 1, 1, 1, 1, 1, # d8 - df + 1, 1, 1, 1, 1, 1, 1, 1, # e0 - e7 + 1, 1, 1, 1, 1, 1, 1, 1, # e8 - ef + 1, 1, 1, 1, 1, 1, 1, 1, # f0 - f7 + 1, 1, 1, 1, 1, 1, 1, 1, # f8 - ff +) + +HZ_ST = ( +MachineState.START, MachineState.ERROR, 3, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07 +MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f +MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, 4, MachineState.ERROR, # 10-17 + 5, MachineState.ERROR, 6, MachineState.ERROR, 5, 5, 4, MachineState.ERROR, # 18-1f + 4, MachineState.ERROR, 4, 4, 4, MachineState.ERROR, 4, MachineState.ERROR, # 20-27 + 4, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 28-2f +) +# fmt: on + +HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +HZ_SM_MODEL: CodingStateMachineDict = { + "class_table": HZ_CLS, + "class_factor": 6, + "state_table": HZ_ST, + "char_len_table": HZ_CHAR_LEN_TABLE, + "name": "HZ-GB-2312", + "language": "Chinese", +} + +# fmt: off +ISO2022CN_CLS = ( + 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 3, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 4, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 + 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f + 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 + 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff +) + +ISO2022CN_ST = ( + MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07 + MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f + MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17 + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, # 18-1f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 20-27 + 5, 6, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 28-2f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 30-37 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, # 38-3f +) +# fmt: on + +ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022CN_SM_MODEL: CodingStateMachineDict = { + "class_table": ISO2022CN_CLS, + "class_factor": 9, + "state_table": ISO2022CN_ST, + "char_len_table": ISO2022CN_CHAR_LEN_TABLE, + "name": "ISO-2022-CN", + "language": "Chinese", +} + +# fmt: off +ISO2022JP_CLS = ( + 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 2, 2, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 7, 0, 0, 0, # 20 - 27 + 3, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 6, 0, 4, 0, 8, 0, 0, 0, # 40 - 47 + 0, 9, 5, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 + 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f + 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 + 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff +) + +ISO2022JP_ST = ( + MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07 + MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17 + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, # 18-1f + MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 20-27 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 6, MachineState.ITS_ME, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, # 28-2f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, # 30-37 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 38-3f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, MachineState.START, # 40-47 +) +# fmt: on + +ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +ISO2022JP_SM_MODEL: CodingStateMachineDict = { + "class_table": ISO2022JP_CLS, + "class_factor": 10, + "state_table": ISO2022JP_ST, + "char_len_table": ISO2022JP_CHAR_LEN_TABLE, + "name": "ISO-2022-JP", + "language": "Japanese", +} + +# fmt: off +ISO2022KR_CLS = ( + 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 3, 0, 0, 0, # 20 - 27 + 0, 4, 0, 0, 0, 0, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 5, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 + 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f + 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 + 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff +) + +ISO2022KR_ST = ( + MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f + MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 10-17 + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f + MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 20-27 +) +# fmt: on + +ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) + +ISO2022KR_SM_MODEL: CodingStateMachineDict = { + "class_table": ISO2022KR_CLS, + "class_factor": 6, + "state_table": ISO2022KR_ST, + "char_len_table": ISO2022KR_CHAR_LEN_TABLE, + "name": "ISO-2022-KR", + "language": "Korean", +} diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/eucjpprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/eucjpprober.py new file mode 100644 index 0000000..39487f4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/eucjpprober.py @@ -0,0 +1,102 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Union + +from .chardistribution import EUCJPDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .enums import MachineState, ProbingState +from .jpcntx import EUCJPContextAnalysis +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import EUCJP_SM_MODEL + + +class EUCJPProber(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) + self.distribution_analyzer = EUCJPDistributionAnalysis() + self.context_analyzer = EUCJPContextAnalysis() + self.reset() + + def reset(self) -> None: + super().reset() + self.context_analyzer.reset() + + @property + def charset_name(self) -> str: + return "EUC-JP" + + @property + def language(self) -> str: + return "Japanese" + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + assert self.coding_sm is not None + assert self.distribution_analyzer is not None + + for i, byte in enumerate(byte_str): + # PY3K: byte_str is a byte array, so byte is an int, not a byte + coding_state = self.coding_sm.next_state(byte) + if coding_state == MachineState.ERROR: + self.logger.debug( + "%s %s prober hit error at byte %s", + self.charset_name, + self.language, + i, + ) + self._state = ProbingState.NOT_ME + break + if coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + if coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte + self.context_analyzer.feed(self._last_char, char_len) + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.context_analyzer.feed(byte_str[i - 1 : i + 1], char_len) + self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if self.context_analyzer.got_enough_data() and ( + self.get_confidence() > self.SHORTCUT_THRESHOLD + ): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self) -> float: + assert self.distribution_analyzer is not None + + context_conf = self.context_analyzer.get_confidence() + distrib_conf = self.distribution_analyzer.get_confidence() + return max(context_conf, distrib_conf) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrfreq.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrfreq.py new file mode 100644 index 0000000..7dc3b10 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrfreq.py @@ -0,0 +1,196 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology + +# 128 --> 0.79 +# 256 --> 0.92 +# 512 --> 0.986 +# 1024 --> 0.99944 +# 2048 --> 0.99999 +# +# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 +# Random Distribution Ration = 512 / (2350-512) = 0.279. +# +# Typical Distribution Ratio + +EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 + +EUCKR_TABLE_SIZE = 2352 + +# Char to FreqOrder table , +# fmt: off +EUCKR_CHAR_TO_FREQ_ORDER = ( + 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, +1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, +1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, + 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, + 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, + 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, +1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, + 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, + 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, +1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, +1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, +1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, +1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, +1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, + 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, +1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, +1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, +1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, +1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, + 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, +1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, + 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, + 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, +1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, + 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, +1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, + 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, + 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, +1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, +1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, +1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, +1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, + 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, +1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, + 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, + 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, +1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, +1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, +1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, +1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, +1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, +1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, + 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, + 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, + 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, +1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, + 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, +1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, + 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, + 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, +2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, + 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, + 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, +2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, +2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, +2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, + 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, + 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, +2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, + 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, +1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, +2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, +1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, +2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, +2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, +1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, + 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, +2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, +2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, + 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, + 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, +2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, +1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, +2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, +2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, +2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, +2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, +2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, +2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, +1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, +2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, +2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, +2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, +2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, +2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, +1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, +1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, +2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, +1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, +2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, +1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, + 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, +2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, + 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, +2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, + 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, +2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, +2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, + 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, +2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, +1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, + 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, +1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, +2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, +1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, +2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, + 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, +2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, +1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, +2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, +1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, +2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, +1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, + 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, +2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, +2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, + 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, + 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, +1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, +1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, + 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, +2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, +2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, + 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, + 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, + 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, +2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, + 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, + 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, +2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, +2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, + 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, +2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, +1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, + 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, +2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, +2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, +2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, + 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, + 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, + 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, +2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, +2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, +2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, +1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, +2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, + 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 +) +# fmt: on diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrprober.py new file mode 100644 index 0000000..1fc5de0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euckrprober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import EUCKRDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import EUCKR_SM_MODEL + + +class EUCKRProber(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) + self.distribution_analyzer = EUCKRDistributionAnalysis() + self.reset() + + @property + def charset_name(self) -> str: + return "EUC-KR" + + @property + def language(self) -> str: + return "Korean" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwfreq.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwfreq.py new file mode 100644 index 0000000..4900ccc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwfreq.py @@ -0,0 +1,388 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# EUCTW frequency table +# Converted from big5 work +# by Taiwan's Mandarin Promotion Council +# + +# 128 --> 0.42261 +# 256 --> 0.57851 +# 512 --> 0.74851 +# 1024 --> 0.89384 +# 2048 --> 0.97583 +# +# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 +# Random Distribution Ration = 512/(5401-512)=0.105 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR + +EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 + +# Char to FreqOrder table +EUCTW_TABLE_SIZE = 5376 + +# fmt: off +EUCTW_CHAR_TO_FREQ_ORDER = ( + 1, 1800, 1506, 255, 1431, 198, 9, 82, 6, 7310, 177, 202, 3615, 1256, 2808, 110, # 2742 + 3735, 33, 3241, 261, 76, 44, 2113, 16, 2931, 2184, 1176, 659, 3868, 26, 3404, 2643, # 2758 + 1198, 3869, 3313, 4060, 410, 2211, 302, 590, 361, 1963, 8, 204, 58, 4296, 7311, 1931, # 2774 + 63, 7312, 7313, 317, 1614, 75, 222, 159, 4061, 2412, 1480, 7314, 3500, 3068, 224, 2809, # 2790 + 3616, 3, 10, 3870, 1471, 29, 2774, 1135, 2852, 1939, 873, 130, 3242, 1123, 312, 7315, # 2806 + 4297, 2051, 507, 252, 682, 7316, 142, 1914, 124, 206, 2932, 34, 3501, 3173, 64, 604, # 2822 + 7317, 2494, 1976, 1977, 155, 1990, 645, 641, 1606, 7318, 3405, 337, 72, 406, 7319, 80, # 2838 + 630, 238, 3174, 1509, 263, 939, 1092, 2644, 756, 1440, 1094, 3406, 449, 69, 2969, 591, # 2854 + 179, 2095, 471, 115, 2034, 1843, 60, 50, 2970, 134, 806, 1868, 734, 2035, 3407, 180, # 2870 + 995, 1607, 156, 537, 2893, 688, 7320, 319, 1305, 779, 2144, 514, 2374, 298, 4298, 359, # 2886 + 2495, 90, 2707, 1338, 663, 11, 906, 1099, 2545, 20, 2436, 182, 532, 1716, 7321, 732, # 2902 + 1376, 4062, 1311, 1420, 3175, 25, 2312, 1056, 113, 399, 382, 1949, 242, 3408, 2467, 529, # 2918 + 3243, 475, 1447, 3617, 7322, 117, 21, 656, 810, 1297, 2295, 2329, 3502, 7323, 126, 4063, # 2934 + 706, 456, 150, 613, 4299, 71, 1118, 2036, 4064, 145, 3069, 85, 835, 486, 2114, 1246, # 2950 + 1426, 428, 727, 1285, 1015, 800, 106, 623, 303, 1281, 7324, 2127, 2354, 347, 3736, 221, # 2966 + 3503, 3110, 7325, 1955, 1153, 4065, 83, 296, 1199, 3070, 192, 624, 93, 7326, 822, 1897, # 2982 + 2810, 3111, 795, 2064, 991, 1554, 1542, 1592, 27, 43, 2853, 859, 139, 1456, 860, 4300, # 2998 + 437, 712, 3871, 164, 2392, 3112, 695, 211, 3017, 2096, 195, 3872, 1608, 3504, 3505, 3618, # 3014 + 3873, 234, 811, 2971, 2097, 3874, 2229, 1441, 3506, 1615, 2375, 668, 2076, 1638, 305, 228, # 3030 + 1664, 4301, 467, 415, 7327, 262, 2098, 1593, 239, 108, 300, 200, 1033, 512, 1247, 2077, # 3046 + 7328, 7329, 2173, 3176, 3619, 2673, 593, 845, 1062, 3244, 88, 1723, 2037, 3875, 1950, 212, # 3062 + 266, 152, 149, 468, 1898, 4066, 4302, 77, 187, 7330, 3018, 37, 5, 2972, 7331, 3876, # 3078 + 7332, 7333, 39, 2517, 4303, 2894, 3177, 2078, 55, 148, 74, 4304, 545, 483, 1474, 1029, # 3094 + 1665, 217, 1869, 1531, 3113, 1104, 2645, 4067, 24, 172, 3507, 900, 3877, 3508, 3509, 4305, # 3110 + 32, 1408, 2811, 1312, 329, 487, 2355, 2247, 2708, 784, 2674, 4, 3019, 3314, 1427, 1788, # 3126 + 188, 109, 499, 7334, 3620, 1717, 1789, 888, 1217, 3020, 4306, 7335, 3510, 7336, 3315, 1520, # 3142 + 3621, 3878, 196, 1034, 775, 7337, 7338, 929, 1815, 249, 439, 38, 7339, 1063, 7340, 794, # 3158 + 3879, 1435, 2296, 46, 178, 3245, 2065, 7341, 2376, 7342, 214, 1709, 4307, 804, 35, 707, # 3174 + 324, 3622, 1601, 2546, 140, 459, 4068, 7343, 7344, 1365, 839, 272, 978, 2257, 2572, 3409, # 3190 + 2128, 1363, 3623, 1423, 697, 100, 3071, 48, 70, 1231, 495, 3114, 2193, 7345, 1294, 7346, # 3206 + 2079, 462, 586, 1042, 3246, 853, 256, 988, 185, 2377, 3410, 1698, 434, 1084, 7347, 3411, # 3222 + 314, 2615, 2775, 4308, 2330, 2331, 569, 2280, 637, 1816, 2518, 757, 1162, 1878, 1616, 3412, # 3238 + 287, 1577, 2115, 768, 4309, 1671, 2854, 3511, 2519, 1321, 3737, 909, 2413, 7348, 4069, 933, # 3254 + 3738, 7349, 2052, 2356, 1222, 4310, 765, 2414, 1322, 786, 4311, 7350, 1919, 1462, 1677, 2895, # 3270 + 1699, 7351, 4312, 1424, 2437, 3115, 3624, 2590, 3316, 1774, 1940, 3413, 3880, 4070, 309, 1369, # 3286 + 1130, 2812, 364, 2230, 1653, 1299, 3881, 3512, 3882, 3883, 2646, 525, 1085, 3021, 902, 2000, # 3302 + 1475, 964, 4313, 421, 1844, 1415, 1057, 2281, 940, 1364, 3116, 376, 4314, 4315, 1381, 7, # 3318 + 2520, 983, 2378, 336, 1710, 2675, 1845, 321, 3414, 559, 1131, 3022, 2742, 1808, 1132, 1313, # 3334 + 265, 1481, 1857, 7352, 352, 1203, 2813, 3247, 167, 1089, 420, 2814, 776, 792, 1724, 3513, # 3350 + 4071, 2438, 3248, 7353, 4072, 7354, 446, 229, 333, 2743, 901, 3739, 1200, 1557, 4316, 2647, # 3366 + 1920, 395, 2744, 2676, 3740, 4073, 1835, 125, 916, 3178, 2616, 4317, 7355, 7356, 3741, 7357, # 3382 + 7358, 7359, 4318, 3117, 3625, 1133, 2547, 1757, 3415, 1510, 2313, 1409, 3514, 7360, 2145, 438, # 3398 + 2591, 2896, 2379, 3317, 1068, 958, 3023, 461, 311, 2855, 2677, 4074, 1915, 3179, 4075, 1978, # 3414 + 383, 750, 2745, 2617, 4076, 274, 539, 385, 1278, 1442, 7361, 1154, 1964, 384, 561, 210, # 3430 + 98, 1295, 2548, 3515, 7362, 1711, 2415, 1482, 3416, 3884, 2897, 1257, 129, 7363, 3742, 642, # 3446 + 523, 2776, 2777, 2648, 7364, 141, 2231, 1333, 68, 176, 441, 876, 907, 4077, 603, 2592, # 3462 + 710, 171, 3417, 404, 549, 18, 3118, 2393, 1410, 3626, 1666, 7365, 3516, 4319, 2898, 4320, # 3478 + 7366, 2973, 368, 7367, 146, 366, 99, 871, 3627, 1543, 748, 807, 1586, 1185, 22, 2258, # 3494 + 379, 3743, 3180, 7368, 3181, 505, 1941, 2618, 1991, 1382, 2314, 7369, 380, 2357, 218, 702, # 3510 + 1817, 1248, 3418, 3024, 3517, 3318, 3249, 7370, 2974, 3628, 930, 3250, 3744, 7371, 59, 7372, # 3526 + 585, 601, 4078, 497, 3419, 1112, 1314, 4321, 1801, 7373, 1223, 1472, 2174, 7374, 749, 1836, # 3542 + 690, 1899, 3745, 1772, 3885, 1476, 429, 1043, 1790, 2232, 2116, 917, 4079, 447, 1086, 1629, # 3558 + 7375, 556, 7376, 7377, 2020, 1654, 844, 1090, 105, 550, 966, 1758, 2815, 1008, 1782, 686, # 3574 + 1095, 7378, 2282, 793, 1602, 7379, 3518, 2593, 4322, 4080, 2933, 2297, 4323, 3746, 980, 2496, # 3590 + 544, 353, 527, 4324, 908, 2678, 2899, 7380, 381, 2619, 1942, 1348, 7381, 1341, 1252, 560, # 3606 + 3072, 7382, 3420, 2856, 7383, 2053, 973, 886, 2080, 143, 4325, 7384, 7385, 157, 3886, 496, # 3622 + 4081, 57, 840, 540, 2038, 4326, 4327, 3421, 2117, 1445, 970, 2259, 1748, 1965, 2081, 4082, # 3638 + 3119, 1234, 1775, 3251, 2816, 3629, 773, 1206, 2129, 1066, 2039, 1326, 3887, 1738, 1725, 4083, # 3654 + 279, 3120, 51, 1544, 2594, 423, 1578, 2130, 2066, 173, 4328, 1879, 7386, 7387, 1583, 264, # 3670 + 610, 3630, 4329, 2439, 280, 154, 7388, 7389, 7390, 1739, 338, 1282, 3073, 693, 2857, 1411, # 3686 + 1074, 3747, 2440, 7391, 4330, 7392, 7393, 1240, 952, 2394, 7394, 2900, 1538, 2679, 685, 1483, # 3702 + 4084, 2468, 1436, 953, 4085, 2054, 4331, 671, 2395, 79, 4086, 2441, 3252, 608, 567, 2680, # 3718 + 3422, 4087, 4088, 1691, 393, 1261, 1791, 2396, 7395, 4332, 7396, 7397, 7398, 7399, 1383, 1672, # 3734 + 3748, 3182, 1464, 522, 1119, 661, 1150, 216, 675, 4333, 3888, 1432, 3519, 609, 4334, 2681, # 3750 + 2397, 7400, 7401, 7402, 4089, 3025, 0, 7403, 2469, 315, 231, 2442, 301, 3319, 4335, 2380, # 3766 + 7404, 233, 4090, 3631, 1818, 4336, 4337, 7405, 96, 1776, 1315, 2082, 7406, 257, 7407, 1809, # 3782 + 3632, 2709, 1139, 1819, 4091, 2021, 1124, 2163, 2778, 1777, 2649, 7408, 3074, 363, 1655, 3183, # 3798 + 7409, 2975, 7410, 7411, 7412, 3889, 1567, 3890, 718, 103, 3184, 849, 1443, 341, 3320, 2934, # 3814 + 1484, 7413, 1712, 127, 67, 339, 4092, 2398, 679, 1412, 821, 7414, 7415, 834, 738, 351, # 3830 + 2976, 2146, 846, 235, 1497, 1880, 418, 1992, 3749, 2710, 186, 1100, 2147, 2746, 3520, 1545, # 3846 + 1355, 2935, 2858, 1377, 583, 3891, 4093, 2573, 2977, 7416, 1298, 3633, 1078, 2549, 3634, 2358, # 3862 + 78, 3750, 3751, 267, 1289, 2099, 2001, 1594, 4094, 348, 369, 1274, 2194, 2175, 1837, 4338, # 3878 + 1820, 2817, 3635, 2747, 2283, 2002, 4339, 2936, 2748, 144, 3321, 882, 4340, 3892, 2749, 3423, # 3894 + 4341, 2901, 7417, 4095, 1726, 320, 7418, 3893, 3026, 788, 2978, 7419, 2818, 1773, 1327, 2859, # 3910 + 3894, 2819, 7420, 1306, 4342, 2003, 1700, 3752, 3521, 2359, 2650, 787, 2022, 506, 824, 3636, # 3926 + 534, 323, 4343, 1044, 3322, 2023, 1900, 946, 3424, 7421, 1778, 1500, 1678, 7422, 1881, 4344, # 3942 + 165, 243, 4345, 3637, 2521, 123, 683, 4096, 764, 4346, 36, 3895, 1792, 589, 2902, 816, # 3958 + 626, 1667, 3027, 2233, 1639, 1555, 1622, 3753, 3896, 7423, 3897, 2860, 1370, 1228, 1932, 891, # 3974 + 2083, 2903, 304, 4097, 7424, 292, 2979, 2711, 3522, 691, 2100, 4098, 1115, 4347, 118, 662, # 3990 + 7425, 611, 1156, 854, 2381, 1316, 2861, 2, 386, 515, 2904, 7426, 7427, 3253, 868, 2234, # 4006 + 1486, 855, 2651, 785, 2212, 3028, 7428, 1040, 3185, 3523, 7429, 3121, 448, 7430, 1525, 7431, # 4022 + 2164, 4348, 7432, 3754, 7433, 4099, 2820, 3524, 3122, 503, 818, 3898, 3123, 1568, 814, 676, # 4038 + 1444, 306, 1749, 7434, 3755, 1416, 1030, 197, 1428, 805, 2821, 1501, 4349, 7435, 7436, 7437, # 4054 + 1993, 7438, 4350, 7439, 7440, 2195, 13, 2779, 3638, 2980, 3124, 1229, 1916, 7441, 3756, 2131, # 4070 + 7442, 4100, 4351, 2399, 3525, 7443, 2213, 1511, 1727, 1120, 7444, 7445, 646, 3757, 2443, 307, # 4086 + 7446, 7447, 1595, 3186, 7448, 7449, 7450, 3639, 1113, 1356, 3899, 1465, 2522, 2523, 7451, 519, # 4102 + 7452, 128, 2132, 92, 2284, 1979, 7453, 3900, 1512, 342, 3125, 2196, 7454, 2780, 2214, 1980, # 4118 + 3323, 7455, 290, 1656, 1317, 789, 827, 2360, 7456, 3758, 4352, 562, 581, 3901, 7457, 401, # 4134 + 4353, 2248, 94, 4354, 1399, 2781, 7458, 1463, 2024, 4355, 3187, 1943, 7459, 828, 1105, 4101, # 4150 + 1262, 1394, 7460, 4102, 605, 4356, 7461, 1783, 2862, 7462, 2822, 819, 2101, 578, 2197, 2937, # 4166 + 7463, 1502, 436, 3254, 4103, 3255, 2823, 3902, 2905, 3425, 3426, 7464, 2712, 2315, 7465, 7466, # 4182 + 2332, 2067, 23, 4357, 193, 826, 3759, 2102, 699, 1630, 4104, 3075, 390, 1793, 1064, 3526, # 4198 + 7467, 1579, 3076, 3077, 1400, 7468, 4105, 1838, 1640, 2863, 7469, 4358, 4359, 137, 4106, 598, # 4214 + 3078, 1966, 780, 104, 974, 2938, 7470, 278, 899, 253, 402, 572, 504, 493, 1339, 7471, # 4230 + 3903, 1275, 4360, 2574, 2550, 7472, 3640, 3029, 3079, 2249, 565, 1334, 2713, 863, 41, 7473, # 4246 + 7474, 4361, 7475, 1657, 2333, 19, 463, 2750, 4107, 606, 7476, 2981, 3256, 1087, 2084, 1323, # 4262 + 2652, 2982, 7477, 1631, 1623, 1750, 4108, 2682, 7478, 2864, 791, 2714, 2653, 2334, 232, 2416, # 4278 + 7479, 2983, 1498, 7480, 2654, 2620, 755, 1366, 3641, 3257, 3126, 2025, 1609, 119, 1917, 3427, # 4294 + 862, 1026, 4109, 7481, 3904, 3760, 4362, 3905, 4363, 2260, 1951, 2470, 7482, 1125, 817, 4110, # 4310 + 4111, 3906, 1513, 1766, 2040, 1487, 4112, 3030, 3258, 2824, 3761, 3127, 7483, 7484, 1507, 7485, # 4326 + 2683, 733, 40, 1632, 1106, 2865, 345, 4113, 841, 2524, 230, 4364, 2984, 1846, 3259, 3428, # 4342 + 7486, 1263, 986, 3429, 7487, 735, 879, 254, 1137, 857, 622, 1300, 1180, 1388, 1562, 3907, # 4358 + 3908, 2939, 967, 2751, 2655, 1349, 592, 2133, 1692, 3324, 2985, 1994, 4114, 1679, 3909, 1901, # 4374 + 2185, 7488, 739, 3642, 2715, 1296, 1290, 7489, 4115, 2198, 2199, 1921, 1563, 2595, 2551, 1870, # 4390 + 2752, 2986, 7490, 435, 7491, 343, 1108, 596, 17, 1751, 4365, 2235, 3430, 3643, 7492, 4366, # 4406 + 294, 3527, 2940, 1693, 477, 979, 281, 2041, 3528, 643, 2042, 3644, 2621, 2782, 2261, 1031, # 4422 + 2335, 2134, 2298, 3529, 4367, 367, 1249, 2552, 7493, 3530, 7494, 4368, 1283, 3325, 2004, 240, # 4438 + 1762, 3326, 4369, 4370, 836, 1069, 3128, 474, 7495, 2148, 2525, 268, 3531, 7496, 3188, 1521, # 4454 + 1284, 7497, 1658, 1546, 4116, 7498, 3532, 3533, 7499, 4117, 3327, 2684, 1685, 4118, 961, 1673, # 4470 + 2622, 190, 2005, 2200, 3762, 4371, 4372, 7500, 570, 2497, 3645, 1490, 7501, 4373, 2623, 3260, # 4486 + 1956, 4374, 584, 1514, 396, 1045, 1944, 7502, 4375, 1967, 2444, 7503, 7504, 4376, 3910, 619, # 4502 + 7505, 3129, 3261, 215, 2006, 2783, 2553, 3189, 4377, 3190, 4378, 763, 4119, 3763, 4379, 7506, # 4518 + 7507, 1957, 1767, 2941, 3328, 3646, 1174, 452, 1477, 4380, 3329, 3130, 7508, 2825, 1253, 2382, # 4534 + 2186, 1091, 2285, 4120, 492, 7509, 638, 1169, 1824, 2135, 1752, 3911, 648, 926, 1021, 1324, # 4550 + 4381, 520, 4382, 997, 847, 1007, 892, 4383, 3764, 2262, 1871, 3647, 7510, 2400, 1784, 4384, # 4566 + 1952, 2942, 3080, 3191, 1728, 4121, 2043, 3648, 4385, 2007, 1701, 3131, 1551, 30, 2263, 4122, # 4582 + 7511, 2026, 4386, 3534, 7512, 501, 7513, 4123, 594, 3431, 2165, 1821, 3535, 3432, 3536, 3192, # 4598 + 829, 2826, 4124, 7514, 1680, 3132, 1225, 4125, 7515, 3262, 4387, 4126, 3133, 2336, 7516, 4388, # 4614 + 4127, 7517, 3912, 3913, 7518, 1847, 2383, 2596, 3330, 7519, 4389, 374, 3914, 652, 4128, 4129, # 4630 + 375, 1140, 798, 7520, 7521, 7522, 2361, 4390, 2264, 546, 1659, 138, 3031, 2445, 4391, 7523, # 4646 + 2250, 612, 1848, 910, 796, 3765, 1740, 1371, 825, 3766, 3767, 7524, 2906, 2554, 7525, 692, # 4662 + 444, 3032, 2624, 801, 4392, 4130, 7526, 1491, 244, 1053, 3033, 4131, 4132, 340, 7527, 3915, # 4678 + 1041, 2987, 293, 1168, 87, 1357, 7528, 1539, 959, 7529, 2236, 721, 694, 4133, 3768, 219, # 4694 + 1478, 644, 1417, 3331, 2656, 1413, 1401, 1335, 1389, 3916, 7530, 7531, 2988, 2362, 3134, 1825, # 4710 + 730, 1515, 184, 2827, 66, 4393, 7532, 1660, 2943, 246, 3332, 378, 1457, 226, 3433, 975, # 4726 + 3917, 2944, 1264, 3537, 674, 696, 7533, 163, 7534, 1141, 2417, 2166, 713, 3538, 3333, 4394, # 4742 + 3918, 7535, 7536, 1186, 15, 7537, 1079, 1070, 7538, 1522, 3193, 3539, 276, 1050, 2716, 758, # 4758 + 1126, 653, 2945, 3263, 7539, 2337, 889, 3540, 3919, 3081, 2989, 903, 1250, 4395, 3920, 3434, # 4774 + 3541, 1342, 1681, 1718, 766, 3264, 286, 89, 2946, 3649, 7540, 1713, 7541, 2597, 3334, 2990, # 4790 + 7542, 2947, 2215, 3194, 2866, 7543, 4396, 2498, 2526, 181, 387, 1075, 3921, 731, 2187, 3335, # 4806 + 7544, 3265, 310, 313, 3435, 2299, 770, 4134, 54, 3034, 189, 4397, 3082, 3769, 3922, 7545, # 4822 + 1230, 1617, 1849, 355, 3542, 4135, 4398, 3336, 111, 4136, 3650, 1350, 3135, 3436, 3035, 4137, # 4838 + 2149, 3266, 3543, 7546, 2784, 3923, 3924, 2991, 722, 2008, 7547, 1071, 247, 1207, 2338, 2471, # 4854 + 1378, 4399, 2009, 864, 1437, 1214, 4400, 373, 3770, 1142, 2216, 667, 4401, 442, 2753, 2555, # 4870 + 3771, 3925, 1968, 4138, 3267, 1839, 837, 170, 1107, 934, 1336, 1882, 7548, 7549, 2118, 4139, # 4886 + 2828, 743, 1569, 7550, 4402, 4140, 582, 2384, 1418, 3437, 7551, 1802, 7552, 357, 1395, 1729, # 4902 + 3651, 3268, 2418, 1564, 2237, 7553, 3083, 3772, 1633, 4403, 1114, 2085, 4141, 1532, 7554, 482, # 4918 + 2446, 4404, 7555, 7556, 1492, 833, 1466, 7557, 2717, 3544, 1641, 2829, 7558, 1526, 1272, 3652, # 4934 + 4142, 1686, 1794, 416, 2556, 1902, 1953, 1803, 7559, 3773, 2785, 3774, 1159, 2316, 7560, 2867, # 4950 + 4405, 1610, 1584, 3036, 2419, 2754, 443, 3269, 1163, 3136, 7561, 7562, 3926, 7563, 4143, 2499, # 4966 + 3037, 4406, 3927, 3137, 2103, 1647, 3545, 2010, 1872, 4144, 7564, 4145, 431, 3438, 7565, 250, # 4982 + 97, 81, 4146, 7566, 1648, 1850, 1558, 160, 848, 7567, 866, 740, 1694, 7568, 2201, 2830, # 4998 + 3195, 4147, 4407, 3653, 1687, 950, 2472, 426, 469, 3196, 3654, 3655, 3928, 7569, 7570, 1188, # 5014 + 424, 1995, 861, 3546, 4148, 3775, 2202, 2685, 168, 1235, 3547, 4149, 7571, 2086, 1674, 4408, # 5030 + 3337, 3270, 220, 2557, 1009, 7572, 3776, 670, 2992, 332, 1208, 717, 7573, 7574, 3548, 2447, # 5046 + 3929, 3338, 7575, 513, 7576, 1209, 2868, 3339, 3138, 4409, 1080, 7577, 7578, 7579, 7580, 2527, # 5062 + 3656, 3549, 815, 1587, 3930, 3931, 7581, 3550, 3439, 3777, 1254, 4410, 1328, 3038, 1390, 3932, # 5078 + 1741, 3933, 3778, 3934, 7582, 236, 3779, 2448, 3271, 7583, 7584, 3657, 3780, 1273, 3781, 4411, # 5094 + 7585, 308, 7586, 4412, 245, 4413, 1851, 2473, 1307, 2575, 430, 715, 2136, 2449, 7587, 270, # 5110 + 199, 2869, 3935, 7588, 3551, 2718, 1753, 761, 1754, 725, 1661, 1840, 4414, 3440, 3658, 7589, # 5126 + 7590, 587, 14, 3272, 227, 2598, 326, 480, 2265, 943, 2755, 3552, 291, 650, 1883, 7591, # 5142 + 1702, 1226, 102, 1547, 62, 3441, 904, 4415, 3442, 1164, 4150, 7592, 7593, 1224, 1548, 2756, # 5158 + 391, 498, 1493, 7594, 1386, 1419, 7595, 2055, 1177, 4416, 813, 880, 1081, 2363, 566, 1145, # 5174 + 4417, 2286, 1001, 1035, 2558, 2599, 2238, 394, 1286, 7596, 7597, 2068, 7598, 86, 1494, 1730, # 5190 + 3936, 491, 1588, 745, 897, 2948, 843, 3340, 3937, 2757, 2870, 3273, 1768, 998, 2217, 2069, # 5206 + 397, 1826, 1195, 1969, 3659, 2993, 3341, 284, 7599, 3782, 2500, 2137, 2119, 1903, 7600, 3938, # 5222 + 2150, 3939, 4151, 1036, 3443, 1904, 114, 2559, 4152, 209, 1527, 7601, 7602, 2949, 2831, 2625, # 5238 + 2385, 2719, 3139, 812, 2560, 7603, 3274, 7604, 1559, 737, 1884, 3660, 1210, 885, 28, 2686, # 5254 + 3553, 3783, 7605, 4153, 1004, 1779, 4418, 7606, 346, 1981, 2218, 2687, 4419, 3784, 1742, 797, # 5270 + 1642, 3940, 1933, 1072, 1384, 2151, 896, 3941, 3275, 3661, 3197, 2871, 3554, 7607, 2561, 1958, # 5286 + 4420, 2450, 1785, 7608, 7609, 7610, 3942, 4154, 1005, 1308, 3662, 4155, 2720, 4421, 4422, 1528, # 5302 + 2600, 161, 1178, 4156, 1982, 987, 4423, 1101, 4157, 631, 3943, 1157, 3198, 2420, 1343, 1241, # 5318 + 1016, 2239, 2562, 372, 877, 2339, 2501, 1160, 555, 1934, 911, 3944, 7611, 466, 1170, 169, # 5334 + 1051, 2907, 2688, 3663, 2474, 2994, 1182, 2011, 2563, 1251, 2626, 7612, 992, 2340, 3444, 1540, # 5350 + 2721, 1201, 2070, 2401, 1996, 2475, 7613, 4424, 528, 1922, 2188, 1503, 1873, 1570, 2364, 3342, # 5366 + 3276, 7614, 557, 1073, 7615, 1827, 3445, 2087, 2266, 3140, 3039, 3084, 767, 3085, 2786, 4425, # 5382 + 1006, 4158, 4426, 2341, 1267, 2176, 3664, 3199, 778, 3945, 3200, 2722, 1597, 2657, 7616, 4427, # 5398 + 7617, 3446, 7618, 7619, 7620, 3277, 2689, 1433, 3278, 131, 95, 1504, 3946, 723, 4159, 3141, # 5414 + 1841, 3555, 2758, 2189, 3947, 2027, 2104, 3665, 7621, 2995, 3948, 1218, 7622, 3343, 3201, 3949, # 5430 + 4160, 2576, 248, 1634, 3785, 912, 7623, 2832, 3666, 3040, 3786, 654, 53, 7624, 2996, 7625, # 5446 + 1688, 4428, 777, 3447, 1032, 3950, 1425, 7626, 191, 820, 2120, 2833, 971, 4429, 931, 3202, # 5462 + 135, 664, 783, 3787, 1997, 772, 2908, 1935, 3951, 3788, 4430, 2909, 3203, 282, 2723, 640, # 5478 + 1372, 3448, 1127, 922, 325, 3344, 7627, 7628, 711, 2044, 7629, 7630, 3952, 2219, 2787, 1936, # 5494 + 3953, 3345, 2220, 2251, 3789, 2300, 7631, 4431, 3790, 1258, 3279, 3954, 3204, 2138, 2950, 3955, # 5510 + 3956, 7632, 2221, 258, 3205, 4432, 101, 1227, 7633, 3280, 1755, 7634, 1391, 3281, 7635, 2910, # 5526 + 2056, 893, 7636, 7637, 7638, 1402, 4161, 2342, 7639, 7640, 3206, 3556, 7641, 7642, 878, 1325, # 5542 + 1780, 2788, 4433, 259, 1385, 2577, 744, 1183, 2267, 4434, 7643, 3957, 2502, 7644, 684, 1024, # 5558 + 4162, 7645, 472, 3557, 3449, 1165, 3282, 3958, 3959, 322, 2152, 881, 455, 1695, 1152, 1340, # 5574 + 660, 554, 2153, 4435, 1058, 4436, 4163, 830, 1065, 3346, 3960, 4437, 1923, 7646, 1703, 1918, # 5590 + 7647, 932, 2268, 122, 7648, 4438, 947, 677, 7649, 3791, 2627, 297, 1905, 1924, 2269, 4439, # 5606 + 2317, 3283, 7650, 7651, 4164, 7652, 4165, 84, 4166, 112, 989, 7653, 547, 1059, 3961, 701, # 5622 + 3558, 1019, 7654, 4167, 7655, 3450, 942, 639, 457, 2301, 2451, 993, 2951, 407, 851, 494, # 5638 + 4440, 3347, 927, 7656, 1237, 7657, 2421, 3348, 573, 4168, 680, 921, 2911, 1279, 1874, 285, # 5654 + 790, 1448, 1983, 719, 2167, 7658, 7659, 4441, 3962, 3963, 1649, 7660, 1541, 563, 7661, 1077, # 5670 + 7662, 3349, 3041, 3451, 511, 2997, 3964, 3965, 3667, 3966, 1268, 2564, 3350, 3207, 4442, 4443, # 5686 + 7663, 535, 1048, 1276, 1189, 2912, 2028, 3142, 1438, 1373, 2834, 2952, 1134, 2012, 7664, 4169, # 5702 + 1238, 2578, 3086, 1259, 7665, 700, 7666, 2953, 3143, 3668, 4170, 7667, 4171, 1146, 1875, 1906, # 5718 + 4444, 2601, 3967, 781, 2422, 132, 1589, 203, 147, 273, 2789, 2402, 898, 1786, 2154, 3968, # 5734 + 3969, 7668, 3792, 2790, 7669, 7670, 4445, 4446, 7671, 3208, 7672, 1635, 3793, 965, 7673, 1804, # 5750 + 2690, 1516, 3559, 1121, 1082, 1329, 3284, 3970, 1449, 3794, 65, 1128, 2835, 2913, 2759, 1590, # 5766 + 3795, 7674, 7675, 12, 2658, 45, 976, 2579, 3144, 4447, 517, 2528, 1013, 1037, 3209, 7676, # 5782 + 3796, 2836, 7677, 3797, 7678, 3452, 7679, 2602, 614, 1998, 2318, 3798, 3087, 2724, 2628, 7680, # 5798 + 2580, 4172, 599, 1269, 7681, 1810, 3669, 7682, 2691, 3088, 759, 1060, 489, 1805, 3351, 3285, # 5814 + 1358, 7683, 7684, 2386, 1387, 1215, 2629, 2252, 490, 7685, 7686, 4173, 1759, 2387, 2343, 7687, # 5830 + 4448, 3799, 1907, 3971, 2630, 1806, 3210, 4449, 3453, 3286, 2760, 2344, 874, 7688, 7689, 3454, # 5846 + 3670, 1858, 91, 2914, 3671, 3042, 3800, 4450, 7690, 3145, 3972, 2659, 7691, 3455, 1202, 1403, # 5862 + 3801, 2954, 2529, 1517, 2503, 4451, 3456, 2504, 7692, 4452, 7693, 2692, 1885, 1495, 1731, 3973, # 5878 + 2365, 4453, 7694, 2029, 7695, 7696, 3974, 2693, 1216, 237, 2581, 4174, 2319, 3975, 3802, 4454, # 5894 + 4455, 2694, 3560, 3457, 445, 4456, 7697, 7698, 7699, 7700, 2761, 61, 3976, 3672, 1822, 3977, # 5910 + 7701, 687, 2045, 935, 925, 405, 2660, 703, 1096, 1859, 2725, 4457, 3978, 1876, 1367, 2695, # 5926 + 3352, 918, 2105, 1781, 2476, 334, 3287, 1611, 1093, 4458, 564, 3146, 3458, 3673, 3353, 945, # 5942 + 2631, 2057, 4459, 7702, 1925, 872, 4175, 7703, 3459, 2696, 3089, 349, 4176, 3674, 3979, 4460, # 5958 + 3803, 4177, 3675, 2155, 3980, 4461, 4462, 4178, 4463, 2403, 2046, 782, 3981, 400, 251, 4179, # 5974 + 1624, 7704, 7705, 277, 3676, 299, 1265, 476, 1191, 3804, 2121, 4180, 4181, 1109, 205, 7706, # 5990 + 2582, 1000, 2156, 3561, 1860, 7707, 7708, 7709, 4464, 7710, 4465, 2565, 107, 2477, 2157, 3982, # 6006 + 3460, 3147, 7711, 1533, 541, 1301, 158, 753, 4182, 2872, 3562, 7712, 1696, 370, 1088, 4183, # 6022 + 4466, 3563, 579, 327, 440, 162, 2240, 269, 1937, 1374, 3461, 968, 3043, 56, 1396, 3090, # 6038 + 2106, 3288, 3354, 7713, 1926, 2158, 4467, 2998, 7714, 3564, 7715, 7716, 3677, 4468, 2478, 7717, # 6054 + 2791, 7718, 1650, 4469, 7719, 2603, 7720, 7721, 3983, 2661, 3355, 1149, 3356, 3984, 3805, 3985, # 6070 + 7722, 1076, 49, 7723, 951, 3211, 3289, 3290, 450, 2837, 920, 7724, 1811, 2792, 2366, 4184, # 6086 + 1908, 1138, 2367, 3806, 3462, 7725, 3212, 4470, 1909, 1147, 1518, 2423, 4471, 3807, 7726, 4472, # 6102 + 2388, 2604, 260, 1795, 3213, 7727, 7728, 3808, 3291, 708, 7729, 3565, 1704, 7730, 3566, 1351, # 6118 + 1618, 3357, 2999, 1886, 944, 4185, 3358, 4186, 3044, 3359, 4187, 7731, 3678, 422, 413, 1714, # 6134 + 3292, 500, 2058, 2345, 4188, 2479, 7732, 1344, 1910, 954, 7733, 1668, 7734, 7735, 3986, 2404, # 6150 + 4189, 3567, 3809, 4190, 7736, 2302, 1318, 2505, 3091, 133, 3092, 2873, 4473, 629, 31, 2838, # 6166 + 2697, 3810, 4474, 850, 949, 4475, 3987, 2955, 1732, 2088, 4191, 1496, 1852, 7737, 3988, 620, # 6182 + 3214, 981, 1242, 3679, 3360, 1619, 3680, 1643, 3293, 2139, 2452, 1970, 1719, 3463, 2168, 7738, # 6198 + 3215, 7739, 7740, 3361, 1828, 7741, 1277, 4476, 1565, 2047, 7742, 1636, 3568, 3093, 7743, 869, # 6214 + 2839, 655, 3811, 3812, 3094, 3989, 3000, 3813, 1310, 3569, 4477, 7744, 7745, 7746, 1733, 558, # 6230 + 4478, 3681, 335, 1549, 3045, 1756, 4192, 3682, 1945, 3464, 1829, 1291, 1192, 470, 2726, 2107, # 6246 + 2793, 913, 1054, 3990, 7747, 1027, 7748, 3046, 3991, 4479, 982, 2662, 3362, 3148, 3465, 3216, # 6262 + 3217, 1946, 2794, 7749, 571, 4480, 7750, 1830, 7751, 3570, 2583, 1523, 2424, 7752, 2089, 984, # 6278 + 4481, 3683, 1959, 7753, 3684, 852, 923, 2795, 3466, 3685, 969, 1519, 999, 2048, 2320, 1705, # 6294 + 7754, 3095, 615, 1662, 151, 597, 3992, 2405, 2321, 1049, 275, 4482, 3686, 4193, 568, 3687, # 6310 + 3571, 2480, 4194, 3688, 7755, 2425, 2270, 409, 3218, 7756, 1566, 2874, 3467, 1002, 769, 2840, # 6326 + 194, 2090, 3149, 3689, 2222, 3294, 4195, 628, 1505, 7757, 7758, 1763, 2177, 3001, 3993, 521, # 6342 + 1161, 2584, 1787, 2203, 2406, 4483, 3994, 1625, 4196, 4197, 412, 42, 3096, 464, 7759, 2632, # 6358 + 4484, 3363, 1760, 1571, 2875, 3468, 2530, 1219, 2204, 3814, 2633, 2140, 2368, 4485, 4486, 3295, # 6374 + 1651, 3364, 3572, 7760, 7761, 3573, 2481, 3469, 7762, 3690, 7763, 7764, 2271, 2091, 460, 7765, # 6390 + 4487, 7766, 3002, 962, 588, 3574, 289, 3219, 2634, 1116, 52, 7767, 3047, 1796, 7768, 7769, # 6406 + 7770, 1467, 7771, 1598, 1143, 3691, 4198, 1984, 1734, 1067, 4488, 1280, 3365, 465, 4489, 1572, # 6422 + 510, 7772, 1927, 2241, 1812, 1644, 3575, 7773, 4490, 3692, 7774, 7775, 2663, 1573, 1534, 7776, # 6438 + 7777, 4199, 536, 1807, 1761, 3470, 3815, 3150, 2635, 7778, 7779, 7780, 4491, 3471, 2915, 1911, # 6454 + 2796, 7781, 3296, 1122, 377, 3220, 7782, 360, 7783, 7784, 4200, 1529, 551, 7785, 2059, 3693, # 6470 + 1769, 2426, 7786, 2916, 4201, 3297, 3097, 2322, 2108, 2030, 4492, 1404, 136, 1468, 1479, 672, # 6486 + 1171, 3221, 2303, 271, 3151, 7787, 2762, 7788, 2049, 678, 2727, 865, 1947, 4493, 7789, 2013, # 6502 + 3995, 2956, 7790, 2728, 2223, 1397, 3048, 3694, 4494, 4495, 1735, 2917, 3366, 3576, 7791, 3816, # 6518 + 509, 2841, 2453, 2876, 3817, 7792, 7793, 3152, 3153, 4496, 4202, 2531, 4497, 2304, 1166, 1010, # 6534 + 552, 681, 1887, 7794, 7795, 2957, 2958, 3996, 1287, 1596, 1861, 3154, 358, 453, 736, 175, # 6550 + 478, 1117, 905, 1167, 1097, 7796, 1853, 1530, 7797, 1706, 7798, 2178, 3472, 2287, 3695, 3473, # 6566 + 3577, 4203, 2092, 4204, 7799, 3367, 1193, 2482, 4205, 1458, 2190, 2205, 1862, 1888, 1421, 3298, # 6582 + 2918, 3049, 2179, 3474, 595, 2122, 7800, 3997, 7801, 7802, 4206, 1707, 2636, 223, 3696, 1359, # 6598 + 751, 3098, 183, 3475, 7803, 2797, 3003, 419, 2369, 633, 704, 3818, 2389, 241, 7804, 7805, # 6614 + 7806, 838, 3004, 3697, 2272, 2763, 2454, 3819, 1938, 2050, 3998, 1309, 3099, 2242, 1181, 7807, # 6630 + 1136, 2206, 3820, 2370, 1446, 4207, 2305, 4498, 7808, 7809, 4208, 1055, 2605, 484, 3698, 7810, # 6646 + 3999, 625, 4209, 2273, 3368, 1499, 4210, 4000, 7811, 4001, 4211, 3222, 2274, 2275, 3476, 7812, # 6662 + 7813, 2764, 808, 2606, 3699, 3369, 4002, 4212, 3100, 2532, 526, 3370, 3821, 4213, 955, 7814, # 6678 + 1620, 4214, 2637, 2427, 7815, 1429, 3700, 1669, 1831, 994, 928, 7816, 3578, 1260, 7817, 7818, # 6694 + 7819, 1948, 2288, 741, 2919, 1626, 4215, 2729, 2455, 867, 1184, 362, 3371, 1392, 7820, 7821, # 6710 + 4003, 4216, 1770, 1736, 3223, 2920, 4499, 4500, 1928, 2698, 1459, 1158, 7822, 3050, 3372, 2877, # 6726 + 1292, 1929, 2506, 2842, 3701, 1985, 1187, 2071, 2014, 2607, 4217, 7823, 2566, 2507, 2169, 3702, # 6742 + 2483, 3299, 7824, 3703, 4501, 7825, 7826, 666, 1003, 3005, 1022, 3579, 4218, 7827, 4502, 1813, # 6758 + 2253, 574, 3822, 1603, 295, 1535, 705, 3823, 4219, 283, 858, 417, 7828, 7829, 3224, 4503, # 6774 + 4504, 3051, 1220, 1889, 1046, 2276, 2456, 4004, 1393, 1599, 689, 2567, 388, 4220, 7830, 2484, # 6790 + 802, 7831, 2798, 3824, 2060, 1405, 2254, 7832, 4505, 3825, 2109, 1052, 1345, 3225, 1585, 7833, # 6806 + 809, 7834, 7835, 7836, 575, 2730, 3477, 956, 1552, 1469, 1144, 2323, 7837, 2324, 1560, 2457, # 6822 + 3580, 3226, 4005, 616, 2207, 3155, 2180, 2289, 7838, 1832, 7839, 3478, 4506, 7840, 1319, 3704, # 6838 + 3705, 1211, 3581, 1023, 3227, 1293, 2799, 7841, 7842, 7843, 3826, 607, 2306, 3827, 762, 2878, # 6854 + 1439, 4221, 1360, 7844, 1485, 3052, 7845, 4507, 1038, 4222, 1450, 2061, 2638, 4223, 1379, 4508, # 6870 + 2585, 7846, 7847, 4224, 1352, 1414, 2325, 2921, 1172, 7848, 7849, 3828, 3829, 7850, 1797, 1451, # 6886 + 7851, 7852, 7853, 7854, 2922, 4006, 4007, 2485, 2346, 411, 4008, 4009, 3582, 3300, 3101, 4509, # 6902 + 1561, 2664, 1452, 4010, 1375, 7855, 7856, 47, 2959, 316, 7857, 1406, 1591, 2923, 3156, 7858, # 6918 + 1025, 2141, 3102, 3157, 354, 2731, 884, 2224, 4225, 2407, 508, 3706, 726, 3583, 996, 2428, # 6934 + 3584, 729, 7859, 392, 2191, 1453, 4011, 4510, 3707, 7860, 7861, 2458, 3585, 2608, 1675, 2800, # 6950 + 919, 2347, 2960, 2348, 1270, 4511, 4012, 73, 7862, 7863, 647, 7864, 3228, 2843, 2255, 1550, # 6966 + 1346, 3006, 7865, 1332, 883, 3479, 7866, 7867, 7868, 7869, 3301, 2765, 7870, 1212, 831, 1347, # 6982 + 4226, 4512, 2326, 3830, 1863, 3053, 720, 3831, 4513, 4514, 3832, 7871, 4227, 7872, 7873, 4515, # 6998 + 7874, 7875, 1798, 4516, 3708, 2609, 4517, 3586, 1645, 2371, 7876, 7877, 2924, 669, 2208, 2665, # 7014 + 2429, 7878, 2879, 7879, 7880, 1028, 3229, 7881, 4228, 2408, 7882, 2256, 1353, 7883, 7884, 4518, # 7030 + 3158, 518, 7885, 4013, 7886, 4229, 1960, 7887, 2142, 4230, 7888, 7889, 3007, 2349, 2350, 3833, # 7046 + 516, 1833, 1454, 4014, 2699, 4231, 4519, 2225, 2610, 1971, 1129, 3587, 7890, 2766, 7891, 2961, # 7062 + 1422, 577, 1470, 3008, 1524, 3373, 7892, 7893, 432, 4232, 3054, 3480, 7894, 2586, 1455, 2508, # 7078 + 2226, 1972, 1175, 7895, 1020, 2732, 4015, 3481, 4520, 7896, 2733, 7897, 1743, 1361, 3055, 3482, # 7094 + 2639, 4016, 4233, 4521, 2290, 895, 924, 4234, 2170, 331, 2243, 3056, 166, 1627, 3057, 1098, # 7110 + 7898, 1232, 2880, 2227, 3374, 4522, 657, 403, 1196, 2372, 542, 3709, 3375, 1600, 4235, 3483, # 7126 + 7899, 4523, 2767, 3230, 576, 530, 1362, 7900, 4524, 2533, 2666, 3710, 4017, 7901, 842, 3834, # 7142 + 7902, 2801, 2031, 1014, 4018, 213, 2700, 3376, 665, 621, 4236, 7903, 3711, 2925, 2430, 7904, # 7158 + 2431, 3302, 3588, 3377, 7905, 4237, 2534, 4238, 4525, 3589, 1682, 4239, 3484, 1380, 7906, 724, # 7174 + 2277, 600, 1670, 7907, 1337, 1233, 4526, 3103, 2244, 7908, 1621, 4527, 7909, 651, 4240, 7910, # 7190 + 1612, 4241, 2611, 7911, 2844, 7912, 2734, 2307, 3058, 7913, 716, 2459, 3059, 174, 1255, 2701, # 7206 + 4019, 3590, 548, 1320, 1398, 728, 4020, 1574, 7914, 1890, 1197, 3060, 4021, 7915, 3061, 3062, # 7222 + 3712, 3591, 3713, 747, 7916, 635, 4242, 4528, 7917, 7918, 7919, 4243, 7920, 7921, 4529, 7922, # 7238 + 3378, 4530, 2432, 451, 7923, 3714, 2535, 2072, 4244, 2735, 4245, 4022, 7924, 1764, 4531, 7925, # 7254 + 4246, 350, 7926, 2278, 2390, 2486, 7927, 4247, 4023, 2245, 1434, 4024, 488, 4532, 458, 4248, # 7270 + 4025, 3715, 771, 1330, 2391, 3835, 2568, 3159, 2159, 2409, 1553, 2667, 3160, 4249, 7928, 2487, # 7286 + 2881, 2612, 1720, 2702, 4250, 3379, 4533, 7929, 2536, 4251, 7930, 3231, 4252, 2768, 7931, 2015, # 7302 + 2736, 7932, 1155, 1017, 3716, 3836, 7933, 3303, 2308, 201, 1864, 4253, 1430, 7934, 4026, 7935, # 7318 + 7936, 7937, 7938, 7939, 4254, 1604, 7940, 414, 1865, 371, 2587, 4534, 4535, 3485, 2016, 3104, # 7334 + 4536, 1708, 960, 4255, 887, 389, 2171, 1536, 1663, 1721, 7941, 2228, 4027, 2351, 2926, 1580, # 7350 + 7942, 7943, 7944, 1744, 7945, 2537, 4537, 4538, 7946, 4539, 7947, 2073, 7948, 7949, 3592, 3380, # 7366 + 2882, 4256, 7950, 4257, 2640, 3381, 2802, 673, 2703, 2460, 709, 3486, 4028, 3593, 4258, 7951, # 7382 + 1148, 502, 634, 7952, 7953, 1204, 4540, 3594, 1575, 4541, 2613, 3717, 7954, 3718, 3105, 948, # 7398 + 3232, 121, 1745, 3837, 1110, 7955, 4259, 3063, 2509, 3009, 4029, 3719, 1151, 1771, 3838, 1488, # 7414 + 4030, 1986, 7956, 2433, 3487, 7957, 7958, 2093, 7959, 4260, 3839, 1213, 1407, 2803, 531, 2737, # 7430 + 2538, 3233, 1011, 1537, 7960, 2769, 4261, 3106, 1061, 7961, 3720, 3721, 1866, 2883, 7962, 2017, # 7446 + 120, 4262, 4263, 2062, 3595, 3234, 2309, 3840, 2668, 3382, 1954, 4542, 7963, 7964, 3488, 1047, # 7462 + 2704, 1266, 7965, 1368, 4543, 2845, 649, 3383, 3841, 2539, 2738, 1102, 2846, 2669, 7966, 7967, # 7478 + 1999, 7968, 1111, 3596, 2962, 7969, 2488, 3842, 3597, 2804, 1854, 3384, 3722, 7970, 7971, 3385, # 7494 + 2410, 2884, 3304, 3235, 3598, 7972, 2569, 7973, 3599, 2805, 4031, 1460, 856, 7974, 3600, 7975, # 7510 + 2885, 2963, 7976, 2886, 3843, 7977, 4264, 632, 2510, 875, 3844, 1697, 3845, 2291, 7978, 7979, # 7526 + 4544, 3010, 1239, 580, 4545, 4265, 7980, 914, 936, 2074, 1190, 4032, 1039, 2123, 7981, 7982, # 7542 + 7983, 3386, 1473, 7984, 1354, 4266, 3846, 7985, 2172, 3064, 4033, 915, 3305, 4267, 4268, 3306, # 7558 + 1605, 1834, 7986, 2739, 398, 3601, 4269, 3847, 4034, 328, 1912, 2847, 4035, 3848, 1331, 4270, # 7574 + 3011, 937, 4271, 7987, 3602, 4036, 4037, 3387, 2160, 4546, 3388, 524, 742, 538, 3065, 1012, # 7590 + 7988, 7989, 3849, 2461, 7990, 658, 1103, 225, 3850, 7991, 7992, 4547, 7993, 4548, 7994, 3236, # 7606 + 1243, 7995, 4038, 963, 2246, 4549, 7996, 2705, 3603, 3161, 7997, 7998, 2588, 2327, 7999, 4550, # 7622 + 8000, 8001, 8002, 3489, 3307, 957, 3389, 2540, 2032, 1930, 2927, 2462, 870, 2018, 3604, 1746, # 7638 + 2770, 2771, 2434, 2463, 8003, 3851, 8004, 3723, 3107, 3724, 3490, 3390, 3725, 8005, 1179, 3066, # 7654 + 8006, 3162, 2373, 4272, 3726, 2541, 3163, 3108, 2740, 4039, 8007, 3391, 1556, 2542, 2292, 977, # 7670 + 2887, 2033, 4040, 1205, 3392, 8008, 1765, 3393, 3164, 2124, 1271, 1689, 714, 4551, 3491, 8009, # 7686 + 2328, 3852, 533, 4273, 3605, 2181, 617, 8010, 2464, 3308, 3492, 2310, 8011, 8012, 3165, 8013, # 7702 + 8014, 3853, 1987, 618, 427, 2641, 3493, 3394, 8015, 8016, 1244, 1690, 8017, 2806, 4274, 4552, # 7718 + 8018, 3494, 8019, 8020, 2279, 1576, 473, 3606, 4275, 3395, 972, 8021, 3607, 8022, 3067, 8023, # 7734 + 8024, 4553, 4554, 8025, 3727, 4041, 4042, 8026, 153, 4555, 356, 8027, 1891, 2888, 4276, 2143, # 7750 + 408, 803, 2352, 8028, 3854, 8029, 4277, 1646, 2570, 2511, 4556, 4557, 3855, 8030, 3856, 4278, # 7766 + 8031, 2411, 3396, 752, 8032, 8033, 1961, 2964, 8034, 746, 3012, 2465, 8035, 4279, 3728, 698, # 7782 + 4558, 1892, 4280, 3608, 2543, 4559, 3609, 3857, 8036, 3166, 3397, 8037, 1823, 1302, 4043, 2706, # 7798 + 3858, 1973, 4281, 8038, 4282, 3167, 823, 1303, 1288, 1236, 2848, 3495, 4044, 3398, 774, 3859, # 7814 + 8039, 1581, 4560, 1304, 2849, 3860, 4561, 8040, 2435, 2161, 1083, 3237, 4283, 4045, 4284, 344, # 7830 + 1173, 288, 2311, 454, 1683, 8041, 8042, 1461, 4562, 4046, 2589, 8043, 8044, 4563, 985, 894, # 7846 + 8045, 3399, 3168, 8046, 1913, 2928, 3729, 1988, 8047, 2110, 1974, 8048, 4047, 8049, 2571, 1194, # 7862 + 425, 8050, 4564, 3169, 1245, 3730, 4285, 8051, 8052, 2850, 8053, 636, 4565, 1855, 3861, 760, # 7878 + 1799, 8054, 4286, 2209, 1508, 4566, 4048, 1893, 1684, 2293, 8055, 8056, 8057, 4287, 4288, 2210, # 7894 + 479, 8058, 8059, 832, 8060, 4049, 2489, 8061, 2965, 2490, 3731, 990, 3109, 627, 1814, 2642, # 7910 + 4289, 1582, 4290, 2125, 2111, 3496, 4567, 8062, 799, 4291, 3170, 8063, 4568, 2112, 1737, 3013, # 7926 + 1018, 543, 754, 4292, 3309, 1676, 4569, 4570, 4050, 8064, 1489, 8065, 3497, 8066, 2614, 2889, # 7942 + 4051, 8067, 8068, 2966, 8069, 8070, 8071, 8072, 3171, 4571, 4572, 2182, 1722, 8073, 3238, 3239, # 7958 + 1842, 3610, 1715, 481, 365, 1975, 1856, 8074, 8075, 1962, 2491, 4573, 8076, 2126, 3611, 3240, # 7974 + 433, 1894, 2063, 2075, 8077, 602, 2741, 8078, 8079, 8080, 8081, 8082, 3014, 1628, 3400, 8083, # 7990 + 3172, 4574, 4052, 2890, 4575, 2512, 8084, 2544, 2772, 8085, 8086, 8087, 3310, 4576, 2891, 8088, # 8006 + 4577, 8089, 2851, 4578, 4579, 1221, 2967, 4053, 2513, 8090, 8091, 8092, 1867, 1989, 8093, 8094, # 8022 + 8095, 1895, 8096, 8097, 4580, 1896, 4054, 318, 8098, 2094, 4055, 4293, 8099, 8100, 485, 8101, # 8038 + 938, 3862, 553, 2670, 116, 8102, 3863, 3612, 8103, 3498, 2671, 2773, 3401, 3311, 2807, 8104, # 8054 + 3613, 2929, 4056, 1747, 2930, 2968, 8105, 8106, 207, 8107, 8108, 2672, 4581, 2514, 8109, 3015, # 8070 + 890, 3614, 3864, 8110, 1877, 3732, 3402, 8111, 2183, 2353, 3403, 1652, 8112, 8113, 8114, 941, # 8086 + 2294, 208, 3499, 4057, 2019, 330, 4294, 3865, 2892, 2492, 3733, 4295, 8115, 8116, 8117, 8118, # 8102 +) +# fmt: on diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwprober.py new file mode 100644 index 0000000..a37ab18 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/euctwprober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import EUCTWDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import EUCTW_SM_MODEL + + +class EUCTWProber(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) + self.distribution_analyzer = EUCTWDistributionAnalysis() + self.reset() + + @property + def charset_name(self) -> str: + return "EUC-TW" + + @property + def language(self) -> str: + return "Taiwan" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312freq.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312freq.py new file mode 100644 index 0000000..b32bfc7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312freq.py @@ -0,0 +1,284 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# GB2312 most frequently used character table +# +# Char to FreqOrder table , from hz6763 + +# 512 --> 0.79 -- 0.79 +# 1024 --> 0.92 -- 0.13 +# 2048 --> 0.98 -- 0.06 +# 6768 --> 1.00 -- 0.02 +# +# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 +# Random Distribution Ration = 512 / (3755 - 512) = 0.157 +# +# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR + +GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 + +GB2312_TABLE_SIZE = 3760 + +# fmt: off +GB2312_CHAR_TO_FREQ_ORDER = ( +1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, +2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, +2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, + 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, +1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, +1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, + 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, +1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, +2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, +3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, + 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, +1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, + 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, +2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, + 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, +2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, +1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, +3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, + 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, +1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, + 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, +2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, +1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, +3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, +1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, +2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, +1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, + 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, +3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, +3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, + 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, +3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, + 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, +1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, +3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, +2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, +1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, + 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, +1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, +4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, + 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, +3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, +3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, + 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, +1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, +2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, +1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, +1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, + 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, +3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, +3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, +4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, + 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, +3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, +1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, +1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, +4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, + 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, + 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, +3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, +1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, + 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, +1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, +2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, + 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, + 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, + 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, +3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, +4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, +3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, + 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, +2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, +2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, +2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, + 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, +2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, + 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, + 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, + 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, +3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, +2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, +2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, +1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, + 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, +2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, + 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, + 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, +1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, +1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, + 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, + 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, +1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, +2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, +3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, +2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, +2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, +2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, +3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, +1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, +1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, +2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, +1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, +3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, +1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, +1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, +3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, + 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, +2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, +1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, +4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, +1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, +1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, +3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, +1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, + 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, + 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, +1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, + 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, +1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, +1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, + 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, +3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, +4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, +3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, +2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, +2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, +1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, +3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, +2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, +1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, +1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, + 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, +2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, +2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, +3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, +4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, +3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, + 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, +3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, +2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, +1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, + 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, + 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, +3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, +4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, +2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, +1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, +1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, + 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, +1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, +3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, + 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, + 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, +1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, + 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, +1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, + 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, +2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, + 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, +2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, +2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, +1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, +1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, +2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, + 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, +1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, +1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, +2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, +2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, +3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, +1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, +4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, + 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, + 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, +3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, +1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, + 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, +3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, +1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, +4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, +1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, +2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, +1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, + 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, +1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, +3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, + 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, +2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, + 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, +1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, +1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, +1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, +3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, +2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, +3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, +3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, +3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, + 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, +2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, + 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, +2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, + 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, +1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, + 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, + 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, +1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, +3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, +3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, +1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, +1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, +3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, +2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, +2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, +1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, +3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, + 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, +4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, +1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, +2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, +3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, +3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, +1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, + 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, + 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, +2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, + 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, +1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, + 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, +1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, +1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, +1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, +1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, +1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, + 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, + 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 +) +# fmt: on diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312prober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312prober.py new file mode 100644 index 0000000..d423e73 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/gb2312prober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import GB2312DistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import GB2312_SM_MODEL + + +class GB2312Prober(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) + self.distribution_analyzer = GB2312DistributionAnalysis() + self.reset() + + @property + def charset_name(self) -> str: + return "GB2312" + + @property + def language(self) -> str: + return "Chinese" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/hebrewprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/hebrewprober.py new file mode 100644 index 0000000..785d005 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/hebrewprober.py @@ -0,0 +1,316 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Shy Shalom +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Optional, Union + +from .charsetprober import CharSetProber +from .enums import ProbingState +from .sbcharsetprober import SingleByteCharSetProber + +# This prober doesn't actually recognize a language or a charset. +# It is a helper prober for the use of the Hebrew model probers + +### General ideas of the Hebrew charset recognition ### +# +# Four main charsets exist in Hebrew: +# "ISO-8859-8" - Visual Hebrew +# "windows-1255" - Logical Hebrew +# "ISO-8859-8-I" - Logical Hebrew +# "x-mac-hebrew" - ?? Logical Hebrew ?? +# +# Both "ISO" charsets use a completely identical set of code points, whereas +# "windows-1255" and "x-mac-hebrew" are two different proper supersets of +# these code points. windows-1255 defines additional characters in the range +# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific +# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. +# x-mac-hebrew defines similar additional code points but with a different +# mapping. +# +# As far as an average Hebrew text with no diacritics is concerned, all four +# charsets are identical with respect to code points. Meaning that for the +# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters +# (including final letters). +# +# The dominant difference between these charsets is their directionality. +# "Visual" directionality means that the text is ordered as if the renderer is +# not aware of a BIDI rendering algorithm. The renderer sees the text and +# draws it from left to right. The text itself when ordered naturally is read +# backwards. A buffer of Visual Hebrew generally looks like so: +# "[last word of first line spelled backwards] [whole line ordered backwards +# and spelled backwards] [first word of first line spelled backwards] +# [end of line] [last word of second line] ... etc' " +# adding punctuation marks, numbers and English text to visual text is +# naturally also "visual" and from left to right. +# +# "Logical" directionality means the text is ordered "naturally" according to +# the order it is read. It is the responsibility of the renderer to display +# the text from right to left. A BIDI algorithm is used to place general +# punctuation marks, numbers and English text in the text. +# +# Texts in x-mac-hebrew are almost impossible to find on the Internet. From +# what little evidence I could find, it seems that its general directionality +# is Logical. +# +# To sum up all of the above, the Hebrew probing mechanism knows about two +# charsets: +# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are +# backwards while line order is natural. For charset recognition purposes +# the line order is unimportant (In fact, for this implementation, even +# word order is unimportant). +# Logical Hebrew - "windows-1255" - normal, naturally ordered text. +# +# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be +# specifically identified. +# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew +# that contain special punctuation marks or diacritics is displayed with +# some unconverted characters showing as question marks. This problem might +# be corrected using another model prober for x-mac-hebrew. Due to the fact +# that x-mac-hebrew texts are so rare, writing another model prober isn't +# worth the effort and performance hit. +# +#### The Prober #### +# +# The prober is divided between two SBCharSetProbers and a HebrewProber, +# all of which are managed, created, fed data, inquired and deleted by the +# SBCSGroupProber. The two SBCharSetProbers identify that the text is in +# fact some kind of Hebrew, Logical or Visual. The final decision about which +# one is it is made by the HebrewProber by combining final-letter scores +# with the scores of the two SBCharSetProbers to produce a final answer. +# +# The SBCSGroupProber is responsible for stripping the original text of HTML +# tags, English characters, numbers, low-ASCII punctuation characters, spaces +# and new lines. It reduces any sequence of such characters to a single space. +# The buffer fed to each prober in the SBCS group prober is pure text in +# high-ASCII. +# The two SBCharSetProbers (model probers) share the same language model: +# Win1255Model. +# The first SBCharSetProber uses the model normally as any other +# SBCharSetProber does, to recognize windows-1255, upon which this model was +# built. The second SBCharSetProber is told to make the pair-of-letter +# lookup in the language model backwards. This in practice exactly simulates +# a visual Hebrew model using the windows-1255 logical Hebrew model. +# +# The HebrewProber is not using any language model. All it does is look for +# final-letter evidence suggesting the text is either logical Hebrew or visual +# Hebrew. Disjointed from the model probers, the results of the HebrewProber +# alone are meaningless. HebrewProber always returns 0.00 as confidence +# since it never identifies a charset by itself. Instead, the pointer to the +# HebrewProber is passed to the model probers as a helper "Name Prober". +# When the Group prober receives a positive identification from any prober, +# it asks for the name of the charset identified. If the prober queried is a +# Hebrew model prober, the model prober forwards the call to the +# HebrewProber to make the final decision. In the HebrewProber, the +# decision is made according to the final-letters scores maintained and Both +# model probers scores. The answer is returned in the form of the name of the +# charset identified, either "windows-1255" or "ISO-8859-8". + + +class HebrewProber(CharSetProber): + SPACE = 0x20 + # windows-1255 / ISO-8859-8 code points of interest + FINAL_KAF = 0xEA + NORMAL_KAF = 0xEB + FINAL_MEM = 0xED + NORMAL_MEM = 0xEE + FINAL_NUN = 0xEF + NORMAL_NUN = 0xF0 + FINAL_PE = 0xF3 + NORMAL_PE = 0xF4 + FINAL_TSADI = 0xF5 + NORMAL_TSADI = 0xF6 + + # Minimum Visual vs Logical final letter score difference. + # If the difference is below this, don't rely solely on the final letter score + # distance. + MIN_FINAL_CHAR_DISTANCE = 5 + + # Minimum Visual vs Logical model score difference. + # If the difference is below this, don't rely at all on the model score + # distance. + MIN_MODEL_DISTANCE = 0.01 + + VISUAL_HEBREW_NAME = "ISO-8859-8" + LOGICAL_HEBREW_NAME = "windows-1255" + + def __init__(self) -> None: + super().__init__() + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 + self._prev = self.SPACE + self._before_prev = self.SPACE + self._logical_prober: Optional[SingleByteCharSetProber] = None + self._visual_prober: Optional[SingleByteCharSetProber] = None + self.reset() + + def reset(self) -> None: + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 + # The two last characters seen in the previous buffer, + # mPrev and mBeforePrev are initialized to space in order to simulate + # a word delimiter at the beginning of the data + self._prev = self.SPACE + self._before_prev = self.SPACE + # These probers are owned by the group prober. + + def set_model_probers( + self, + logical_prober: SingleByteCharSetProber, + visual_prober: SingleByteCharSetProber, + ) -> None: + self._logical_prober = logical_prober + self._visual_prober = visual_prober + + def is_final(self, c: int) -> bool: + return c in [ + self.FINAL_KAF, + self.FINAL_MEM, + self.FINAL_NUN, + self.FINAL_PE, + self.FINAL_TSADI, + ] + + def is_non_final(self, c: int) -> bool: + # The normal Tsadi is not a good Non-Final letter due to words like + # 'lechotet' (to chat) containing an apostrophe after the tsadi. This + # apostrophe is converted to a space in FilterWithoutEnglishLetters + # causing the Non-Final tsadi to appear at an end of a word even + # though this is not the case in the original text. + # The letters Pe and Kaf rarely display a related behavior of not being + # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' + # for example legally end with a Non-Final Pe or Kaf. However, the + # benefit of these letters as Non-Final letters outweighs the damage + # since these words are quite rare. + return c in [self.NORMAL_KAF, self.NORMAL_MEM, self.NORMAL_NUN, self.NORMAL_PE] + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + # Final letter analysis for logical-visual decision. + # Look for evidence that the received buffer is either logical Hebrew + # or visual Hebrew. + # The following cases are checked: + # 1) A word longer than 1 letter, ending with a final letter. This is + # an indication that the text is laid out "naturally" since the + # final letter really appears at the end. +1 for logical score. + # 2) A word longer than 1 letter, ending with a Non-Final letter. In + # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, + # should not end with the Non-Final form of that letter. Exceptions + # to this rule are mentioned above in isNonFinal(). This is an + # indication that the text is laid out backwards. +1 for visual + # score + # 3) A word longer than 1 letter, starting with a final letter. Final + # letters should not appear at the beginning of a word. This is an + # indication that the text is laid out backwards. +1 for visual + # score. + # + # The visual score and logical score are accumulated throughout the + # text and are finally checked against each other in GetCharSetName(). + # No checking for final letters in the middle of words is done since + # that case is not an indication for either Logical or Visual text. + # + # We automatically filter out all 7-bit characters (replace them with + # spaces) so the word boundary detection works properly. [MAP] + + if self.state == ProbingState.NOT_ME: + # Both model probers say it's not them. No reason to continue. + return ProbingState.NOT_ME + + byte_str = self.filter_high_byte_only(byte_str) + + for cur in byte_str: + if cur == self.SPACE: + # We stand on a space - a word just ended + if self._before_prev != self.SPACE: + # next-to-last char was not a space so self._prev is not a + # 1 letter word + if self.is_final(self._prev): + # case (1) [-2:not space][-1:final letter][cur:space] + self._final_char_logical_score += 1 + elif self.is_non_final(self._prev): + # case (2) [-2:not space][-1:Non-Final letter][ + # cur:space] + self._final_char_visual_score += 1 + else: + # Not standing on a space + if ( + (self._before_prev == self.SPACE) + and (self.is_final(self._prev)) + and (cur != self.SPACE) + ): + # case (3) [-2:space][-1:final letter][cur:not space] + self._final_char_visual_score += 1 + self._before_prev = self._prev + self._prev = cur + + # Forever detecting, till the end or until both model probers return + # ProbingState.NOT_ME (handled above) + return ProbingState.DETECTING + + @property + def charset_name(self) -> str: + assert self._logical_prober is not None + assert self._visual_prober is not None + + # Make the decision: is it Logical or Visual? + # If the final letter score distance is dominant enough, rely on it. + finalsub = self._final_char_logical_score - self._final_char_visual_score + if finalsub >= self.MIN_FINAL_CHAR_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # It's not dominant enough, try to rely on the model scores instead. + modelsub = ( + self._logical_prober.get_confidence() - self._visual_prober.get_confidence() + ) + if modelsub > self.MIN_MODEL_DISTANCE: + return self.LOGICAL_HEBREW_NAME + if modelsub < -self.MIN_MODEL_DISTANCE: + return self.VISUAL_HEBREW_NAME + + # Still no good, back to final letter distance, maybe it'll save the + # day. + if finalsub < 0.0: + return self.VISUAL_HEBREW_NAME + + # (finalsub > 0 - Logical) or (don't know what to do) default to + # Logical. + return self.LOGICAL_HEBREW_NAME + + @property + def language(self) -> str: + return "Hebrew" + + @property + def state(self) -> ProbingState: + assert self._logical_prober is not None + assert self._visual_prober is not None + + # Remain active as long as any of the model probers are active. + if (self._logical_prober.state == ProbingState.NOT_ME) and ( + self._visual_prober.state == ProbingState.NOT_ME + ): + return ProbingState.NOT_ME + return ProbingState.DETECTING diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/jisfreq.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/jisfreq.py new file mode 100644 index 0000000..3293576 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/jisfreq.py @@ -0,0 +1,325 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# Sampling from about 20M text materials include literature and computer technology +# +# Japanese frequency table, applied to both S-JIS and EUC-JP +# They are sorted in order. + +# 128 --> 0.77094 +# 256 --> 0.85710 +# 512 --> 0.92635 +# 1024 --> 0.97130 +# 2048 --> 0.99431 +# +# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 +# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 +# +# Typical Distribution Ratio, 25% of IDR + +JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 + +# Char to FreqOrder table , +JIS_TABLE_SIZE = 4368 + +# fmt: off +JIS_CHAR_TO_FREQ_ORDER = ( + 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 +3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 +1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 +2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 +2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 +5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 +1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 +5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 +5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 +5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 +5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 +5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 +5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 +1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 +1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 +1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 +2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 +3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 +3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 + 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 + 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 +1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 + 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 +5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 + 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 + 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 + 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 + 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 + 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 +5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 +5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 +5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 +4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 +5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 +5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 +5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 +5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 +5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 +5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 +5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 +5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 +5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 +3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 +5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 +5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 +5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 +5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 +5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 +5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 +5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 +5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 +5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 +5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 +5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 +5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 +5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 +5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 +5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 +5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 +5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 +5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 +5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 +5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 +5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 +5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 +5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 +5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 +5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 +5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 +5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 +5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 +5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 +5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 +5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 +5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 +5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 +5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 +5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 +5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 +5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 +5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 +6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 +6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 +6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 +6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 +6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 +6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 +6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 +6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 +4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 + 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 + 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 +1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 +1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 + 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 +3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 +3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 + 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 +3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 +3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 + 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 +2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 + 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 +3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 +1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 + 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 +1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 + 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 +2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 +2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 +2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 +2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 +1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 +1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 +1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 +1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 +2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 +1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 +2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 +1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 +1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 +1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 +1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 +1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 +1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 + 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 + 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 +1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 +2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 +2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 +2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 +3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 +3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 + 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 +3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 +1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 + 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 +2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 +1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 + 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 +3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 +4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 +2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 +1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 +2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 +1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 + 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 + 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 +1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 +2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 +2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 +2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 +3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 +1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 +2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 + 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 + 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 + 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 +1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 +2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 + 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 +1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 +1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 + 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 +1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 +1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 +1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 + 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 +2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 + 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 +2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 +3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 +2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 +1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 +6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 +1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 +2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 +1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 + 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 + 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 +3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 +3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 +1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 +1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 +1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 +1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 + 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 + 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 +2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 + 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 +3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 +2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 + 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 +1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 +2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 + 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 +1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 + 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 +4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 +2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 +1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 + 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 +1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 +2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 + 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 +6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 +1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 +1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 +2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 +3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 + 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 +3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 +1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 + 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 +1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 + 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 +3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 + 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 +2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 + 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 +4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 +2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 +1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 +1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 +1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 + 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 +1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 +3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 +1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 +3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 + 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 + 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 + 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 +2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 +1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 + 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 +1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 + 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 +1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 + 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 + 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 + 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 +1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 +1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 +2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 +4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 + 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 +1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 + 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 +1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 +3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 +1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 +2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 +2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 +1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 +1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 +2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 + 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 +2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 +1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 +1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 +1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 +1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 +3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 +2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 +2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 + 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 +3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 +3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 +1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 +2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 +1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 +2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 +) +# fmt: on diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabfreq.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabfreq.py new file mode 100644 index 0000000..c129699 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabfreq.py @@ -0,0 +1,2382 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +# The frequency data itself is the same as euc-kr. +# This is just a mapping table to euc-kr. + +JOHAB_TO_EUCKR_ORDER_TABLE = { + 0x8861: 0, + 0x8862: 1, + 0x8865: 2, + 0x8868: 3, + 0x8869: 4, + 0x886A: 5, + 0x886B: 6, + 0x8871: 7, + 0x8873: 8, + 0x8874: 9, + 0x8875: 10, + 0x8876: 11, + 0x8877: 12, + 0x8878: 13, + 0x8879: 14, + 0x887B: 15, + 0x887C: 16, + 0x887D: 17, + 0x8881: 18, + 0x8882: 19, + 0x8885: 20, + 0x8889: 21, + 0x8891: 22, + 0x8893: 23, + 0x8895: 24, + 0x8896: 25, + 0x8897: 26, + 0x88A1: 27, + 0x88A2: 28, + 0x88A5: 29, + 0x88A9: 30, + 0x88B5: 31, + 0x88B7: 32, + 0x88C1: 33, + 0x88C5: 34, + 0x88C9: 35, + 0x88E1: 36, + 0x88E2: 37, + 0x88E5: 38, + 0x88E8: 39, + 0x88E9: 40, + 0x88EB: 41, + 0x88F1: 42, + 0x88F3: 43, + 0x88F5: 44, + 0x88F6: 45, + 0x88F7: 46, + 0x88F8: 47, + 0x88FB: 48, + 0x88FC: 49, + 0x88FD: 50, + 0x8941: 51, + 0x8945: 52, + 0x8949: 53, + 0x8951: 54, + 0x8953: 55, + 0x8955: 56, + 0x8956: 57, + 0x8957: 58, + 0x8961: 59, + 0x8962: 60, + 0x8963: 61, + 0x8965: 62, + 0x8968: 63, + 0x8969: 64, + 0x8971: 65, + 0x8973: 66, + 0x8975: 67, + 0x8976: 68, + 0x8977: 69, + 0x897B: 70, + 0x8981: 71, + 0x8985: 72, + 0x8989: 73, + 0x8993: 74, + 0x8995: 75, + 0x89A1: 76, + 0x89A2: 77, + 0x89A5: 78, + 0x89A8: 79, + 0x89A9: 80, + 0x89AB: 81, + 0x89AD: 82, + 0x89B0: 83, + 0x89B1: 84, + 0x89B3: 85, + 0x89B5: 86, + 0x89B7: 87, + 0x89B8: 88, + 0x89C1: 89, + 0x89C2: 90, + 0x89C5: 91, + 0x89C9: 92, + 0x89CB: 93, + 0x89D1: 94, + 0x89D3: 95, + 0x89D5: 96, + 0x89D7: 97, + 0x89E1: 98, + 0x89E5: 99, + 0x89E9: 100, + 0x89F3: 101, + 0x89F6: 102, + 0x89F7: 103, + 0x8A41: 104, + 0x8A42: 105, + 0x8A45: 106, + 0x8A49: 107, + 0x8A51: 108, + 0x8A53: 109, + 0x8A55: 110, + 0x8A57: 111, + 0x8A61: 112, + 0x8A65: 113, + 0x8A69: 114, + 0x8A73: 115, + 0x8A75: 116, + 0x8A81: 117, + 0x8A82: 118, + 0x8A85: 119, + 0x8A88: 120, + 0x8A89: 121, + 0x8A8A: 122, + 0x8A8B: 123, + 0x8A90: 124, + 0x8A91: 125, + 0x8A93: 126, + 0x8A95: 127, + 0x8A97: 128, + 0x8A98: 129, + 0x8AA1: 130, + 0x8AA2: 131, + 0x8AA5: 132, + 0x8AA9: 133, + 0x8AB6: 134, + 0x8AB7: 135, + 0x8AC1: 136, + 0x8AD5: 137, + 0x8AE1: 138, + 0x8AE2: 139, + 0x8AE5: 140, + 0x8AE9: 141, + 0x8AF1: 142, + 0x8AF3: 143, + 0x8AF5: 144, + 0x8B41: 145, + 0x8B45: 146, + 0x8B49: 147, + 0x8B61: 148, + 0x8B62: 149, + 0x8B65: 150, + 0x8B68: 151, + 0x8B69: 152, + 0x8B6A: 153, + 0x8B71: 154, + 0x8B73: 155, + 0x8B75: 156, + 0x8B77: 157, + 0x8B81: 158, + 0x8BA1: 159, + 0x8BA2: 160, + 0x8BA5: 161, + 0x8BA8: 162, + 0x8BA9: 163, + 0x8BAB: 164, + 0x8BB1: 165, + 0x8BB3: 166, + 0x8BB5: 167, + 0x8BB7: 168, + 0x8BB8: 169, + 0x8BBC: 170, + 0x8C61: 171, + 0x8C62: 172, + 0x8C63: 173, + 0x8C65: 174, + 0x8C69: 175, + 0x8C6B: 176, + 0x8C71: 177, + 0x8C73: 178, + 0x8C75: 179, + 0x8C76: 180, + 0x8C77: 181, + 0x8C7B: 182, + 0x8C81: 183, + 0x8C82: 184, + 0x8C85: 185, + 0x8C89: 186, + 0x8C91: 187, + 0x8C93: 188, + 0x8C95: 189, + 0x8C96: 190, + 0x8C97: 191, + 0x8CA1: 192, + 0x8CA2: 193, + 0x8CA9: 194, + 0x8CE1: 195, + 0x8CE2: 196, + 0x8CE3: 197, + 0x8CE5: 198, + 0x8CE9: 199, + 0x8CF1: 200, + 0x8CF3: 201, + 0x8CF5: 202, + 0x8CF6: 203, + 0x8CF7: 204, + 0x8D41: 205, + 0x8D42: 206, + 0x8D45: 207, + 0x8D51: 208, + 0x8D55: 209, + 0x8D57: 210, + 0x8D61: 211, + 0x8D65: 212, + 0x8D69: 213, + 0x8D75: 214, + 0x8D76: 215, + 0x8D7B: 216, + 0x8D81: 217, + 0x8DA1: 218, + 0x8DA2: 219, + 0x8DA5: 220, + 0x8DA7: 221, + 0x8DA9: 222, + 0x8DB1: 223, + 0x8DB3: 224, + 0x8DB5: 225, + 0x8DB7: 226, + 0x8DB8: 227, + 0x8DB9: 228, + 0x8DC1: 229, + 0x8DC2: 230, + 0x8DC9: 231, + 0x8DD6: 232, + 0x8DD7: 233, + 0x8DE1: 234, + 0x8DE2: 235, + 0x8DF7: 236, + 0x8E41: 237, + 0x8E45: 238, + 0x8E49: 239, + 0x8E51: 240, + 0x8E53: 241, + 0x8E57: 242, + 0x8E61: 243, + 0x8E81: 244, + 0x8E82: 245, + 0x8E85: 246, + 0x8E89: 247, + 0x8E90: 248, + 0x8E91: 249, + 0x8E93: 250, + 0x8E95: 251, + 0x8E97: 252, + 0x8E98: 253, + 0x8EA1: 254, + 0x8EA9: 255, + 0x8EB6: 256, + 0x8EB7: 257, + 0x8EC1: 258, + 0x8EC2: 259, + 0x8EC5: 260, + 0x8EC9: 261, + 0x8ED1: 262, + 0x8ED3: 263, + 0x8ED6: 264, + 0x8EE1: 265, + 0x8EE5: 266, + 0x8EE9: 267, + 0x8EF1: 268, + 0x8EF3: 269, + 0x8F41: 270, + 0x8F61: 271, + 0x8F62: 272, + 0x8F65: 273, + 0x8F67: 274, + 0x8F69: 275, + 0x8F6B: 276, + 0x8F70: 277, + 0x8F71: 278, + 0x8F73: 279, + 0x8F75: 280, + 0x8F77: 281, + 0x8F7B: 282, + 0x8FA1: 283, + 0x8FA2: 284, + 0x8FA5: 285, + 0x8FA9: 286, + 0x8FB1: 287, + 0x8FB3: 288, + 0x8FB5: 289, + 0x8FB7: 290, + 0x9061: 291, + 0x9062: 292, + 0x9063: 293, + 0x9065: 294, + 0x9068: 295, + 0x9069: 296, + 0x906A: 297, + 0x906B: 298, + 0x9071: 299, + 0x9073: 300, + 0x9075: 301, + 0x9076: 302, + 0x9077: 303, + 0x9078: 304, + 0x9079: 305, + 0x907B: 306, + 0x907D: 307, + 0x9081: 308, + 0x9082: 309, + 0x9085: 310, + 0x9089: 311, + 0x9091: 312, + 0x9093: 313, + 0x9095: 314, + 0x9096: 315, + 0x9097: 316, + 0x90A1: 317, + 0x90A2: 318, + 0x90A5: 319, + 0x90A9: 320, + 0x90B1: 321, + 0x90B7: 322, + 0x90E1: 323, + 0x90E2: 324, + 0x90E4: 325, + 0x90E5: 326, + 0x90E9: 327, + 0x90EB: 328, + 0x90EC: 329, + 0x90F1: 330, + 0x90F3: 331, + 0x90F5: 332, + 0x90F6: 333, + 0x90F7: 334, + 0x90FD: 335, + 0x9141: 336, + 0x9142: 337, + 0x9145: 338, + 0x9149: 339, + 0x9151: 340, + 0x9153: 341, + 0x9155: 342, + 0x9156: 343, + 0x9157: 344, + 0x9161: 345, + 0x9162: 346, + 0x9165: 347, + 0x9169: 348, + 0x9171: 349, + 0x9173: 350, + 0x9176: 351, + 0x9177: 352, + 0x917A: 353, + 0x9181: 354, + 0x9185: 355, + 0x91A1: 356, + 0x91A2: 357, + 0x91A5: 358, + 0x91A9: 359, + 0x91AB: 360, + 0x91B1: 361, + 0x91B3: 362, + 0x91B5: 363, + 0x91B7: 364, + 0x91BC: 365, + 0x91BD: 366, + 0x91C1: 367, + 0x91C5: 368, + 0x91C9: 369, + 0x91D6: 370, + 0x9241: 371, + 0x9245: 372, + 0x9249: 373, + 0x9251: 374, + 0x9253: 375, + 0x9255: 376, + 0x9261: 377, + 0x9262: 378, + 0x9265: 379, + 0x9269: 380, + 0x9273: 381, + 0x9275: 382, + 0x9277: 383, + 0x9281: 384, + 0x9282: 385, + 0x9285: 386, + 0x9288: 387, + 0x9289: 388, + 0x9291: 389, + 0x9293: 390, + 0x9295: 391, + 0x9297: 392, + 0x92A1: 393, + 0x92B6: 394, + 0x92C1: 395, + 0x92E1: 396, + 0x92E5: 397, + 0x92E9: 398, + 0x92F1: 399, + 0x92F3: 400, + 0x9341: 401, + 0x9342: 402, + 0x9349: 403, + 0x9351: 404, + 0x9353: 405, + 0x9357: 406, + 0x9361: 407, + 0x9362: 408, + 0x9365: 409, + 0x9369: 410, + 0x936A: 411, + 0x936B: 412, + 0x9371: 413, + 0x9373: 414, + 0x9375: 415, + 0x9377: 416, + 0x9378: 417, + 0x937C: 418, + 0x9381: 419, + 0x9385: 420, + 0x9389: 421, + 0x93A1: 422, + 0x93A2: 423, + 0x93A5: 424, + 0x93A9: 425, + 0x93AB: 426, + 0x93B1: 427, + 0x93B3: 428, + 0x93B5: 429, + 0x93B7: 430, + 0x93BC: 431, + 0x9461: 432, + 0x9462: 433, + 0x9463: 434, + 0x9465: 435, + 0x9468: 436, + 0x9469: 437, + 0x946A: 438, + 0x946B: 439, + 0x946C: 440, + 0x9470: 441, + 0x9471: 442, + 0x9473: 443, + 0x9475: 444, + 0x9476: 445, + 0x9477: 446, + 0x9478: 447, + 0x9479: 448, + 0x947D: 449, + 0x9481: 450, + 0x9482: 451, + 0x9485: 452, + 0x9489: 453, + 0x9491: 454, + 0x9493: 455, + 0x9495: 456, + 0x9496: 457, + 0x9497: 458, + 0x94A1: 459, + 0x94E1: 460, + 0x94E2: 461, + 0x94E3: 462, + 0x94E5: 463, + 0x94E8: 464, + 0x94E9: 465, + 0x94EB: 466, + 0x94EC: 467, + 0x94F1: 468, + 0x94F3: 469, + 0x94F5: 470, + 0x94F7: 471, + 0x94F9: 472, + 0x94FC: 473, + 0x9541: 474, + 0x9542: 475, + 0x9545: 476, + 0x9549: 477, + 0x9551: 478, + 0x9553: 479, + 0x9555: 480, + 0x9556: 481, + 0x9557: 482, + 0x9561: 483, + 0x9565: 484, + 0x9569: 485, + 0x9576: 486, + 0x9577: 487, + 0x9581: 488, + 0x9585: 489, + 0x95A1: 490, + 0x95A2: 491, + 0x95A5: 492, + 0x95A8: 493, + 0x95A9: 494, + 0x95AB: 495, + 0x95AD: 496, + 0x95B1: 497, + 0x95B3: 498, + 0x95B5: 499, + 0x95B7: 500, + 0x95B9: 501, + 0x95BB: 502, + 0x95C1: 503, + 0x95C5: 504, + 0x95C9: 505, + 0x95E1: 506, + 0x95F6: 507, + 0x9641: 508, + 0x9645: 509, + 0x9649: 510, + 0x9651: 511, + 0x9653: 512, + 0x9655: 513, + 0x9661: 514, + 0x9681: 515, + 0x9682: 516, + 0x9685: 517, + 0x9689: 518, + 0x9691: 519, + 0x9693: 520, + 0x9695: 521, + 0x9697: 522, + 0x96A1: 523, + 0x96B6: 524, + 0x96C1: 525, + 0x96D7: 526, + 0x96E1: 527, + 0x96E5: 528, + 0x96E9: 529, + 0x96F3: 530, + 0x96F5: 531, + 0x96F7: 532, + 0x9741: 533, + 0x9745: 534, + 0x9749: 535, + 0x9751: 536, + 0x9757: 537, + 0x9761: 538, + 0x9762: 539, + 0x9765: 540, + 0x9768: 541, + 0x9769: 542, + 0x976B: 543, + 0x9771: 544, + 0x9773: 545, + 0x9775: 546, + 0x9777: 547, + 0x9781: 548, + 0x97A1: 549, + 0x97A2: 550, + 0x97A5: 551, + 0x97A8: 552, + 0x97A9: 553, + 0x97B1: 554, + 0x97B3: 555, + 0x97B5: 556, + 0x97B6: 557, + 0x97B7: 558, + 0x97B8: 559, + 0x9861: 560, + 0x9862: 561, + 0x9865: 562, + 0x9869: 563, + 0x9871: 564, + 0x9873: 565, + 0x9875: 566, + 0x9876: 567, + 0x9877: 568, + 0x987D: 569, + 0x9881: 570, + 0x9882: 571, + 0x9885: 572, + 0x9889: 573, + 0x9891: 574, + 0x9893: 575, + 0x9895: 576, + 0x9896: 577, + 0x9897: 578, + 0x98E1: 579, + 0x98E2: 580, + 0x98E5: 581, + 0x98E9: 582, + 0x98EB: 583, + 0x98EC: 584, + 0x98F1: 585, + 0x98F3: 586, + 0x98F5: 587, + 0x98F6: 588, + 0x98F7: 589, + 0x98FD: 590, + 0x9941: 591, + 0x9942: 592, + 0x9945: 593, + 0x9949: 594, + 0x9951: 595, + 0x9953: 596, + 0x9955: 597, + 0x9956: 598, + 0x9957: 599, + 0x9961: 600, + 0x9976: 601, + 0x99A1: 602, + 0x99A2: 603, + 0x99A5: 604, + 0x99A9: 605, + 0x99B7: 606, + 0x99C1: 607, + 0x99C9: 608, + 0x99E1: 609, + 0x9A41: 610, + 0x9A45: 611, + 0x9A81: 612, + 0x9A82: 613, + 0x9A85: 614, + 0x9A89: 615, + 0x9A90: 616, + 0x9A91: 617, + 0x9A97: 618, + 0x9AC1: 619, + 0x9AE1: 620, + 0x9AE5: 621, + 0x9AE9: 622, + 0x9AF1: 623, + 0x9AF3: 624, + 0x9AF7: 625, + 0x9B61: 626, + 0x9B62: 627, + 0x9B65: 628, + 0x9B68: 629, + 0x9B69: 630, + 0x9B71: 631, + 0x9B73: 632, + 0x9B75: 633, + 0x9B81: 634, + 0x9B85: 635, + 0x9B89: 636, + 0x9B91: 637, + 0x9B93: 638, + 0x9BA1: 639, + 0x9BA5: 640, + 0x9BA9: 641, + 0x9BB1: 642, + 0x9BB3: 643, + 0x9BB5: 644, + 0x9BB7: 645, + 0x9C61: 646, + 0x9C62: 647, + 0x9C65: 648, + 0x9C69: 649, + 0x9C71: 650, + 0x9C73: 651, + 0x9C75: 652, + 0x9C76: 653, + 0x9C77: 654, + 0x9C78: 655, + 0x9C7C: 656, + 0x9C7D: 657, + 0x9C81: 658, + 0x9C82: 659, + 0x9C85: 660, + 0x9C89: 661, + 0x9C91: 662, + 0x9C93: 663, + 0x9C95: 664, + 0x9C96: 665, + 0x9C97: 666, + 0x9CA1: 667, + 0x9CA2: 668, + 0x9CA5: 669, + 0x9CB5: 670, + 0x9CB7: 671, + 0x9CE1: 672, + 0x9CE2: 673, + 0x9CE5: 674, + 0x9CE9: 675, + 0x9CF1: 676, + 0x9CF3: 677, + 0x9CF5: 678, + 0x9CF6: 679, + 0x9CF7: 680, + 0x9CFD: 681, + 0x9D41: 682, + 0x9D42: 683, + 0x9D45: 684, + 0x9D49: 685, + 0x9D51: 686, + 0x9D53: 687, + 0x9D55: 688, + 0x9D57: 689, + 0x9D61: 690, + 0x9D62: 691, + 0x9D65: 692, + 0x9D69: 693, + 0x9D71: 694, + 0x9D73: 695, + 0x9D75: 696, + 0x9D76: 697, + 0x9D77: 698, + 0x9D81: 699, + 0x9D85: 700, + 0x9D93: 701, + 0x9D95: 702, + 0x9DA1: 703, + 0x9DA2: 704, + 0x9DA5: 705, + 0x9DA9: 706, + 0x9DB1: 707, + 0x9DB3: 708, + 0x9DB5: 709, + 0x9DB7: 710, + 0x9DC1: 711, + 0x9DC5: 712, + 0x9DD7: 713, + 0x9DF6: 714, + 0x9E41: 715, + 0x9E45: 716, + 0x9E49: 717, + 0x9E51: 718, + 0x9E53: 719, + 0x9E55: 720, + 0x9E57: 721, + 0x9E61: 722, + 0x9E65: 723, + 0x9E69: 724, + 0x9E73: 725, + 0x9E75: 726, + 0x9E77: 727, + 0x9E81: 728, + 0x9E82: 729, + 0x9E85: 730, + 0x9E89: 731, + 0x9E91: 732, + 0x9E93: 733, + 0x9E95: 734, + 0x9E97: 735, + 0x9EA1: 736, + 0x9EB6: 737, + 0x9EC1: 738, + 0x9EE1: 739, + 0x9EE2: 740, + 0x9EE5: 741, + 0x9EE9: 742, + 0x9EF1: 743, + 0x9EF5: 744, + 0x9EF7: 745, + 0x9F41: 746, + 0x9F42: 747, + 0x9F45: 748, + 0x9F49: 749, + 0x9F51: 750, + 0x9F53: 751, + 0x9F55: 752, + 0x9F57: 753, + 0x9F61: 754, + 0x9F62: 755, + 0x9F65: 756, + 0x9F69: 757, + 0x9F71: 758, + 0x9F73: 759, + 0x9F75: 760, + 0x9F77: 761, + 0x9F78: 762, + 0x9F7B: 763, + 0x9F7C: 764, + 0x9FA1: 765, + 0x9FA2: 766, + 0x9FA5: 767, + 0x9FA9: 768, + 0x9FB1: 769, + 0x9FB3: 770, + 0x9FB5: 771, + 0x9FB7: 772, + 0xA061: 773, + 0xA062: 774, + 0xA065: 775, + 0xA067: 776, + 0xA068: 777, + 0xA069: 778, + 0xA06A: 779, + 0xA06B: 780, + 0xA071: 781, + 0xA073: 782, + 0xA075: 783, + 0xA077: 784, + 0xA078: 785, + 0xA07B: 786, + 0xA07D: 787, + 0xA081: 788, + 0xA082: 789, + 0xA085: 790, + 0xA089: 791, + 0xA091: 792, + 0xA093: 793, + 0xA095: 794, + 0xA096: 795, + 0xA097: 796, + 0xA098: 797, + 0xA0A1: 798, + 0xA0A2: 799, + 0xA0A9: 800, + 0xA0B7: 801, + 0xA0E1: 802, + 0xA0E2: 803, + 0xA0E5: 804, + 0xA0E9: 805, + 0xA0EB: 806, + 0xA0F1: 807, + 0xA0F3: 808, + 0xA0F5: 809, + 0xA0F7: 810, + 0xA0F8: 811, + 0xA0FD: 812, + 0xA141: 813, + 0xA142: 814, + 0xA145: 815, + 0xA149: 816, + 0xA151: 817, + 0xA153: 818, + 0xA155: 819, + 0xA156: 820, + 0xA157: 821, + 0xA161: 822, + 0xA162: 823, + 0xA165: 824, + 0xA169: 825, + 0xA175: 826, + 0xA176: 827, + 0xA177: 828, + 0xA179: 829, + 0xA181: 830, + 0xA1A1: 831, + 0xA1A2: 832, + 0xA1A4: 833, + 0xA1A5: 834, + 0xA1A9: 835, + 0xA1AB: 836, + 0xA1B1: 837, + 0xA1B3: 838, + 0xA1B5: 839, + 0xA1B7: 840, + 0xA1C1: 841, + 0xA1C5: 842, + 0xA1D6: 843, + 0xA1D7: 844, + 0xA241: 845, + 0xA245: 846, + 0xA249: 847, + 0xA253: 848, + 0xA255: 849, + 0xA257: 850, + 0xA261: 851, + 0xA265: 852, + 0xA269: 853, + 0xA273: 854, + 0xA275: 855, + 0xA281: 856, + 0xA282: 857, + 0xA283: 858, + 0xA285: 859, + 0xA288: 860, + 0xA289: 861, + 0xA28A: 862, + 0xA28B: 863, + 0xA291: 864, + 0xA293: 865, + 0xA295: 866, + 0xA297: 867, + 0xA29B: 868, + 0xA29D: 869, + 0xA2A1: 870, + 0xA2A5: 871, + 0xA2A9: 872, + 0xA2B3: 873, + 0xA2B5: 874, + 0xA2C1: 875, + 0xA2E1: 876, + 0xA2E5: 877, + 0xA2E9: 878, + 0xA341: 879, + 0xA345: 880, + 0xA349: 881, + 0xA351: 882, + 0xA355: 883, + 0xA361: 884, + 0xA365: 885, + 0xA369: 886, + 0xA371: 887, + 0xA375: 888, + 0xA3A1: 889, + 0xA3A2: 890, + 0xA3A5: 891, + 0xA3A8: 892, + 0xA3A9: 893, + 0xA3AB: 894, + 0xA3B1: 895, + 0xA3B3: 896, + 0xA3B5: 897, + 0xA3B6: 898, + 0xA3B7: 899, + 0xA3B9: 900, + 0xA3BB: 901, + 0xA461: 902, + 0xA462: 903, + 0xA463: 904, + 0xA464: 905, + 0xA465: 906, + 0xA468: 907, + 0xA469: 908, + 0xA46A: 909, + 0xA46B: 910, + 0xA46C: 911, + 0xA471: 912, + 0xA473: 913, + 0xA475: 914, + 0xA477: 915, + 0xA47B: 916, + 0xA481: 917, + 0xA482: 918, + 0xA485: 919, + 0xA489: 920, + 0xA491: 921, + 0xA493: 922, + 0xA495: 923, + 0xA496: 924, + 0xA497: 925, + 0xA49B: 926, + 0xA4A1: 927, + 0xA4A2: 928, + 0xA4A5: 929, + 0xA4B3: 930, + 0xA4E1: 931, + 0xA4E2: 932, + 0xA4E5: 933, + 0xA4E8: 934, + 0xA4E9: 935, + 0xA4EB: 936, + 0xA4F1: 937, + 0xA4F3: 938, + 0xA4F5: 939, + 0xA4F7: 940, + 0xA4F8: 941, + 0xA541: 942, + 0xA542: 943, + 0xA545: 944, + 0xA548: 945, + 0xA549: 946, + 0xA551: 947, + 0xA553: 948, + 0xA555: 949, + 0xA556: 950, + 0xA557: 951, + 0xA561: 952, + 0xA562: 953, + 0xA565: 954, + 0xA569: 955, + 0xA573: 956, + 0xA575: 957, + 0xA576: 958, + 0xA577: 959, + 0xA57B: 960, + 0xA581: 961, + 0xA585: 962, + 0xA5A1: 963, + 0xA5A2: 964, + 0xA5A3: 965, + 0xA5A5: 966, + 0xA5A9: 967, + 0xA5B1: 968, + 0xA5B3: 969, + 0xA5B5: 970, + 0xA5B7: 971, + 0xA5C1: 972, + 0xA5C5: 973, + 0xA5D6: 974, + 0xA5E1: 975, + 0xA5F6: 976, + 0xA641: 977, + 0xA642: 978, + 0xA645: 979, + 0xA649: 980, + 0xA651: 981, + 0xA653: 982, + 0xA661: 983, + 0xA665: 984, + 0xA681: 985, + 0xA682: 986, + 0xA685: 987, + 0xA688: 988, + 0xA689: 989, + 0xA68A: 990, + 0xA68B: 991, + 0xA691: 992, + 0xA693: 993, + 0xA695: 994, + 0xA697: 995, + 0xA69B: 996, + 0xA69C: 997, + 0xA6A1: 998, + 0xA6A9: 999, + 0xA6B6: 1000, + 0xA6C1: 1001, + 0xA6E1: 1002, + 0xA6E2: 1003, + 0xA6E5: 1004, + 0xA6E9: 1005, + 0xA6F7: 1006, + 0xA741: 1007, + 0xA745: 1008, + 0xA749: 1009, + 0xA751: 1010, + 0xA755: 1011, + 0xA757: 1012, + 0xA761: 1013, + 0xA762: 1014, + 0xA765: 1015, + 0xA769: 1016, + 0xA771: 1017, + 0xA773: 1018, + 0xA775: 1019, + 0xA7A1: 1020, + 0xA7A2: 1021, + 0xA7A5: 1022, + 0xA7A9: 1023, + 0xA7AB: 1024, + 0xA7B1: 1025, + 0xA7B3: 1026, + 0xA7B5: 1027, + 0xA7B7: 1028, + 0xA7B8: 1029, + 0xA7B9: 1030, + 0xA861: 1031, + 0xA862: 1032, + 0xA865: 1033, + 0xA869: 1034, + 0xA86B: 1035, + 0xA871: 1036, + 0xA873: 1037, + 0xA875: 1038, + 0xA876: 1039, + 0xA877: 1040, + 0xA87D: 1041, + 0xA881: 1042, + 0xA882: 1043, + 0xA885: 1044, + 0xA889: 1045, + 0xA891: 1046, + 0xA893: 1047, + 0xA895: 1048, + 0xA896: 1049, + 0xA897: 1050, + 0xA8A1: 1051, + 0xA8A2: 1052, + 0xA8B1: 1053, + 0xA8E1: 1054, + 0xA8E2: 1055, + 0xA8E5: 1056, + 0xA8E8: 1057, + 0xA8E9: 1058, + 0xA8F1: 1059, + 0xA8F5: 1060, + 0xA8F6: 1061, + 0xA8F7: 1062, + 0xA941: 1063, + 0xA957: 1064, + 0xA961: 1065, + 0xA962: 1066, + 0xA971: 1067, + 0xA973: 1068, + 0xA975: 1069, + 0xA976: 1070, + 0xA977: 1071, + 0xA9A1: 1072, + 0xA9A2: 1073, + 0xA9A5: 1074, + 0xA9A9: 1075, + 0xA9B1: 1076, + 0xA9B3: 1077, + 0xA9B7: 1078, + 0xAA41: 1079, + 0xAA61: 1080, + 0xAA77: 1081, + 0xAA81: 1082, + 0xAA82: 1083, + 0xAA85: 1084, + 0xAA89: 1085, + 0xAA91: 1086, + 0xAA95: 1087, + 0xAA97: 1088, + 0xAB41: 1089, + 0xAB57: 1090, + 0xAB61: 1091, + 0xAB65: 1092, + 0xAB69: 1093, + 0xAB71: 1094, + 0xAB73: 1095, + 0xABA1: 1096, + 0xABA2: 1097, + 0xABA5: 1098, + 0xABA9: 1099, + 0xABB1: 1100, + 0xABB3: 1101, + 0xABB5: 1102, + 0xABB7: 1103, + 0xAC61: 1104, + 0xAC62: 1105, + 0xAC64: 1106, + 0xAC65: 1107, + 0xAC68: 1108, + 0xAC69: 1109, + 0xAC6A: 1110, + 0xAC6B: 1111, + 0xAC71: 1112, + 0xAC73: 1113, + 0xAC75: 1114, + 0xAC76: 1115, + 0xAC77: 1116, + 0xAC7B: 1117, + 0xAC81: 1118, + 0xAC82: 1119, + 0xAC85: 1120, + 0xAC89: 1121, + 0xAC91: 1122, + 0xAC93: 1123, + 0xAC95: 1124, + 0xAC96: 1125, + 0xAC97: 1126, + 0xACA1: 1127, + 0xACA2: 1128, + 0xACA5: 1129, + 0xACA9: 1130, + 0xACB1: 1131, + 0xACB3: 1132, + 0xACB5: 1133, + 0xACB7: 1134, + 0xACC1: 1135, + 0xACC5: 1136, + 0xACC9: 1137, + 0xACD1: 1138, + 0xACD7: 1139, + 0xACE1: 1140, + 0xACE2: 1141, + 0xACE3: 1142, + 0xACE4: 1143, + 0xACE5: 1144, + 0xACE8: 1145, + 0xACE9: 1146, + 0xACEB: 1147, + 0xACEC: 1148, + 0xACF1: 1149, + 0xACF3: 1150, + 0xACF5: 1151, + 0xACF6: 1152, + 0xACF7: 1153, + 0xACFC: 1154, + 0xAD41: 1155, + 0xAD42: 1156, + 0xAD45: 1157, + 0xAD49: 1158, + 0xAD51: 1159, + 0xAD53: 1160, + 0xAD55: 1161, + 0xAD56: 1162, + 0xAD57: 1163, + 0xAD61: 1164, + 0xAD62: 1165, + 0xAD65: 1166, + 0xAD69: 1167, + 0xAD71: 1168, + 0xAD73: 1169, + 0xAD75: 1170, + 0xAD76: 1171, + 0xAD77: 1172, + 0xAD81: 1173, + 0xAD85: 1174, + 0xAD89: 1175, + 0xAD97: 1176, + 0xADA1: 1177, + 0xADA2: 1178, + 0xADA3: 1179, + 0xADA5: 1180, + 0xADA9: 1181, + 0xADAB: 1182, + 0xADB1: 1183, + 0xADB3: 1184, + 0xADB5: 1185, + 0xADB7: 1186, + 0xADBB: 1187, + 0xADC1: 1188, + 0xADC2: 1189, + 0xADC5: 1190, + 0xADC9: 1191, + 0xADD7: 1192, + 0xADE1: 1193, + 0xADE5: 1194, + 0xADE9: 1195, + 0xADF1: 1196, + 0xADF5: 1197, + 0xADF6: 1198, + 0xAE41: 1199, + 0xAE45: 1200, + 0xAE49: 1201, + 0xAE51: 1202, + 0xAE53: 1203, + 0xAE55: 1204, + 0xAE61: 1205, + 0xAE62: 1206, + 0xAE65: 1207, + 0xAE69: 1208, + 0xAE71: 1209, + 0xAE73: 1210, + 0xAE75: 1211, + 0xAE77: 1212, + 0xAE81: 1213, + 0xAE82: 1214, + 0xAE85: 1215, + 0xAE88: 1216, + 0xAE89: 1217, + 0xAE91: 1218, + 0xAE93: 1219, + 0xAE95: 1220, + 0xAE97: 1221, + 0xAE99: 1222, + 0xAE9B: 1223, + 0xAE9C: 1224, + 0xAEA1: 1225, + 0xAEB6: 1226, + 0xAEC1: 1227, + 0xAEC2: 1228, + 0xAEC5: 1229, + 0xAEC9: 1230, + 0xAED1: 1231, + 0xAED7: 1232, + 0xAEE1: 1233, + 0xAEE2: 1234, + 0xAEE5: 1235, + 0xAEE9: 1236, + 0xAEF1: 1237, + 0xAEF3: 1238, + 0xAEF5: 1239, + 0xAEF7: 1240, + 0xAF41: 1241, + 0xAF42: 1242, + 0xAF49: 1243, + 0xAF51: 1244, + 0xAF55: 1245, + 0xAF57: 1246, + 0xAF61: 1247, + 0xAF62: 1248, + 0xAF65: 1249, + 0xAF69: 1250, + 0xAF6A: 1251, + 0xAF71: 1252, + 0xAF73: 1253, + 0xAF75: 1254, + 0xAF77: 1255, + 0xAFA1: 1256, + 0xAFA2: 1257, + 0xAFA5: 1258, + 0xAFA8: 1259, + 0xAFA9: 1260, + 0xAFB0: 1261, + 0xAFB1: 1262, + 0xAFB3: 1263, + 0xAFB5: 1264, + 0xAFB7: 1265, + 0xAFBC: 1266, + 0xB061: 1267, + 0xB062: 1268, + 0xB064: 1269, + 0xB065: 1270, + 0xB069: 1271, + 0xB071: 1272, + 0xB073: 1273, + 0xB076: 1274, + 0xB077: 1275, + 0xB07D: 1276, + 0xB081: 1277, + 0xB082: 1278, + 0xB085: 1279, + 0xB089: 1280, + 0xB091: 1281, + 0xB093: 1282, + 0xB096: 1283, + 0xB097: 1284, + 0xB0B7: 1285, + 0xB0E1: 1286, + 0xB0E2: 1287, + 0xB0E5: 1288, + 0xB0E9: 1289, + 0xB0EB: 1290, + 0xB0F1: 1291, + 0xB0F3: 1292, + 0xB0F6: 1293, + 0xB0F7: 1294, + 0xB141: 1295, + 0xB145: 1296, + 0xB149: 1297, + 0xB185: 1298, + 0xB1A1: 1299, + 0xB1A2: 1300, + 0xB1A5: 1301, + 0xB1A8: 1302, + 0xB1A9: 1303, + 0xB1AB: 1304, + 0xB1B1: 1305, + 0xB1B3: 1306, + 0xB1B7: 1307, + 0xB1C1: 1308, + 0xB1C2: 1309, + 0xB1C5: 1310, + 0xB1D6: 1311, + 0xB1E1: 1312, + 0xB1F6: 1313, + 0xB241: 1314, + 0xB245: 1315, + 0xB249: 1316, + 0xB251: 1317, + 0xB253: 1318, + 0xB261: 1319, + 0xB281: 1320, + 0xB282: 1321, + 0xB285: 1322, + 0xB289: 1323, + 0xB291: 1324, + 0xB293: 1325, + 0xB297: 1326, + 0xB2A1: 1327, + 0xB2B6: 1328, + 0xB2C1: 1329, + 0xB2E1: 1330, + 0xB2E5: 1331, + 0xB357: 1332, + 0xB361: 1333, + 0xB362: 1334, + 0xB365: 1335, + 0xB369: 1336, + 0xB36B: 1337, + 0xB370: 1338, + 0xB371: 1339, + 0xB373: 1340, + 0xB381: 1341, + 0xB385: 1342, + 0xB389: 1343, + 0xB391: 1344, + 0xB3A1: 1345, + 0xB3A2: 1346, + 0xB3A5: 1347, + 0xB3A9: 1348, + 0xB3B1: 1349, + 0xB3B3: 1350, + 0xB3B5: 1351, + 0xB3B7: 1352, + 0xB461: 1353, + 0xB462: 1354, + 0xB465: 1355, + 0xB466: 1356, + 0xB467: 1357, + 0xB469: 1358, + 0xB46A: 1359, + 0xB46B: 1360, + 0xB470: 1361, + 0xB471: 1362, + 0xB473: 1363, + 0xB475: 1364, + 0xB476: 1365, + 0xB477: 1366, + 0xB47B: 1367, + 0xB47C: 1368, + 0xB481: 1369, + 0xB482: 1370, + 0xB485: 1371, + 0xB489: 1372, + 0xB491: 1373, + 0xB493: 1374, + 0xB495: 1375, + 0xB496: 1376, + 0xB497: 1377, + 0xB4A1: 1378, + 0xB4A2: 1379, + 0xB4A5: 1380, + 0xB4A9: 1381, + 0xB4AC: 1382, + 0xB4B1: 1383, + 0xB4B3: 1384, + 0xB4B5: 1385, + 0xB4B7: 1386, + 0xB4BB: 1387, + 0xB4BD: 1388, + 0xB4C1: 1389, + 0xB4C5: 1390, + 0xB4C9: 1391, + 0xB4D3: 1392, + 0xB4E1: 1393, + 0xB4E2: 1394, + 0xB4E5: 1395, + 0xB4E6: 1396, + 0xB4E8: 1397, + 0xB4E9: 1398, + 0xB4EA: 1399, + 0xB4EB: 1400, + 0xB4F1: 1401, + 0xB4F3: 1402, + 0xB4F4: 1403, + 0xB4F5: 1404, + 0xB4F6: 1405, + 0xB4F7: 1406, + 0xB4F8: 1407, + 0xB4FA: 1408, + 0xB4FC: 1409, + 0xB541: 1410, + 0xB542: 1411, + 0xB545: 1412, + 0xB549: 1413, + 0xB551: 1414, + 0xB553: 1415, + 0xB555: 1416, + 0xB557: 1417, + 0xB561: 1418, + 0xB562: 1419, + 0xB563: 1420, + 0xB565: 1421, + 0xB569: 1422, + 0xB56B: 1423, + 0xB56C: 1424, + 0xB571: 1425, + 0xB573: 1426, + 0xB574: 1427, + 0xB575: 1428, + 0xB576: 1429, + 0xB577: 1430, + 0xB57B: 1431, + 0xB57C: 1432, + 0xB57D: 1433, + 0xB581: 1434, + 0xB585: 1435, + 0xB589: 1436, + 0xB591: 1437, + 0xB593: 1438, + 0xB595: 1439, + 0xB596: 1440, + 0xB5A1: 1441, + 0xB5A2: 1442, + 0xB5A5: 1443, + 0xB5A9: 1444, + 0xB5AA: 1445, + 0xB5AB: 1446, + 0xB5AD: 1447, + 0xB5B0: 1448, + 0xB5B1: 1449, + 0xB5B3: 1450, + 0xB5B5: 1451, + 0xB5B7: 1452, + 0xB5B9: 1453, + 0xB5C1: 1454, + 0xB5C2: 1455, + 0xB5C5: 1456, + 0xB5C9: 1457, + 0xB5D1: 1458, + 0xB5D3: 1459, + 0xB5D5: 1460, + 0xB5D6: 1461, + 0xB5D7: 1462, + 0xB5E1: 1463, + 0xB5E2: 1464, + 0xB5E5: 1465, + 0xB5F1: 1466, + 0xB5F5: 1467, + 0xB5F7: 1468, + 0xB641: 1469, + 0xB642: 1470, + 0xB645: 1471, + 0xB649: 1472, + 0xB651: 1473, + 0xB653: 1474, + 0xB655: 1475, + 0xB657: 1476, + 0xB661: 1477, + 0xB662: 1478, + 0xB665: 1479, + 0xB669: 1480, + 0xB671: 1481, + 0xB673: 1482, + 0xB675: 1483, + 0xB677: 1484, + 0xB681: 1485, + 0xB682: 1486, + 0xB685: 1487, + 0xB689: 1488, + 0xB68A: 1489, + 0xB68B: 1490, + 0xB691: 1491, + 0xB693: 1492, + 0xB695: 1493, + 0xB697: 1494, + 0xB6A1: 1495, + 0xB6A2: 1496, + 0xB6A5: 1497, + 0xB6A9: 1498, + 0xB6B1: 1499, + 0xB6B3: 1500, + 0xB6B6: 1501, + 0xB6B7: 1502, + 0xB6C1: 1503, + 0xB6C2: 1504, + 0xB6C5: 1505, + 0xB6C9: 1506, + 0xB6D1: 1507, + 0xB6D3: 1508, + 0xB6D7: 1509, + 0xB6E1: 1510, + 0xB6E2: 1511, + 0xB6E5: 1512, + 0xB6E9: 1513, + 0xB6F1: 1514, + 0xB6F3: 1515, + 0xB6F5: 1516, + 0xB6F7: 1517, + 0xB741: 1518, + 0xB742: 1519, + 0xB745: 1520, + 0xB749: 1521, + 0xB751: 1522, + 0xB753: 1523, + 0xB755: 1524, + 0xB757: 1525, + 0xB759: 1526, + 0xB761: 1527, + 0xB762: 1528, + 0xB765: 1529, + 0xB769: 1530, + 0xB76F: 1531, + 0xB771: 1532, + 0xB773: 1533, + 0xB775: 1534, + 0xB777: 1535, + 0xB778: 1536, + 0xB779: 1537, + 0xB77A: 1538, + 0xB77B: 1539, + 0xB77C: 1540, + 0xB77D: 1541, + 0xB781: 1542, + 0xB785: 1543, + 0xB789: 1544, + 0xB791: 1545, + 0xB795: 1546, + 0xB7A1: 1547, + 0xB7A2: 1548, + 0xB7A5: 1549, + 0xB7A9: 1550, + 0xB7AA: 1551, + 0xB7AB: 1552, + 0xB7B0: 1553, + 0xB7B1: 1554, + 0xB7B3: 1555, + 0xB7B5: 1556, + 0xB7B6: 1557, + 0xB7B7: 1558, + 0xB7B8: 1559, + 0xB7BC: 1560, + 0xB861: 1561, + 0xB862: 1562, + 0xB865: 1563, + 0xB867: 1564, + 0xB868: 1565, + 0xB869: 1566, + 0xB86B: 1567, + 0xB871: 1568, + 0xB873: 1569, + 0xB875: 1570, + 0xB876: 1571, + 0xB877: 1572, + 0xB878: 1573, + 0xB881: 1574, + 0xB882: 1575, + 0xB885: 1576, + 0xB889: 1577, + 0xB891: 1578, + 0xB893: 1579, + 0xB895: 1580, + 0xB896: 1581, + 0xB897: 1582, + 0xB8A1: 1583, + 0xB8A2: 1584, + 0xB8A5: 1585, + 0xB8A7: 1586, + 0xB8A9: 1587, + 0xB8B1: 1588, + 0xB8B7: 1589, + 0xB8C1: 1590, + 0xB8C5: 1591, + 0xB8C9: 1592, + 0xB8E1: 1593, + 0xB8E2: 1594, + 0xB8E5: 1595, + 0xB8E9: 1596, + 0xB8EB: 1597, + 0xB8F1: 1598, + 0xB8F3: 1599, + 0xB8F5: 1600, + 0xB8F7: 1601, + 0xB8F8: 1602, + 0xB941: 1603, + 0xB942: 1604, + 0xB945: 1605, + 0xB949: 1606, + 0xB951: 1607, + 0xB953: 1608, + 0xB955: 1609, + 0xB957: 1610, + 0xB961: 1611, + 0xB965: 1612, + 0xB969: 1613, + 0xB971: 1614, + 0xB973: 1615, + 0xB976: 1616, + 0xB977: 1617, + 0xB981: 1618, + 0xB9A1: 1619, + 0xB9A2: 1620, + 0xB9A5: 1621, + 0xB9A9: 1622, + 0xB9AB: 1623, + 0xB9B1: 1624, + 0xB9B3: 1625, + 0xB9B5: 1626, + 0xB9B7: 1627, + 0xB9B8: 1628, + 0xB9B9: 1629, + 0xB9BD: 1630, + 0xB9C1: 1631, + 0xB9C2: 1632, + 0xB9C9: 1633, + 0xB9D3: 1634, + 0xB9D5: 1635, + 0xB9D7: 1636, + 0xB9E1: 1637, + 0xB9F6: 1638, + 0xB9F7: 1639, + 0xBA41: 1640, + 0xBA45: 1641, + 0xBA49: 1642, + 0xBA51: 1643, + 0xBA53: 1644, + 0xBA55: 1645, + 0xBA57: 1646, + 0xBA61: 1647, + 0xBA62: 1648, + 0xBA65: 1649, + 0xBA77: 1650, + 0xBA81: 1651, + 0xBA82: 1652, + 0xBA85: 1653, + 0xBA89: 1654, + 0xBA8A: 1655, + 0xBA8B: 1656, + 0xBA91: 1657, + 0xBA93: 1658, + 0xBA95: 1659, + 0xBA97: 1660, + 0xBAA1: 1661, + 0xBAB6: 1662, + 0xBAC1: 1663, + 0xBAE1: 1664, + 0xBAE2: 1665, + 0xBAE5: 1666, + 0xBAE9: 1667, + 0xBAF1: 1668, + 0xBAF3: 1669, + 0xBAF5: 1670, + 0xBB41: 1671, + 0xBB45: 1672, + 0xBB49: 1673, + 0xBB51: 1674, + 0xBB61: 1675, + 0xBB62: 1676, + 0xBB65: 1677, + 0xBB69: 1678, + 0xBB71: 1679, + 0xBB73: 1680, + 0xBB75: 1681, + 0xBB77: 1682, + 0xBBA1: 1683, + 0xBBA2: 1684, + 0xBBA5: 1685, + 0xBBA8: 1686, + 0xBBA9: 1687, + 0xBBAB: 1688, + 0xBBB1: 1689, + 0xBBB3: 1690, + 0xBBB5: 1691, + 0xBBB7: 1692, + 0xBBB8: 1693, + 0xBBBB: 1694, + 0xBBBC: 1695, + 0xBC61: 1696, + 0xBC62: 1697, + 0xBC65: 1698, + 0xBC67: 1699, + 0xBC69: 1700, + 0xBC6C: 1701, + 0xBC71: 1702, + 0xBC73: 1703, + 0xBC75: 1704, + 0xBC76: 1705, + 0xBC77: 1706, + 0xBC81: 1707, + 0xBC82: 1708, + 0xBC85: 1709, + 0xBC89: 1710, + 0xBC91: 1711, + 0xBC93: 1712, + 0xBC95: 1713, + 0xBC96: 1714, + 0xBC97: 1715, + 0xBCA1: 1716, + 0xBCA5: 1717, + 0xBCB7: 1718, + 0xBCE1: 1719, + 0xBCE2: 1720, + 0xBCE5: 1721, + 0xBCE9: 1722, + 0xBCF1: 1723, + 0xBCF3: 1724, + 0xBCF5: 1725, + 0xBCF6: 1726, + 0xBCF7: 1727, + 0xBD41: 1728, + 0xBD57: 1729, + 0xBD61: 1730, + 0xBD76: 1731, + 0xBDA1: 1732, + 0xBDA2: 1733, + 0xBDA5: 1734, + 0xBDA9: 1735, + 0xBDB1: 1736, + 0xBDB3: 1737, + 0xBDB5: 1738, + 0xBDB7: 1739, + 0xBDB9: 1740, + 0xBDC1: 1741, + 0xBDC2: 1742, + 0xBDC9: 1743, + 0xBDD6: 1744, + 0xBDE1: 1745, + 0xBDF6: 1746, + 0xBE41: 1747, + 0xBE45: 1748, + 0xBE49: 1749, + 0xBE51: 1750, + 0xBE53: 1751, + 0xBE77: 1752, + 0xBE81: 1753, + 0xBE82: 1754, + 0xBE85: 1755, + 0xBE89: 1756, + 0xBE91: 1757, + 0xBE93: 1758, + 0xBE97: 1759, + 0xBEA1: 1760, + 0xBEB6: 1761, + 0xBEB7: 1762, + 0xBEE1: 1763, + 0xBF41: 1764, + 0xBF61: 1765, + 0xBF71: 1766, + 0xBF75: 1767, + 0xBF77: 1768, + 0xBFA1: 1769, + 0xBFA2: 1770, + 0xBFA5: 1771, + 0xBFA9: 1772, + 0xBFB1: 1773, + 0xBFB3: 1774, + 0xBFB7: 1775, + 0xBFB8: 1776, + 0xBFBD: 1777, + 0xC061: 1778, + 0xC062: 1779, + 0xC065: 1780, + 0xC067: 1781, + 0xC069: 1782, + 0xC071: 1783, + 0xC073: 1784, + 0xC075: 1785, + 0xC076: 1786, + 0xC077: 1787, + 0xC078: 1788, + 0xC081: 1789, + 0xC082: 1790, + 0xC085: 1791, + 0xC089: 1792, + 0xC091: 1793, + 0xC093: 1794, + 0xC095: 1795, + 0xC096: 1796, + 0xC097: 1797, + 0xC0A1: 1798, + 0xC0A5: 1799, + 0xC0A7: 1800, + 0xC0A9: 1801, + 0xC0B1: 1802, + 0xC0B7: 1803, + 0xC0E1: 1804, + 0xC0E2: 1805, + 0xC0E5: 1806, + 0xC0E9: 1807, + 0xC0F1: 1808, + 0xC0F3: 1809, + 0xC0F5: 1810, + 0xC0F6: 1811, + 0xC0F7: 1812, + 0xC141: 1813, + 0xC142: 1814, + 0xC145: 1815, + 0xC149: 1816, + 0xC151: 1817, + 0xC153: 1818, + 0xC155: 1819, + 0xC157: 1820, + 0xC161: 1821, + 0xC165: 1822, + 0xC176: 1823, + 0xC181: 1824, + 0xC185: 1825, + 0xC197: 1826, + 0xC1A1: 1827, + 0xC1A2: 1828, + 0xC1A5: 1829, + 0xC1A9: 1830, + 0xC1B1: 1831, + 0xC1B3: 1832, + 0xC1B5: 1833, + 0xC1B7: 1834, + 0xC1C1: 1835, + 0xC1C5: 1836, + 0xC1C9: 1837, + 0xC1D7: 1838, + 0xC241: 1839, + 0xC245: 1840, + 0xC249: 1841, + 0xC251: 1842, + 0xC253: 1843, + 0xC255: 1844, + 0xC257: 1845, + 0xC261: 1846, + 0xC271: 1847, + 0xC281: 1848, + 0xC282: 1849, + 0xC285: 1850, + 0xC289: 1851, + 0xC291: 1852, + 0xC293: 1853, + 0xC295: 1854, + 0xC297: 1855, + 0xC2A1: 1856, + 0xC2B6: 1857, + 0xC2C1: 1858, + 0xC2C5: 1859, + 0xC2E1: 1860, + 0xC2E5: 1861, + 0xC2E9: 1862, + 0xC2F1: 1863, + 0xC2F3: 1864, + 0xC2F5: 1865, + 0xC2F7: 1866, + 0xC341: 1867, + 0xC345: 1868, + 0xC349: 1869, + 0xC351: 1870, + 0xC357: 1871, + 0xC361: 1872, + 0xC362: 1873, + 0xC365: 1874, + 0xC369: 1875, + 0xC371: 1876, + 0xC373: 1877, + 0xC375: 1878, + 0xC377: 1879, + 0xC3A1: 1880, + 0xC3A2: 1881, + 0xC3A5: 1882, + 0xC3A8: 1883, + 0xC3A9: 1884, + 0xC3AA: 1885, + 0xC3B1: 1886, + 0xC3B3: 1887, + 0xC3B5: 1888, + 0xC3B7: 1889, + 0xC461: 1890, + 0xC462: 1891, + 0xC465: 1892, + 0xC469: 1893, + 0xC471: 1894, + 0xC473: 1895, + 0xC475: 1896, + 0xC477: 1897, + 0xC481: 1898, + 0xC482: 1899, + 0xC485: 1900, + 0xC489: 1901, + 0xC491: 1902, + 0xC493: 1903, + 0xC495: 1904, + 0xC496: 1905, + 0xC497: 1906, + 0xC4A1: 1907, + 0xC4A2: 1908, + 0xC4B7: 1909, + 0xC4E1: 1910, + 0xC4E2: 1911, + 0xC4E5: 1912, + 0xC4E8: 1913, + 0xC4E9: 1914, + 0xC4F1: 1915, + 0xC4F3: 1916, + 0xC4F5: 1917, + 0xC4F6: 1918, + 0xC4F7: 1919, + 0xC541: 1920, + 0xC542: 1921, + 0xC545: 1922, + 0xC549: 1923, + 0xC551: 1924, + 0xC553: 1925, + 0xC555: 1926, + 0xC557: 1927, + 0xC561: 1928, + 0xC565: 1929, + 0xC569: 1930, + 0xC571: 1931, + 0xC573: 1932, + 0xC575: 1933, + 0xC576: 1934, + 0xC577: 1935, + 0xC581: 1936, + 0xC5A1: 1937, + 0xC5A2: 1938, + 0xC5A5: 1939, + 0xC5A9: 1940, + 0xC5B1: 1941, + 0xC5B3: 1942, + 0xC5B5: 1943, + 0xC5B7: 1944, + 0xC5C1: 1945, + 0xC5C2: 1946, + 0xC5C5: 1947, + 0xC5C9: 1948, + 0xC5D1: 1949, + 0xC5D7: 1950, + 0xC5E1: 1951, + 0xC5F7: 1952, + 0xC641: 1953, + 0xC649: 1954, + 0xC661: 1955, + 0xC681: 1956, + 0xC682: 1957, + 0xC685: 1958, + 0xC689: 1959, + 0xC691: 1960, + 0xC693: 1961, + 0xC695: 1962, + 0xC697: 1963, + 0xC6A1: 1964, + 0xC6A5: 1965, + 0xC6A9: 1966, + 0xC6B7: 1967, + 0xC6C1: 1968, + 0xC6D7: 1969, + 0xC6E1: 1970, + 0xC6E2: 1971, + 0xC6E5: 1972, + 0xC6E9: 1973, + 0xC6F1: 1974, + 0xC6F3: 1975, + 0xC6F5: 1976, + 0xC6F7: 1977, + 0xC741: 1978, + 0xC745: 1979, + 0xC749: 1980, + 0xC751: 1981, + 0xC761: 1982, + 0xC762: 1983, + 0xC765: 1984, + 0xC769: 1985, + 0xC771: 1986, + 0xC773: 1987, + 0xC777: 1988, + 0xC7A1: 1989, + 0xC7A2: 1990, + 0xC7A5: 1991, + 0xC7A9: 1992, + 0xC7B1: 1993, + 0xC7B3: 1994, + 0xC7B5: 1995, + 0xC7B7: 1996, + 0xC861: 1997, + 0xC862: 1998, + 0xC865: 1999, + 0xC869: 2000, + 0xC86A: 2001, + 0xC871: 2002, + 0xC873: 2003, + 0xC875: 2004, + 0xC876: 2005, + 0xC877: 2006, + 0xC881: 2007, + 0xC882: 2008, + 0xC885: 2009, + 0xC889: 2010, + 0xC891: 2011, + 0xC893: 2012, + 0xC895: 2013, + 0xC896: 2014, + 0xC897: 2015, + 0xC8A1: 2016, + 0xC8B7: 2017, + 0xC8E1: 2018, + 0xC8E2: 2019, + 0xC8E5: 2020, + 0xC8E9: 2021, + 0xC8EB: 2022, + 0xC8F1: 2023, + 0xC8F3: 2024, + 0xC8F5: 2025, + 0xC8F6: 2026, + 0xC8F7: 2027, + 0xC941: 2028, + 0xC942: 2029, + 0xC945: 2030, + 0xC949: 2031, + 0xC951: 2032, + 0xC953: 2033, + 0xC955: 2034, + 0xC957: 2035, + 0xC961: 2036, + 0xC965: 2037, + 0xC976: 2038, + 0xC981: 2039, + 0xC985: 2040, + 0xC9A1: 2041, + 0xC9A2: 2042, + 0xC9A5: 2043, + 0xC9A9: 2044, + 0xC9B1: 2045, + 0xC9B3: 2046, + 0xC9B5: 2047, + 0xC9B7: 2048, + 0xC9BC: 2049, + 0xC9C1: 2050, + 0xC9C5: 2051, + 0xC9E1: 2052, + 0xCA41: 2053, + 0xCA45: 2054, + 0xCA55: 2055, + 0xCA57: 2056, + 0xCA61: 2057, + 0xCA81: 2058, + 0xCA82: 2059, + 0xCA85: 2060, + 0xCA89: 2061, + 0xCA91: 2062, + 0xCA93: 2063, + 0xCA95: 2064, + 0xCA97: 2065, + 0xCAA1: 2066, + 0xCAB6: 2067, + 0xCAC1: 2068, + 0xCAE1: 2069, + 0xCAE2: 2070, + 0xCAE5: 2071, + 0xCAE9: 2072, + 0xCAF1: 2073, + 0xCAF3: 2074, + 0xCAF7: 2075, + 0xCB41: 2076, + 0xCB45: 2077, + 0xCB49: 2078, + 0xCB51: 2079, + 0xCB57: 2080, + 0xCB61: 2081, + 0xCB62: 2082, + 0xCB65: 2083, + 0xCB68: 2084, + 0xCB69: 2085, + 0xCB6B: 2086, + 0xCB71: 2087, + 0xCB73: 2088, + 0xCB75: 2089, + 0xCB81: 2090, + 0xCB85: 2091, + 0xCB89: 2092, + 0xCB91: 2093, + 0xCB93: 2094, + 0xCBA1: 2095, + 0xCBA2: 2096, + 0xCBA5: 2097, + 0xCBA9: 2098, + 0xCBB1: 2099, + 0xCBB3: 2100, + 0xCBB5: 2101, + 0xCBB7: 2102, + 0xCC61: 2103, + 0xCC62: 2104, + 0xCC63: 2105, + 0xCC65: 2106, + 0xCC69: 2107, + 0xCC6B: 2108, + 0xCC71: 2109, + 0xCC73: 2110, + 0xCC75: 2111, + 0xCC76: 2112, + 0xCC77: 2113, + 0xCC7B: 2114, + 0xCC81: 2115, + 0xCC82: 2116, + 0xCC85: 2117, + 0xCC89: 2118, + 0xCC91: 2119, + 0xCC93: 2120, + 0xCC95: 2121, + 0xCC96: 2122, + 0xCC97: 2123, + 0xCCA1: 2124, + 0xCCA2: 2125, + 0xCCE1: 2126, + 0xCCE2: 2127, + 0xCCE5: 2128, + 0xCCE9: 2129, + 0xCCF1: 2130, + 0xCCF3: 2131, + 0xCCF5: 2132, + 0xCCF6: 2133, + 0xCCF7: 2134, + 0xCD41: 2135, + 0xCD42: 2136, + 0xCD45: 2137, + 0xCD49: 2138, + 0xCD51: 2139, + 0xCD53: 2140, + 0xCD55: 2141, + 0xCD57: 2142, + 0xCD61: 2143, + 0xCD65: 2144, + 0xCD69: 2145, + 0xCD71: 2146, + 0xCD73: 2147, + 0xCD76: 2148, + 0xCD77: 2149, + 0xCD81: 2150, + 0xCD89: 2151, + 0xCD93: 2152, + 0xCD95: 2153, + 0xCDA1: 2154, + 0xCDA2: 2155, + 0xCDA5: 2156, + 0xCDA9: 2157, + 0xCDB1: 2158, + 0xCDB3: 2159, + 0xCDB5: 2160, + 0xCDB7: 2161, + 0xCDC1: 2162, + 0xCDD7: 2163, + 0xCE41: 2164, + 0xCE45: 2165, + 0xCE61: 2166, + 0xCE65: 2167, + 0xCE69: 2168, + 0xCE73: 2169, + 0xCE75: 2170, + 0xCE81: 2171, + 0xCE82: 2172, + 0xCE85: 2173, + 0xCE88: 2174, + 0xCE89: 2175, + 0xCE8B: 2176, + 0xCE91: 2177, + 0xCE93: 2178, + 0xCE95: 2179, + 0xCE97: 2180, + 0xCEA1: 2181, + 0xCEB7: 2182, + 0xCEE1: 2183, + 0xCEE5: 2184, + 0xCEE9: 2185, + 0xCEF1: 2186, + 0xCEF5: 2187, + 0xCF41: 2188, + 0xCF45: 2189, + 0xCF49: 2190, + 0xCF51: 2191, + 0xCF55: 2192, + 0xCF57: 2193, + 0xCF61: 2194, + 0xCF65: 2195, + 0xCF69: 2196, + 0xCF71: 2197, + 0xCF73: 2198, + 0xCF75: 2199, + 0xCFA1: 2200, + 0xCFA2: 2201, + 0xCFA5: 2202, + 0xCFA9: 2203, + 0xCFB1: 2204, + 0xCFB3: 2205, + 0xCFB5: 2206, + 0xCFB7: 2207, + 0xD061: 2208, + 0xD062: 2209, + 0xD065: 2210, + 0xD069: 2211, + 0xD06E: 2212, + 0xD071: 2213, + 0xD073: 2214, + 0xD075: 2215, + 0xD077: 2216, + 0xD081: 2217, + 0xD082: 2218, + 0xD085: 2219, + 0xD089: 2220, + 0xD091: 2221, + 0xD093: 2222, + 0xD095: 2223, + 0xD096: 2224, + 0xD097: 2225, + 0xD0A1: 2226, + 0xD0B7: 2227, + 0xD0E1: 2228, + 0xD0E2: 2229, + 0xD0E5: 2230, + 0xD0E9: 2231, + 0xD0EB: 2232, + 0xD0F1: 2233, + 0xD0F3: 2234, + 0xD0F5: 2235, + 0xD0F7: 2236, + 0xD141: 2237, + 0xD142: 2238, + 0xD145: 2239, + 0xD149: 2240, + 0xD151: 2241, + 0xD153: 2242, + 0xD155: 2243, + 0xD157: 2244, + 0xD161: 2245, + 0xD162: 2246, + 0xD165: 2247, + 0xD169: 2248, + 0xD171: 2249, + 0xD173: 2250, + 0xD175: 2251, + 0xD176: 2252, + 0xD177: 2253, + 0xD181: 2254, + 0xD185: 2255, + 0xD189: 2256, + 0xD193: 2257, + 0xD1A1: 2258, + 0xD1A2: 2259, + 0xD1A5: 2260, + 0xD1A9: 2261, + 0xD1AE: 2262, + 0xD1B1: 2263, + 0xD1B3: 2264, + 0xD1B5: 2265, + 0xD1B7: 2266, + 0xD1BB: 2267, + 0xD1C1: 2268, + 0xD1C2: 2269, + 0xD1C5: 2270, + 0xD1C9: 2271, + 0xD1D5: 2272, + 0xD1D7: 2273, + 0xD1E1: 2274, + 0xD1E2: 2275, + 0xD1E5: 2276, + 0xD1F5: 2277, + 0xD1F7: 2278, + 0xD241: 2279, + 0xD242: 2280, + 0xD245: 2281, + 0xD249: 2282, + 0xD253: 2283, + 0xD255: 2284, + 0xD257: 2285, + 0xD261: 2286, + 0xD265: 2287, + 0xD269: 2288, + 0xD273: 2289, + 0xD275: 2290, + 0xD281: 2291, + 0xD282: 2292, + 0xD285: 2293, + 0xD289: 2294, + 0xD28E: 2295, + 0xD291: 2296, + 0xD295: 2297, + 0xD297: 2298, + 0xD2A1: 2299, + 0xD2A5: 2300, + 0xD2A9: 2301, + 0xD2B1: 2302, + 0xD2B7: 2303, + 0xD2C1: 2304, + 0xD2C2: 2305, + 0xD2C5: 2306, + 0xD2C9: 2307, + 0xD2D7: 2308, + 0xD2E1: 2309, + 0xD2E2: 2310, + 0xD2E5: 2311, + 0xD2E9: 2312, + 0xD2F1: 2313, + 0xD2F3: 2314, + 0xD2F5: 2315, + 0xD2F7: 2316, + 0xD341: 2317, + 0xD342: 2318, + 0xD345: 2319, + 0xD349: 2320, + 0xD351: 2321, + 0xD355: 2322, + 0xD357: 2323, + 0xD361: 2324, + 0xD362: 2325, + 0xD365: 2326, + 0xD367: 2327, + 0xD368: 2328, + 0xD369: 2329, + 0xD36A: 2330, + 0xD371: 2331, + 0xD373: 2332, + 0xD375: 2333, + 0xD377: 2334, + 0xD37B: 2335, + 0xD381: 2336, + 0xD385: 2337, + 0xD389: 2338, + 0xD391: 2339, + 0xD393: 2340, + 0xD397: 2341, + 0xD3A1: 2342, + 0xD3A2: 2343, + 0xD3A5: 2344, + 0xD3A9: 2345, + 0xD3B1: 2346, + 0xD3B3: 2347, + 0xD3B5: 2348, + 0xD3B7: 2349, +} diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabprober.py new file mode 100644 index 0000000..d7364ba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/johabprober.py @@ -0,0 +1,47 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .chardistribution import JOHABDistributionAnalysis +from .codingstatemachine import CodingStateMachine +from .mbcharsetprober import MultiByteCharSetProber +from .mbcssm import JOHAB_SM_MODEL + + +class JOHABProber(MultiByteCharSetProber): + def __init__(self) -> None: + super().__init__() + self.coding_sm = CodingStateMachine(JOHAB_SM_MODEL) + self.distribution_analyzer = JOHABDistributionAnalysis() + self.reset() + + @property + def charset_name(self) -> str: + return "Johab" + + @property + def language(self) -> str: + return "Korean" diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/jpcntx.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/jpcntx.py new file mode 100644 index 0000000..2f53bdd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/jpcntx.py @@ -0,0 +1,238 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Communicator client code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import List, Tuple, Union + +# This is hiragana 2-char sequence table, the number in each cell represents its frequency category +# fmt: off +jp2_char_context = ( + (0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1), + (2, 4, 0, 4, 0, 3, 0, 4, 0, 3, 4, 4, 4, 2, 4, 3, 3, 4, 3, 2, 3, 3, 4, 2, 3, 3, 3, 2, 4, 1, 4, 3, 3, 1, 5, 4, 3, 4, 3, 4, 3, 5, 3, 0, 3, 5, 4, 2, 0, 3, 1, 0, 3, 3, 0, 3, 3, 0, 1, 1, 0, 4, 3, 0, 3, 3, 0, 4, 0, 2, 0, 3, 5, 5, 5, 5, 4, 0, 4, 1, 0, 3, 4), + (0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2), + (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 4, 4, 3, 5, 3, 5, 1, 5, 3, 4, 3, 4, 4, 3, 4, 3, 3, 4, 3, 5, 4, 4, 3, 5, 5, 3, 5, 5, 5, 3, 5, 5, 3, 4, 5, 5, 3, 1, 3, 2, 0, 3, 4, 0, 4, 2, 0, 4, 2, 1, 5, 3, 2, 3, 5, 0, 4, 0, 2, 0, 5, 4, 4, 5, 4, 5, 0, 4, 0, 0, 4, 4), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 5, 4, 3, 3, 3, 3, 4, 3, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 4, 4, 4, 4, 5, 3, 4, 4, 3, 4, 5, 5, 4, 5, 5, 1, 4, 5, 4, 3, 0, 3, 3, 1, 3, 3, 0, 4, 4, 0, 3, 3, 1, 5, 3, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 0, 4, 1, 1, 3, 4), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 4, 0, 3, 0, 3, 0, 4, 0, 3, 4, 4, 3, 2, 2, 1, 2, 1, 3, 1, 3, 3, 3, 3, 3, 4, 3, 1, 3, 3, 5, 3, 3, 0, 4, 3, 0, 5, 4, 3, 3, 5, 4, 4, 3, 4, 4, 5, 0, 1, 2, 0, 1, 2, 0, 2, 2, 0, 1, 0, 0, 5, 2, 2, 1, 4, 0, 3, 0, 1, 0, 4, 4, 3, 5, 4, 3, 0, 2, 1, 0, 4, 3), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 3, 0, 5, 0, 4, 0, 2, 1, 4, 4, 2, 4, 1, 4, 2, 4, 2, 4, 3, 3, 3, 4, 3, 3, 3, 3, 1, 4, 2, 3, 3, 3, 1, 4, 4, 1, 1, 1, 4, 3, 3, 2, 0, 2, 4, 3, 2, 0, 3, 3, 0, 3, 1, 1, 0, 0, 0, 3, 3, 0, 4, 2, 2, 3, 4, 0, 4, 0, 3, 0, 4, 4, 5, 3, 4, 4, 0, 3, 0, 0, 1, 4), + (1, 4, 0, 4, 0, 4, 0, 4, 0, 3, 5, 4, 4, 3, 4, 3, 5, 4, 3, 3, 4, 3, 5, 4, 4, 4, 4, 3, 4, 2, 4, 3, 3, 1, 5, 4, 3, 2, 4, 5, 4, 5, 5, 4, 4, 5, 4, 4, 0, 3, 2, 2, 3, 3, 0, 4, 3, 1, 3, 2, 1, 4, 3, 3, 4, 5, 0, 3, 0, 2, 0, 4, 5, 5, 4, 5, 4, 0, 4, 0, 0, 5, 4), + (0, 5, 0, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 3, 4, 0, 4, 4, 4, 3, 4, 3, 4, 3, 3, 1, 4, 2, 4, 3, 4, 0, 5, 4, 1, 4, 5, 4, 4, 5, 3, 2, 4, 3, 4, 3, 2, 4, 1, 3, 3, 3, 2, 3, 2, 0, 4, 3, 3, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 4, 3, 0, 4, 1, 0, 1, 3), + (0, 3, 1, 4, 0, 3, 0, 2, 0, 3, 4, 4, 3, 1, 4, 2, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 1, 5, 4, 4, 1, 4, 4, 3, 5, 4, 4, 3, 5, 5, 4, 3, 4, 4, 3, 1, 2, 3, 1, 2, 2, 0, 3, 2, 0, 3, 1, 0, 5, 3, 3, 3, 4, 3, 3, 3, 3, 4, 4, 4, 4, 5, 4, 2, 0, 3, 3, 2, 4, 3), + (0, 2, 0, 3, 0, 1, 0, 1, 0, 0, 3, 2, 0, 0, 2, 0, 1, 0, 2, 1, 3, 3, 3, 1, 2, 3, 1, 0, 1, 0, 4, 2, 1, 1, 3, 3, 0, 4, 3, 3, 1, 4, 3, 3, 0, 3, 3, 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 4, 1, 0, 2, 3, 2, 2, 2, 1, 3, 3, 3, 4, 4, 3, 2, 0, 3, 1, 0, 3, 3), + (0, 4, 0, 4, 0, 3, 0, 3, 0, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 2, 4, 3, 4, 3, 3, 2, 4, 3, 4, 5, 4, 1, 4, 5, 3, 5, 4, 5, 3, 5, 4, 0, 3, 5, 5, 3, 1, 3, 3, 2, 2, 3, 0, 3, 4, 1, 3, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 5, 3, 0, 4, 1, 0, 3, 4), + (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 3, 0, 3, 0, 3, 0, 1, 3, 1, 0, 3, 1, 3, 3, 3, 1, 3, 3, 3, 0, 1, 3, 1, 3, 4, 0, 0, 3, 1, 1, 0, 3, 2, 0, 0, 0, 0, 1, 3, 0, 1, 0, 0, 3, 3, 2, 0, 3, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 3, 0, 3, 0, 0, 2, 3), + (2, 3, 0, 3, 0, 2, 0, 1, 0, 3, 3, 4, 3, 1, 3, 1, 1, 1, 3, 1, 4, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 4, 3, 1, 4, 3, 2, 5, 5, 4, 4, 4, 4, 3, 3, 4, 4, 4, 0, 2, 1, 1, 3, 2, 0, 1, 2, 0, 0, 1, 0, 4, 1, 3, 3, 3, 0, 3, 0, 1, 0, 4, 4, 4, 5, 5, 3, 0, 2, 0, 0, 4, 4), + (0, 2, 0, 1, 0, 3, 1, 3, 0, 2, 3, 3, 3, 0, 3, 1, 0, 0, 3, 0, 3, 2, 3, 1, 3, 2, 1, 1, 0, 0, 4, 2, 1, 0, 2, 3, 1, 4, 3, 2, 0, 4, 4, 3, 1, 3, 1, 3, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 1, 1, 1, 2, 0, 3, 0, 0, 0, 3, 4, 2, 4, 3, 2, 0, 1, 0, 0, 3, 3), + (0, 1, 0, 4, 0, 5, 0, 4, 0, 2, 4, 4, 2, 3, 3, 2, 3, 3, 5, 3, 3, 3, 4, 3, 4, 2, 3, 0, 4, 3, 3, 3, 4, 1, 4, 3, 2, 1, 5, 5, 3, 4, 5, 1, 3, 5, 4, 2, 0, 3, 3, 0, 1, 3, 0, 4, 2, 0, 1, 3, 1, 4, 3, 3, 3, 3, 0, 3, 0, 1, 0, 3, 4, 4, 4, 5, 5, 0, 3, 0, 1, 4, 5), + (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 3, 1, 3, 0, 4, 0, 1, 1, 3, 0, 3, 4, 3, 2, 3, 1, 0, 3, 3, 2, 3, 1, 3, 0, 2, 3, 0, 2, 1, 4, 1, 2, 2, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 2, 2, 0, 3, 2, 1, 3, 3, 0, 2, 0, 2, 0, 0, 3, 3, 1, 2, 4, 0, 3, 0, 2, 2, 3), + (2, 4, 0, 5, 0, 4, 0, 4, 0, 2, 4, 4, 4, 3, 4, 3, 3, 3, 1, 2, 4, 3, 4, 3, 4, 4, 5, 0, 3, 3, 3, 3, 2, 0, 4, 3, 1, 4, 3, 4, 1, 4, 4, 3, 3, 4, 4, 3, 1, 2, 3, 0, 4, 2, 0, 4, 1, 0, 3, 3, 0, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 3, 5, 3, 4, 5, 2, 0, 3, 0, 0, 4, 5), + (0, 3, 0, 4, 0, 1, 0, 1, 0, 1, 3, 2, 2, 1, 3, 0, 3, 0, 2, 0, 2, 0, 3, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 0, 3, 1, 0, 0, 0, 4, 0, 3, 1, 0, 2, 1, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 3, 1, 0, 3, 0, 0, 0, 1, 4, 4, 4, 3, 0, 0, 4, 0, 0, 1, 4), + (1, 4, 1, 5, 0, 3, 0, 3, 0, 4, 5, 4, 4, 3, 5, 3, 3, 4, 4, 3, 4, 1, 3, 3, 3, 3, 2, 1, 4, 1, 5, 4, 3, 1, 4, 4, 3, 5, 4, 4, 3, 5, 4, 3, 3, 4, 4, 4, 0, 3, 3, 1, 2, 3, 0, 3, 1, 0, 3, 3, 0, 5, 4, 4, 4, 4, 4, 4, 3, 3, 5, 4, 4, 3, 3, 5, 4, 0, 3, 2, 0, 4, 4), + (0, 2, 0, 3, 0, 1, 0, 0, 0, 1, 3, 3, 3, 2, 4, 1, 3, 0, 3, 1, 3, 0, 2, 2, 1, 1, 0, 0, 2, 0, 4, 3, 1, 0, 4, 3, 0, 4, 4, 4, 1, 4, 3, 1, 1, 3, 3, 1, 0, 2, 0, 0, 1, 3, 0, 0, 0, 0, 2, 0, 0, 4, 3, 2, 4, 3, 5, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 0, 2, 1, 0, 3, 3), + (0, 2, 0, 4, 0, 3, 0, 2, 0, 2, 5, 5, 3, 4, 4, 4, 4, 1, 4, 3, 3, 0, 4, 3, 4, 3, 1, 3, 3, 2, 4, 3, 0, 3, 4, 3, 0, 3, 4, 4, 2, 4, 4, 0, 4, 5, 3, 3, 2, 2, 1, 1, 1, 2, 0, 1, 5, 0, 3, 3, 2, 4, 3, 3, 3, 4, 0, 3, 0, 2, 0, 4, 4, 3, 5, 5, 0, 0, 3, 0, 2, 3, 3), + (0, 3, 0, 4, 0, 3, 0, 1, 0, 3, 4, 3, 3, 1, 3, 3, 3, 0, 3, 1, 3, 0, 4, 3, 3, 1, 1, 0, 3, 0, 3, 3, 0, 0, 4, 4, 0, 1, 5, 4, 3, 3, 5, 0, 3, 3, 4, 3, 0, 2, 0, 1, 1, 1, 0, 1, 3, 0, 1, 2, 1, 3, 3, 2, 3, 3, 0, 3, 0, 1, 0, 1, 3, 3, 4, 4, 1, 0, 1, 2, 2, 1, 3), + (0, 1, 0, 4, 0, 4, 0, 3, 0, 1, 3, 3, 3, 2, 3, 1, 1, 0, 3, 0, 3, 3, 4, 3, 2, 4, 2, 0, 1, 0, 4, 3, 2, 0, 4, 3, 0, 5, 3, 3, 2, 4, 4, 4, 3, 3, 3, 4, 0, 1, 3, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 4, 2, 3, 3, 3, 0, 3, 0, 0, 0, 4, 4, 4, 5, 3, 2, 0, 3, 3, 0, 3, 5), + (0, 2, 0, 3, 0, 0, 0, 3, 0, 1, 3, 0, 2, 0, 0, 0, 1, 0, 3, 1, 1, 3, 3, 0, 0, 3, 0, 0, 3, 0, 2, 3, 1, 0, 3, 1, 0, 3, 3, 2, 0, 4, 2, 2, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1, 0, 1, 0, 0, 0, 1, 3, 1, 2, 0, 0, 0, 1, 0, 0, 1, 4), + (0, 3, 0, 3, 0, 5, 0, 1, 0, 2, 4, 3, 1, 3, 3, 2, 1, 1, 5, 2, 1, 0, 5, 1, 2, 0, 0, 0, 3, 3, 2, 2, 3, 2, 4, 3, 0, 0, 3, 3, 1, 3, 3, 0, 2, 5, 3, 4, 0, 3, 3, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0, 2, 2, 3, 3, 3, 0, 2, 0, 1, 0, 3, 4, 4, 2, 5, 4, 0, 3, 0, 0, 3, 5), + (0, 3, 0, 3, 0, 3, 0, 1, 0, 3, 3, 3, 3, 0, 3, 0, 2, 0, 2, 1, 1, 0, 2, 0, 1, 0, 0, 0, 2, 1, 0, 0, 1, 0, 3, 2, 0, 0, 3, 3, 1, 2, 3, 1, 0, 3, 3, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 3, 1, 2, 3, 0, 3, 0, 1, 0, 3, 2, 1, 0, 4, 3, 0, 1, 1, 0, 3, 3), + (0, 4, 0, 5, 0, 3, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 4, 3, 5, 3, 3, 2, 5, 3, 4, 4, 4, 3, 4, 3, 4, 5, 5, 3, 4, 4, 3, 4, 4, 5, 4, 4, 4, 3, 4, 5, 5, 4, 2, 3, 4, 2, 3, 4, 0, 3, 3, 1, 4, 3, 2, 4, 3, 3, 5, 5, 0, 3, 0, 3, 0, 5, 5, 5, 5, 4, 4, 0, 4, 0, 1, 4, 4), + (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 5, 4, 4, 2, 3, 2, 5, 1, 3, 2, 5, 1, 4, 2, 3, 2, 3, 3, 4, 3, 3, 3, 3, 2, 5, 4, 1, 3, 3, 5, 3, 4, 4, 0, 4, 4, 3, 1, 1, 3, 1, 0, 2, 3, 0, 2, 3, 0, 3, 0, 0, 4, 3, 1, 3, 4, 0, 3, 0, 2, 0, 4, 4, 4, 3, 4, 5, 0, 4, 0, 0, 3, 4), + (0, 3, 0, 3, 0, 3, 1, 2, 0, 3, 4, 4, 3, 3, 3, 0, 2, 2, 4, 3, 3, 1, 3, 3, 3, 1, 1, 0, 3, 1, 4, 3, 2, 3, 4, 4, 2, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 3, 1, 3, 3, 1, 3, 3, 0, 4, 1, 0, 2, 2, 1, 4, 3, 2, 3, 3, 5, 4, 3, 3, 5, 4, 4, 3, 3, 0, 4, 0, 3, 2, 2, 4, 4), + (0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 2, 1, 3, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 1, 0, 1, 1, 3, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 0, 3, 4, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1), + (0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 4, 1, 4, 0, 3, 0, 4, 0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 1, 5, 1, 4, 0, 0, 3, 0, 5, 0, 5, 2, 0, 1, 0, 0, 0, 2, 1, 4, 0, 1, 3, 0, 0, 3, 0, 0, 3, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), + (1, 4, 0, 5, 0, 3, 0, 2, 0, 3, 5, 4, 4, 3, 4, 3, 5, 3, 4, 3, 3, 0, 4, 3, 3, 3, 3, 3, 3, 2, 4, 4, 3, 1, 3, 4, 4, 5, 4, 4, 3, 4, 4, 1, 3, 5, 4, 3, 3, 3, 1, 2, 2, 3, 3, 1, 3, 1, 3, 3, 3, 5, 3, 3, 4, 5, 0, 3, 0, 3, 0, 3, 4, 3, 4, 4, 3, 0, 3, 0, 2, 4, 3), + (0, 1, 0, 4, 0, 0, 0, 0, 0, 1, 4, 0, 4, 1, 4, 2, 4, 0, 3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 2, 0, 3, 1, 1, 1, 0, 3, 0, 0, 0, 1, 2, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 3, 2, 0, 2, 2, 0, 1, 0, 0, 0, 2, 3, 2, 3, 3, 0, 0, 0, 0, 2, 1, 0), + (0, 5, 1, 5, 0, 3, 0, 3, 0, 5, 4, 4, 5, 1, 5, 3, 3, 0, 4, 3, 4, 3, 5, 3, 4, 3, 3, 2, 4, 3, 4, 3, 3, 0, 3, 3, 1, 4, 4, 3, 4, 4, 4, 3, 4, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 3, 1, 1, 3, 3, 2, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 5, 3, 3, 0, 3, 4, 0, 4, 3), + (0, 5, 0, 5, 0, 3, 0, 2, 0, 4, 4, 3, 5, 2, 4, 3, 3, 3, 4, 4, 4, 3, 5, 3, 5, 3, 3, 1, 4, 0, 4, 3, 3, 0, 3, 3, 0, 4, 4, 4, 4, 5, 4, 3, 3, 5, 5, 3, 2, 3, 1, 2, 3, 2, 0, 1, 0, 0, 3, 2, 2, 4, 4, 3, 1, 5, 0, 4, 0, 3, 0, 4, 3, 1, 3, 2, 1, 0, 3, 3, 0, 3, 3), + (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 5, 5, 3, 4, 3, 3, 2, 5, 4, 4, 3, 5, 3, 5, 3, 4, 0, 4, 3, 4, 4, 3, 2, 4, 4, 3, 4, 5, 4, 4, 5, 5, 0, 3, 5, 5, 4, 1, 3, 3, 2, 3, 3, 1, 3, 1, 0, 4, 3, 1, 4, 4, 3, 4, 5, 0, 4, 0, 2, 0, 4, 3, 4, 4, 3, 3, 0, 4, 0, 0, 5, 5), + (0, 4, 0, 4, 0, 5, 0, 1, 1, 3, 3, 4, 4, 3, 4, 1, 3, 0, 5, 1, 3, 0, 3, 1, 3, 1, 1, 0, 3, 0, 3, 3, 4, 0, 4, 3, 0, 4, 4, 4, 3, 4, 4, 0, 3, 5, 4, 1, 0, 3, 0, 0, 2, 3, 0, 3, 1, 0, 3, 1, 0, 3, 2, 1, 3, 5, 0, 3, 0, 1, 0, 3, 2, 3, 3, 4, 4, 0, 2, 2, 0, 4, 4), + (2, 4, 0, 5, 0, 4, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 5, 3, 5, 3, 5, 2, 5, 3, 4, 3, 3, 4, 3, 4, 5, 3, 2, 1, 5, 4, 3, 2, 3, 4, 5, 3, 4, 1, 2, 5, 4, 3, 0, 3, 3, 0, 3, 2, 0, 2, 3, 0, 4, 1, 0, 3, 4, 3, 3, 5, 0, 3, 0, 1, 0, 4, 5, 5, 5, 4, 3, 0, 4, 2, 0, 3, 5), + (0, 5, 0, 4, 0, 4, 0, 2, 0, 5, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 2, 5, 3, 5, 3, 4, 1, 4, 3, 4, 4, 4, 0, 3, 5, 0, 4, 4, 4, 4, 5, 3, 1, 3, 4, 5, 3, 3, 3, 3, 3, 3, 3, 0, 2, 2, 0, 3, 3, 2, 4, 3, 3, 3, 5, 3, 4, 1, 3, 3, 5, 3, 2, 0, 0, 0, 0, 4, 3, 1, 3, 3), + (0, 1, 0, 3, 0, 3, 0, 1, 0, 1, 3, 3, 3, 2, 3, 3, 3, 0, 3, 0, 0, 0, 3, 1, 3, 0, 0, 0, 2, 2, 2, 3, 0, 0, 3, 2, 0, 1, 2, 4, 1, 3, 3, 0, 0, 3, 3, 3, 0, 1, 0, 0, 2, 1, 0, 0, 3, 0, 3, 1, 0, 3, 0, 0, 1, 3, 0, 2, 0, 1, 0, 3, 3, 1, 3, 3, 0, 0, 1, 1, 0, 3, 3), + (0, 2, 0, 3, 0, 2, 1, 4, 0, 2, 2, 3, 1, 1, 3, 1, 1, 0, 2, 0, 3, 1, 2, 3, 1, 3, 0, 0, 1, 0, 4, 3, 2, 3, 3, 3, 1, 4, 2, 3, 3, 3, 3, 1, 0, 3, 1, 4, 0, 1, 1, 0, 1, 2, 0, 1, 1, 0, 1, 1, 0, 3, 1, 3, 2, 2, 0, 1, 0, 0, 0, 2, 3, 3, 3, 1, 0, 0, 0, 0, 0, 2, 3), + (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 5, 5, 3, 3, 4, 3, 3, 1, 5, 4, 4, 2, 4, 4, 4, 3, 4, 2, 4, 3, 5, 5, 4, 3, 3, 4, 3, 3, 5, 5, 4, 5, 5, 1, 3, 4, 5, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 1, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 5, 3, 3, 1, 4, 3, 0, 4, 0, 1, 5, 3), + (0, 5, 0, 5, 0, 4, 0, 2, 0, 4, 4, 3, 4, 3, 3, 3, 3, 3, 5, 4, 4, 4, 4, 4, 4, 5, 3, 3, 5, 2, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4, 5, 5, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 3, 3, 1, 2, 2, 1, 4, 3, 3, 5, 4, 4, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 4, 4, 1, 0, 4, 2, 0, 2, 4), + (0, 4, 0, 4, 0, 3, 0, 1, 0, 3, 5, 2, 3, 0, 3, 0, 2, 1, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 1, 3, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 5, 3, 3, 3, 3, 3, 2, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 3, 1, 2, 2, 3, 0, 3, 0, 2, 0, 4, 4, 3, 3, 4, 1, 0, 3, 0, 0, 2, 4), + (0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 3, 1, 3, 0, 3, 2, 0, 0, 0, 1, 0, 3, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 2, 0, 0, 0, 0, 0, 0, 2), + (0, 2, 1, 3, 0, 2, 0, 2, 0, 3, 3, 3, 3, 1, 3, 1, 3, 3, 3, 3, 3, 3, 4, 2, 2, 1, 2, 1, 4, 0, 4, 3, 1, 3, 3, 3, 2, 4, 3, 5, 4, 3, 3, 3, 3, 3, 3, 3, 0, 1, 3, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 4, 2, 0, 2, 3, 0, 3, 3, 0, 3, 3, 4, 2, 3, 1, 4, 0, 1, 2, 0, 2, 3), + (0, 3, 0, 3, 0, 1, 0, 3, 0, 2, 3, 3, 3, 0, 3, 1, 2, 0, 3, 3, 2, 3, 3, 2, 3, 2, 3, 1, 3, 0, 4, 3, 2, 0, 3, 3, 1, 4, 3, 3, 2, 3, 4, 3, 1, 3, 3, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 4, 1, 1, 0, 3, 0, 3, 1, 0, 2, 3, 3, 3, 3, 3, 1, 0, 0, 2, 0, 3, 3), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3), + (0, 2, 0, 3, 1, 3, 0, 3, 0, 2, 3, 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 1, 3, 0, 2, 3, 1, 1, 4, 3, 3, 2, 3, 3, 1, 2, 2, 4, 1, 3, 3, 0, 1, 4, 2, 3, 0, 1, 3, 0, 3, 0, 0, 1, 3, 0, 2, 0, 0, 3, 3, 2, 1, 3, 0, 3, 0, 2, 0, 3, 4, 4, 4, 3, 1, 0, 3, 0, 0, 3, 3), + (0, 2, 0, 1, 0, 2, 0, 0, 0, 1, 3, 2, 2, 1, 3, 0, 1, 1, 3, 0, 3, 2, 3, 1, 2, 0, 2, 0, 1, 1, 3, 3, 3, 0, 3, 3, 1, 1, 2, 3, 2, 3, 3, 1, 2, 3, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 2, 1, 2, 1, 3, 0, 3, 0, 0, 0, 3, 4, 4, 4, 3, 2, 0, 2, 0, 0, 2, 4), + (0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 3), + (0, 3, 0, 3, 0, 2, 0, 3, 0, 3, 3, 3, 2, 3, 2, 2, 2, 0, 3, 1, 3, 3, 3, 2, 3, 3, 0, 0, 3, 0, 3, 2, 2, 0, 2, 3, 1, 4, 3, 4, 3, 3, 2, 3, 1, 5, 4, 4, 0, 3, 1, 2, 1, 3, 0, 3, 1, 1, 2, 0, 2, 3, 1, 3, 1, 3, 0, 3, 0, 1, 0, 3, 3, 4, 4, 2, 1, 0, 2, 1, 0, 2, 4), + (0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 4, 2, 5, 1, 4, 0, 2, 0, 2, 1, 3, 1, 4, 0, 2, 1, 0, 0, 2, 1, 4, 1, 1, 0, 3, 3, 0, 5, 1, 3, 2, 3, 3, 1, 0, 3, 2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 1, 0, 3, 0, 2, 0, 1, 0, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 2, 3), + (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 3), + (0, 1, 0, 3, 0, 4, 0, 3, 0, 2, 4, 3, 1, 0, 3, 2, 2, 1, 3, 1, 2, 2, 3, 1, 1, 1, 2, 1, 3, 0, 1, 2, 0, 1, 3, 2, 1, 3, 0, 5, 5, 1, 0, 0, 1, 3, 2, 1, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 3, 4, 0, 1, 1, 1, 3, 2, 0, 2, 0, 1, 0, 2, 3, 3, 1, 2, 3, 0, 1, 0, 1, 0, 4), + (0, 0, 0, 1, 0, 3, 0, 3, 0, 2, 2, 1, 0, 0, 4, 0, 3, 0, 3, 1, 3, 0, 3, 0, 3, 0, 1, 0, 3, 0, 3, 1, 3, 0, 3, 3, 0, 0, 1, 2, 1, 1, 1, 0, 1, 2, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 2, 0, 0, 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, 1, 4), + (0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, 1, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 2, 3, 0, 0, 2, 2, 3, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 2, 3), + (2, 4, 0, 5, 0, 5, 0, 4, 0, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 2, 3, 0, 5, 5, 4, 1, 5, 4, 3, 1, 5, 4, 3, 4, 4, 3, 3, 4, 3, 3, 0, 3, 2, 0, 2, 3, 0, 3, 0, 0, 3, 3, 0, 5, 3, 2, 3, 3, 0, 3, 0, 3, 0, 3, 4, 5, 4, 5, 3, 0, 4, 3, 0, 3, 4), + (0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 3, 4, 3, 2, 3, 2, 3, 0, 4, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 2, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 1, 0, 2, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0, 5, 3, 2, 1, 3, 0, 3, 0, 1, 2, 4, 3, 2, 4, 3, 3, 0, 3, 2, 0, 4, 4), + (0, 3, 0, 3, 0, 1, 0, 0, 0, 1, 4, 3, 3, 2, 3, 1, 3, 1, 4, 2, 3, 2, 4, 2, 3, 4, 3, 0, 2, 2, 3, 3, 3, 0, 3, 3, 3, 0, 3, 4, 1, 3, 3, 0, 3, 4, 3, 3, 0, 1, 1, 0, 1, 0, 0, 0, 4, 0, 3, 0, 0, 3, 1, 2, 1, 3, 0, 4, 0, 1, 0, 4, 3, 3, 4, 3, 3, 0, 2, 0, 0, 3, 3), + (0, 3, 0, 4, 0, 1, 0, 3, 0, 3, 4, 3, 3, 0, 3, 3, 3, 1, 3, 1, 3, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 3, 3, 1, 3, 3, 2, 5, 4, 3, 3, 4, 5, 3, 2, 5, 3, 4, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 0, 4, 2, 2, 1, 3, 0, 3, 0, 2, 0, 4, 4, 3, 5, 3, 2, 0, 1, 1, 0, 3, 4), + (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 4, 3, 3, 2, 3, 3, 3, 1, 4, 3, 4, 1, 5, 3, 4, 3, 4, 0, 4, 2, 4, 3, 4, 1, 5, 4, 0, 4, 4, 4, 4, 5, 4, 1, 3, 5, 4, 2, 1, 4, 1, 1, 3, 2, 0, 3, 1, 0, 3, 2, 1, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 3, 3, 3, 0, 4, 2, 0, 3, 4), + (1, 4, 0, 4, 0, 3, 0, 1, 0, 3, 3, 3, 1, 1, 3, 3, 2, 2, 3, 3, 1, 0, 3, 2, 2, 1, 2, 0, 3, 1, 2, 1, 2, 0, 3, 2, 0, 2, 2, 3, 3, 4, 3, 0, 3, 3, 1, 2, 0, 1, 1, 3, 1, 2, 0, 0, 3, 0, 1, 1, 0, 3, 2, 2, 3, 3, 0, 3, 0, 0, 0, 2, 3, 3, 4, 3, 3, 0, 1, 0, 0, 1, 4), + (0, 4, 0, 4, 0, 4, 0, 0, 0, 3, 4, 4, 3, 1, 4, 2, 3, 2, 3, 3, 3, 1, 4, 3, 4, 0, 3, 0, 4, 2, 3, 3, 2, 2, 5, 4, 2, 1, 3, 4, 3, 4, 3, 1, 3, 3, 4, 2, 0, 2, 1, 0, 3, 3, 0, 0, 2, 0, 3, 1, 0, 4, 4, 3, 4, 3, 0, 4, 0, 1, 0, 2, 4, 4, 4, 4, 4, 0, 3, 2, 0, 3, 3), + (0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2), + (0, 2, 0, 3, 0, 4, 0, 4, 0, 1, 3, 3, 3, 0, 4, 0, 2, 1, 2, 1, 1, 1, 2, 0, 3, 1, 1, 0, 1, 0, 3, 1, 0, 0, 3, 3, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 2, 2, 0, 3, 1, 0, 0, 1, 0, 1, 1, 0, 1, 2, 0, 3, 0, 0, 0, 0, 1, 0, 0, 3, 3, 4, 3, 1, 0, 1, 0, 3, 0, 2), + (0, 0, 0, 3, 0, 5, 0, 0, 0, 0, 1, 0, 2, 0, 3, 1, 0, 1, 3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 4, 0, 0, 0, 2, 3, 0, 1, 4, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 3), + (0, 2, 0, 5, 0, 5, 0, 1, 0, 2, 4, 3, 3, 2, 5, 1, 3, 2, 3, 3, 3, 0, 4, 1, 2, 0, 3, 0, 4, 0, 2, 2, 1, 1, 5, 3, 0, 0, 1, 4, 2, 3, 2, 0, 3, 3, 3, 2, 0, 2, 4, 1, 1, 2, 0, 1, 1, 0, 3, 1, 0, 1, 3, 1, 2, 3, 0, 2, 0, 0, 0, 1, 3, 5, 4, 4, 4, 0, 3, 0, 0, 1, 3), + (0, 4, 0, 5, 0, 4, 0, 4, 0, 4, 5, 4, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 3, 4, 5, 4, 2, 4, 2, 3, 4, 3, 1, 4, 4, 1, 3, 5, 4, 4, 5, 5, 4, 4, 5, 5, 5, 2, 3, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 4, 4, 4, 0, 3, 0, 4, 0, 3, 3, 4, 4, 5, 0, 0, 4, 3, 0, 4, 5), + (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 4, 4, 4, 3, 3, 2, 4, 3, 4, 3, 4, 3, 5, 3, 4, 3, 2, 1, 4, 2, 4, 4, 3, 1, 3, 4, 2, 4, 5, 5, 3, 4, 5, 4, 1, 5, 4, 3, 0, 3, 2, 2, 3, 2, 1, 3, 1, 0, 3, 3, 3, 5, 3, 3, 3, 5, 4, 4, 2, 3, 3, 4, 3, 3, 3, 2, 1, 0, 3, 2, 1, 4, 3), + (0, 4, 0, 5, 0, 4, 0, 3, 0, 3, 5, 5, 3, 2, 4, 3, 4, 0, 5, 4, 4, 1, 4, 4, 4, 3, 3, 3, 4, 3, 5, 5, 2, 3, 3, 4, 1, 2, 5, 5, 3, 5, 5, 2, 3, 5, 5, 4, 0, 3, 2, 0, 3, 3, 1, 1, 5, 1, 4, 1, 0, 4, 3, 2, 3, 5, 0, 4, 0, 3, 0, 5, 4, 3, 4, 3, 0, 0, 4, 1, 0, 4, 4), + (1, 3, 0, 4, 0, 2, 0, 2, 0, 2, 5, 5, 3, 3, 3, 3, 3, 0, 4, 2, 3, 4, 4, 4, 3, 4, 0, 0, 3, 4, 5, 4, 3, 3, 3, 3, 2, 5, 5, 4, 5, 5, 5, 4, 3, 5, 5, 5, 1, 3, 1, 0, 1, 0, 0, 3, 2, 0, 4, 2, 0, 5, 2, 3, 2, 4, 1, 3, 0, 3, 0, 4, 5, 4, 5, 4, 3, 0, 4, 2, 0, 5, 4), + (0, 3, 0, 4, 0, 5, 0, 3, 0, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 3, 3, 2, 4, 3, 3, 2, 2, 0, 3, 3, 3, 3, 3, 1, 3, 3, 3, 0, 4, 4, 3, 4, 4, 1, 1, 4, 4, 2, 0, 3, 1, 0, 1, 1, 0, 4, 1, 0, 2, 3, 1, 3, 3, 1, 3, 4, 0, 3, 0, 1, 0, 3, 1, 3, 0, 0, 1, 0, 2, 0, 0, 4, 4), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + (0, 3, 0, 3, 0, 2, 0, 3, 0, 1, 5, 4, 3, 3, 3, 1, 4, 2, 1, 2, 3, 4, 4, 2, 4, 4, 5, 0, 3, 1, 4, 3, 4, 0, 4, 3, 3, 3, 2, 3, 2, 5, 3, 4, 3, 2, 2, 3, 0, 0, 3, 0, 2, 1, 0, 1, 2, 0, 0, 0, 0, 2, 1, 1, 3, 1, 0, 2, 0, 4, 0, 3, 4, 4, 4, 5, 2, 0, 2, 0, 0, 1, 3), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 4, 2, 1, 1, 0, 1, 0, 3, 2, 0, 0, 3, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 4, 0, 4, 2, 1, 0, 0, 0, 0, 0, 1), + (0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 2, 0, 2, 1, 0, 0, 1, 2, 1, 0, 1, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2), + (0, 4, 0, 4, 0, 4, 0, 3, 0, 4, 4, 3, 4, 2, 4, 3, 2, 0, 4, 4, 4, 3, 5, 3, 5, 3, 3, 2, 4, 2, 4, 3, 4, 3, 1, 4, 0, 2, 3, 4, 4, 4, 3, 3, 3, 4, 4, 4, 3, 4, 1, 3, 4, 3, 2, 1, 2, 1, 3, 3, 3, 4, 4, 3, 3, 5, 0, 4, 0, 3, 0, 4, 3, 3, 3, 2, 1, 0, 3, 0, 0, 3, 3), + (0, 4, 0, 3, 0, 3, 0, 3, 0, 3, 5, 5, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 4, 3, 5, 3, 3, 1, 3, 2, 4, 5, 5, 5, 5, 4, 3, 4, 5, 5, 3, 2, 2, 3, 3, 3, 3, 2, 3, 3, 1, 2, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 4, 3, 2, 2, 1, 2, 0, 3, 0, 0, 4, 1), +) +# fmt: on + + +class JapaneseContextAnalysis: + NUM_OF_CATEGORY = 6 + DONT_KNOW = -1 + ENOUGH_REL_THRESHOLD = 100 + MAX_REL_THRESHOLD = 1000 + MINIMUM_DATA_THRESHOLD = 4 + + def __init__(self) -> None: + self._total_rel = 0 + self._rel_sample: List[int] = [] + self._need_to_skip_char_num = 0 + self._last_char_order = -1 + self._done = False + self.reset() + + def reset(self) -> None: + self._total_rel = 0 # total sequence received + # category counters, each integer counts sequence in its category + self._rel_sample = [0] * self.NUM_OF_CATEGORY + # if last byte in current buffer is not the last byte of a character, + # we need to know how many bytes to skip in next buffer + self._need_to_skip_char_num = 0 + self._last_char_order = -1 # The order of previous char + # If this flag is set to True, detection is done and conclusion has + # been made + self._done = False + + def feed(self, byte_str: Union[bytes, bytearray], num_bytes: int) -> None: + if self._done: + return + + # The buffer we got is byte oriented, and a character may span in more than one + # buffers. In case the last one or two byte in last buffer is not + # complete, we record how many byte needed to complete that character + # and skip these bytes here. We can choose to record those bytes as + # well and analyse the character once it is complete, but since a + # character will not make much difference, by simply skipping + # this character will simply our logic and improve performance. + i = self._need_to_skip_char_num + while i < num_bytes: + order, char_len = self.get_order(byte_str[i : i + 2]) + i += char_len + if i > num_bytes: + self._need_to_skip_char_num = i - num_bytes + self._last_char_order = -1 + else: + if (order != -1) and (self._last_char_order != -1): + self._total_rel += 1 + if self._total_rel > self.MAX_REL_THRESHOLD: + self._done = True + break + self._rel_sample[ + jp2_char_context[self._last_char_order][order] + ] += 1 + self._last_char_order = order + + def got_enough_data(self) -> bool: + return self._total_rel > self.ENOUGH_REL_THRESHOLD + + def get_confidence(self) -> float: + # This is just one way to calculate confidence. It works well for me. + if self._total_rel > self.MINIMUM_DATA_THRESHOLD: + return (self._total_rel - self._rel_sample[0]) / self._total_rel + return self.DONT_KNOW + + def get_order(self, _: Union[bytes, bytearray]) -> Tuple[int, int]: + return -1, 1 + + +class SJISContextAnalysis(JapaneseContextAnalysis): + def __init__(self) -> None: + super().__init__() + self._charset_name = "SHIFT_JIS" + + @property + def charset_name(self) -> str: + return self._charset_name + + def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]: + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): + char_len = 2 + if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): + self._charset_name = "CP932" + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 202) and (0x9F <= second_char <= 0xF1): + return second_char - 0x9F, char_len + + return -1, char_len + + +class EUCJPContextAnalysis(JapaneseContextAnalysis): + def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]: + if not byte_str: + return -1, 1 + # find out current char's byte length + first_char = byte_str[0] + if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): + char_len = 2 + elif first_char == 0x8F: + char_len = 3 + else: + char_len = 1 + + # return its order if it is hiragana + if len(byte_str) > 1: + second_char = byte_str[1] + if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): + return second_char - 0xA1, char_len + + return -1, char_len diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langbulgarianmodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langbulgarianmodel.py new file mode 100644 index 0000000..9946682 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langbulgarianmodel.py @@ -0,0 +1,4649 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +BULGARIAN_LANG_MODEL = { + 63: { # 'e' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 1, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 45: { # '\xad' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 1, # 'М' + 36: 0, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 31: { # 'А' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 1, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 2, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 2, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 0, # 'и' + 26: 2, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 32: { # 'Б' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 1, # 'Щ' + 61: 2, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 35: { # 'В' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 2, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 2, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 43: { # 'Г' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 1, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 37: { # 'Д' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 2, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 44: { # 'Е' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 2, # 'Ф' + 49: 1, # 'Х' + 53: 2, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 0, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 55: { # 'Ж' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 47: { # 'З' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 40: { # 'И' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 2, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 2, # 'М' + 36: 2, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 2, # 'Я' + 1: 1, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 3, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 0, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 59: { # 'Й' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 33: { # 'К' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 46: { # 'Л' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 2, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 38: { # 'М' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 36: { # 'Н' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 2, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 41: { # 'О' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 1, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 1, # 'Й' + 33: 2, # 'К' + 46: 2, # 'Л' + 38: 2, # 'М' + 36: 2, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 1, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 0, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 2, # 'ч' + 27: 0, # 'ш' + 24: 2, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 30: { # 'П' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 2, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 39: { # 'Р' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 2, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 1, # 'с' + 5: 0, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 28: { # 'С' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 3, # 'А' + 32: 2, # 'Б' + 35: 2, # 'В' + 43: 1, # 'Г' + 37: 2, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 2, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 1, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 34: { # 'Т' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 2, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 2, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 1, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 1, # 'Ъ' + 60: 0, # 'Ю' + 56: 1, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 3, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 51: { # 'У' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 2, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 2, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 48: { # 'Ф' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 2, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 1, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 2, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 49: { # 'Х' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 1, # 'П' + 39: 1, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 53: { # 'Ц' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 2, # 'И' + 59: 0, # 'Й' + 33: 2, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 2, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 50: { # 'Ч' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 2, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 2, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 54: { # 'Ш' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 1, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 1, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 2, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 57: { # 'Щ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 1, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 1, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 1, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 61: { # 'Ъ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 1, # 'Ж' + 47: 1, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 2, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 2, # 'Р' + 28: 1, # 'С' + 34: 1, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 1, # 'Х' + 53: 1, # 'Ц' + 50: 1, # 'Ч' + 54: 1, # 'Ш' + 57: 1, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 1, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 60: { # 'Ю' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 1, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 0, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 0, # 'е' + 23: 2, # 'ж' + 15: 1, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 0, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 56: { # 'Я' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 1, # 'В' + 43: 1, # 'Г' + 37: 1, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 1, # 'Л' + 38: 1, # 'М' + 36: 1, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 2, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 1, # 'и' + 26: 1, # 'й' + 12: 1, # 'к' + 10: 1, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 0, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 1: { # 'а' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 1, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 18: { # 'б' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 0, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 2, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 3, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 9: { # 'в' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 1, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 0, # 'в' + 20: 2, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 20: { # 'г' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 11: { # 'д' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 1, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 3: { # 'е' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 2, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 23: { # 'ж' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 15: { # 'з' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 2: { # 'и' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 1, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 1, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 1, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 1, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 26: { # 'й' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 2, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 12: { # 'к' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 1, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 1, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 3, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 10: { # 'л' + 63: 1, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 1, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 1, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 3, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 14: { # 'м' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 1, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 1, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 6: { # 'н' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 1, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 2, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 2, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 3, # 'ф' + 25: 2, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 4: { # 'о' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 2, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 3, # 'и' + 26: 3, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 2, # 'у' + 29: 3, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 3, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 13: { # 'п' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 3, # 'л' + 14: 1, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 7: { # 'р' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 3, # 'е' + 23: 3, # 'ж' + 15: 2, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 3, # 'х' + 22: 3, # 'ц' + 21: 2, # 'ч' + 27: 3, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 1, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 8: { # 'с' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 2, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 2, # 'ш' + 24: 0, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 5: { # 'т' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 2, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 3, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 2, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 3, # 'ъ' + 52: 2, # 'ь' + 42: 2, # 'ю' + 16: 3, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 19: { # 'у' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 2, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 2, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 3, # 'ш' + 24: 2, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 29: { # 'ф' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 1, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 2, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 2, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 25: { # 'х' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 2, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 1, # 'п' + 7: 3, # 'р' + 8: 1, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 1, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 22: { # 'ц' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 2, # 'в' + 20: 1, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 1, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 2, # 'к' + 10: 1, # 'л' + 14: 1, # 'м' + 6: 1, # 'н' + 4: 2, # 'о' + 13: 1, # 'п' + 7: 1, # 'р' + 8: 1, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 1, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 0, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 21: { # 'ч' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 1, # 'б' + 9: 3, # 'в' + 20: 1, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 1, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 2, # 'р' + 8: 0, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 1, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 27: { # 'ш' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 2, # 'в' + 20: 0, # 'г' + 11: 1, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 3, # 'к' + 10: 2, # 'л' + 14: 1, # 'м' + 6: 3, # 'н' + 4: 2, # 'о' + 13: 2, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 2, # 'у' + 29: 1, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 1, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 2, # 'ъ' + 52: 1, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 24: { # 'щ' + 63: 1, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 3, # 'а' + 18: 0, # 'б' + 9: 1, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 3, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 3, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 2, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 1, # 'р' + 8: 0, # 'с' + 5: 2, # 'т' + 19: 3, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 2, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 17: { # 'ъ' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 3, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 3, # 'ж' + 15: 3, # 'з' + 2: 1, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 3, # 'о' + 13: 3, # 'п' + 7: 3, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 2, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 2, # 'ш' + 24: 3, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 2, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 52: { # 'ь' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 1, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 1, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 1, # 'н' + 4: 3, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 1, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 1, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 1, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 42: { # 'ю' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 1, # 'а' + 18: 2, # 'б' + 9: 1, # 'в' + 20: 2, # 'г' + 11: 2, # 'д' + 3: 1, # 'е' + 23: 2, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 1, # 'й' + 12: 2, # 'к' + 10: 2, # 'л' + 14: 2, # 'м' + 6: 2, # 'н' + 4: 1, # 'о' + 13: 1, # 'п' + 7: 2, # 'р' + 8: 2, # 'с' + 5: 2, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 1, # 'х' + 22: 2, # 'ц' + 21: 3, # 'ч' + 27: 1, # 'ш' + 24: 1, # 'щ' + 17: 1, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 16: { # 'я' + 63: 0, # 'e' + 45: 1, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 3, # 'б' + 9: 3, # 'в' + 20: 2, # 'г' + 11: 3, # 'д' + 3: 2, # 'е' + 23: 1, # 'ж' + 15: 2, # 'з' + 2: 1, # 'и' + 26: 2, # 'й' + 12: 3, # 'к' + 10: 3, # 'л' + 14: 3, # 'м' + 6: 3, # 'н' + 4: 1, # 'о' + 13: 2, # 'п' + 7: 2, # 'р' + 8: 3, # 'с' + 5: 3, # 'т' + 19: 1, # 'у' + 29: 1, # 'ф' + 25: 3, # 'х' + 22: 2, # 'ц' + 21: 1, # 'ч' + 27: 1, # 'ш' + 24: 2, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 1, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 58: { # 'є' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, + 62: { # '№' + 63: 0, # 'e' + 45: 0, # '\xad' + 31: 0, # 'А' + 32: 0, # 'Б' + 35: 0, # 'В' + 43: 0, # 'Г' + 37: 0, # 'Д' + 44: 0, # 'Е' + 55: 0, # 'Ж' + 47: 0, # 'З' + 40: 0, # 'И' + 59: 0, # 'Й' + 33: 0, # 'К' + 46: 0, # 'Л' + 38: 0, # 'М' + 36: 0, # 'Н' + 41: 0, # 'О' + 30: 0, # 'П' + 39: 0, # 'Р' + 28: 0, # 'С' + 34: 0, # 'Т' + 51: 0, # 'У' + 48: 0, # 'Ф' + 49: 0, # 'Х' + 53: 0, # 'Ц' + 50: 0, # 'Ч' + 54: 0, # 'Ш' + 57: 0, # 'Щ' + 61: 0, # 'Ъ' + 60: 0, # 'Ю' + 56: 0, # 'Я' + 1: 0, # 'а' + 18: 0, # 'б' + 9: 0, # 'в' + 20: 0, # 'г' + 11: 0, # 'д' + 3: 0, # 'е' + 23: 0, # 'ж' + 15: 0, # 'з' + 2: 0, # 'и' + 26: 0, # 'й' + 12: 0, # 'к' + 10: 0, # 'л' + 14: 0, # 'м' + 6: 0, # 'н' + 4: 0, # 'о' + 13: 0, # 'п' + 7: 0, # 'р' + 8: 0, # 'с' + 5: 0, # 'т' + 19: 0, # 'у' + 29: 0, # 'ф' + 25: 0, # 'х' + 22: 0, # 'ц' + 21: 0, # 'ч' + 27: 0, # 'ш' + 24: 0, # 'щ' + 17: 0, # 'ъ' + 52: 0, # 'ь' + 42: 0, # 'ю' + 16: 0, # 'я' + 58: 0, # 'є' + 62: 0, # '№' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +ISO_8859_5_BULGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 194, # '\x80' + 129: 195, # '\x81' + 130: 196, # '\x82' + 131: 197, # '\x83' + 132: 198, # '\x84' + 133: 199, # '\x85' + 134: 200, # '\x86' + 135: 201, # '\x87' + 136: 202, # '\x88' + 137: 203, # '\x89' + 138: 204, # '\x8a' + 139: 205, # '\x8b' + 140: 206, # '\x8c' + 141: 207, # '\x8d' + 142: 208, # '\x8e' + 143: 209, # '\x8f' + 144: 210, # '\x90' + 145: 211, # '\x91' + 146: 212, # '\x92' + 147: 213, # '\x93' + 148: 214, # '\x94' + 149: 215, # '\x95' + 150: 216, # '\x96' + 151: 217, # '\x97' + 152: 218, # '\x98' + 153: 219, # '\x99' + 154: 220, # '\x9a' + 155: 221, # '\x9b' + 156: 222, # '\x9c' + 157: 223, # '\x9d' + 158: 224, # '\x9e' + 159: 225, # '\x9f' + 160: 81, # '\xa0' + 161: 226, # 'Ё' + 162: 227, # 'Ђ' + 163: 228, # 'Ѓ' + 164: 229, # 'Є' + 165: 230, # 'Ѕ' + 166: 105, # 'І' + 167: 231, # 'Ї' + 168: 232, # 'Ј' + 169: 233, # 'Љ' + 170: 234, # 'Њ' + 171: 235, # 'Ћ' + 172: 236, # 'Ќ' + 173: 45, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 31, # 'А' + 177: 32, # 'Б' + 178: 35, # 'В' + 179: 43, # 'Г' + 180: 37, # 'Д' + 181: 44, # 'Е' + 182: 55, # 'Ж' + 183: 47, # 'З' + 184: 40, # 'И' + 185: 59, # 'Й' + 186: 33, # 'К' + 187: 46, # 'Л' + 188: 38, # 'М' + 189: 36, # 'Н' + 190: 41, # 'О' + 191: 30, # 'П' + 192: 39, # 'Р' + 193: 28, # 'С' + 194: 34, # 'Т' + 195: 51, # 'У' + 196: 48, # 'Ф' + 197: 49, # 'Х' + 198: 53, # 'Ц' + 199: 50, # 'Ч' + 200: 54, # 'Ш' + 201: 57, # 'Щ' + 202: 61, # 'Ъ' + 203: 239, # 'Ы' + 204: 67, # 'Ь' + 205: 240, # 'Э' + 206: 60, # 'Ю' + 207: 56, # 'Я' + 208: 1, # 'а' + 209: 18, # 'б' + 210: 9, # 'в' + 211: 20, # 'г' + 212: 11, # 'д' + 213: 3, # 'е' + 214: 23, # 'ж' + 215: 15, # 'з' + 216: 2, # 'и' + 217: 26, # 'й' + 218: 12, # 'к' + 219: 10, # 'л' + 220: 14, # 'м' + 221: 6, # 'н' + 222: 4, # 'о' + 223: 13, # 'п' + 224: 7, # 'р' + 225: 8, # 'с' + 226: 5, # 'т' + 227: 19, # 'у' + 228: 29, # 'ф' + 229: 25, # 'х' + 230: 22, # 'ц' + 231: 21, # 'ч' + 232: 27, # 'ш' + 233: 24, # 'щ' + 234: 17, # 'ъ' + 235: 75, # 'ы' + 236: 52, # 'ь' + 237: 241, # 'э' + 238: 42, # 'ю' + 239: 16, # 'я' + 240: 62, # '№' + 241: 242, # 'ё' + 242: 243, # 'ђ' + 243: 244, # 'ѓ' + 244: 58, # 'є' + 245: 245, # 'ѕ' + 246: 98, # 'і' + 247: 246, # 'ї' + 248: 247, # 'ј' + 249: 248, # 'љ' + 250: 249, # 'њ' + 251: 250, # 'ћ' + 252: 251, # 'ќ' + 253: 91, # '§' + 254: 252, # 'ў' + 255: 253, # 'џ' +} + +ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-5", + language="Bulgarian", + char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя", +) + +WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 77, # 'A' + 66: 90, # 'B' + 67: 99, # 'C' + 68: 100, # 'D' + 69: 72, # 'E' + 70: 109, # 'F' + 71: 107, # 'G' + 72: 101, # 'H' + 73: 79, # 'I' + 74: 185, # 'J' + 75: 81, # 'K' + 76: 102, # 'L' + 77: 76, # 'M' + 78: 94, # 'N' + 79: 82, # 'O' + 80: 110, # 'P' + 81: 186, # 'Q' + 82: 108, # 'R' + 83: 91, # 'S' + 84: 74, # 'T' + 85: 119, # 'U' + 86: 84, # 'V' + 87: 96, # 'W' + 88: 111, # 'X' + 89: 187, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 65, # 'a' + 98: 69, # 'b' + 99: 70, # 'c' + 100: 66, # 'd' + 101: 63, # 'e' + 102: 68, # 'f' + 103: 112, # 'g' + 104: 103, # 'h' + 105: 92, # 'i' + 106: 194, # 'j' + 107: 104, # 'k' + 108: 95, # 'l' + 109: 86, # 'm' + 110: 87, # 'n' + 111: 71, # 'o' + 112: 116, # 'p' + 113: 195, # 'q' + 114: 85, # 'r' + 115: 93, # 's' + 116: 97, # 't' + 117: 113, # 'u' + 118: 196, # 'v' + 119: 197, # 'w' + 120: 198, # 'x' + 121: 199, # 'y' + 122: 200, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 206, # 'Ђ' + 129: 207, # 'Ѓ' + 130: 208, # '‚' + 131: 209, # 'ѓ' + 132: 210, # '„' + 133: 211, # '…' + 134: 212, # '†' + 135: 213, # '‡' + 136: 120, # '€' + 137: 214, # '‰' + 138: 215, # 'Љ' + 139: 216, # '‹' + 140: 217, # 'Њ' + 141: 218, # 'Ќ' + 142: 219, # 'Ћ' + 143: 220, # 'Џ' + 144: 221, # 'ђ' + 145: 78, # '‘' + 146: 64, # '’' + 147: 83, # '“' + 148: 121, # '”' + 149: 98, # '•' + 150: 117, # '–' + 151: 105, # '—' + 152: 222, # None + 153: 223, # '™' + 154: 224, # 'љ' + 155: 225, # '›' + 156: 226, # 'њ' + 157: 227, # 'ќ' + 158: 228, # 'ћ' + 159: 229, # 'џ' + 160: 88, # '\xa0' + 161: 230, # 'Ў' + 162: 231, # 'ў' + 163: 232, # 'Ј' + 164: 233, # '¤' + 165: 122, # 'Ґ' + 166: 89, # '¦' + 167: 106, # '§' + 168: 234, # 'Ё' + 169: 235, # '©' + 170: 236, # 'Є' + 171: 237, # '«' + 172: 238, # '¬' + 173: 45, # '\xad' + 174: 239, # '®' + 175: 240, # 'Ї' + 176: 73, # '°' + 177: 80, # '±' + 178: 118, # 'І' + 179: 114, # 'і' + 180: 241, # 'ґ' + 181: 242, # 'µ' + 182: 243, # '¶' + 183: 244, # '·' + 184: 245, # 'ё' + 185: 62, # '№' + 186: 58, # 'є' + 187: 246, # '»' + 188: 247, # 'ј' + 189: 248, # 'Ѕ' + 190: 249, # 'ѕ' + 191: 250, # 'ї' + 192: 31, # 'А' + 193: 32, # 'Б' + 194: 35, # 'В' + 195: 43, # 'Г' + 196: 37, # 'Д' + 197: 44, # 'Е' + 198: 55, # 'Ж' + 199: 47, # 'З' + 200: 40, # 'И' + 201: 59, # 'Й' + 202: 33, # 'К' + 203: 46, # 'Л' + 204: 38, # 'М' + 205: 36, # 'Н' + 206: 41, # 'О' + 207: 30, # 'П' + 208: 39, # 'Р' + 209: 28, # 'С' + 210: 34, # 'Т' + 211: 51, # 'У' + 212: 48, # 'Ф' + 213: 49, # 'Х' + 214: 53, # 'Ц' + 215: 50, # 'Ч' + 216: 54, # 'Ш' + 217: 57, # 'Щ' + 218: 61, # 'Ъ' + 219: 251, # 'Ы' + 220: 67, # 'Ь' + 221: 252, # 'Э' + 222: 60, # 'Ю' + 223: 56, # 'Я' + 224: 1, # 'а' + 225: 18, # 'б' + 226: 9, # 'в' + 227: 20, # 'г' + 228: 11, # 'д' + 229: 3, # 'е' + 230: 23, # 'ж' + 231: 15, # 'з' + 232: 2, # 'и' + 233: 26, # 'й' + 234: 12, # 'к' + 235: 10, # 'л' + 236: 14, # 'м' + 237: 6, # 'н' + 238: 4, # 'о' + 239: 13, # 'п' + 240: 7, # 'р' + 241: 8, # 'с' + 242: 5, # 'т' + 243: 19, # 'у' + 244: 29, # 'ф' + 245: 25, # 'х' + 246: 22, # 'ц' + 247: 21, # 'ч' + 248: 27, # 'ш' + 249: 24, # 'щ' + 250: 17, # 'ъ' + 251: 75, # 'ы' + 252: 52, # 'ь' + 253: 253, # 'э' + 254: 42, # 'ю' + 255: 16, # 'я' +} + +WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel( + charset_name="windows-1251", + language="Bulgarian", + char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER, + language_model=BULGARIAN_LANG_MODEL, + typical_positive_ratio=0.969392, + keep_ascii_letters=False, + alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langgreekmodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langgreekmodel.py new file mode 100644 index 0000000..cfb8639 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langgreekmodel.py @@ -0,0 +1,4397 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +GREEK_LANG_MODEL = { + 60: { # 'e' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 55: { # 'o' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 2, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 58: { # 't' + 60: 2, # 'e' + 55: 1, # 'o' + 58: 1, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 36: { # '·' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 61: { # 'Ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 1, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 1, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 46: { # 'Έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 1, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 54: { # 'Ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 31: { # 'Α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 2, # 'Β' + 43: 2, # 'Γ' + 41: 1, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 0, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 51: { # 'Β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 1, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 43: { # 'Γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 1, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 41: { # 'Δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 34: { # 'Ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 1, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 1, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 0, # 'ώ' + }, + 40: { # 'Η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 52: { # 'Θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 1, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 47: { # 'Ι' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 1, # 'Β' + 43: 1, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 2, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 1, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 44: { # 'Κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 1, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 53: { # 'Λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 0, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 1, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 38: { # 'Μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 2, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 2, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 49: { # 'Ν' + 60: 2, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 1, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 1, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 59: { # 'Ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 39: { # 'Ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 1, # 'Β' + 43: 2, # 'Γ' + 41: 2, # 'Δ' + 34: 2, # 'Ε' + 40: 1, # 'Η' + 52: 2, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 2, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 2, # 'Φ' + 50: 2, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 35: { # 'Π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 2, # 'Λ' + 38: 1, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 1, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 48: { # 'Ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 1, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 37: { # 'Σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 1, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 2, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 2, # 'Υ' + 56: 0, # 'Φ' + 50: 2, # 'Χ' + 57: 2, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 2, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 2, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 33: { # 'Τ' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 2, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 2, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 1, # 'Τ' + 45: 1, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 2, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 2, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 45: { # 'Υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 2, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 2, # 'Η' + 52: 2, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 2, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 2, # 'Π' + 48: 1, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 56: { # 'Φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 1, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 2, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 1, # 'ύ' + 27: 1, # 'ώ' + }, + 50: { # 'Χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 1, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 2, # 'Ε' + 40: 2, # 'Η' + 52: 0, # 'Θ' + 47: 2, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 1, # 'Ν' + 59: 0, # 'Ξ' + 39: 1, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 1, # 'Χ' + 57: 1, # 'Ω' + 17: 2, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 2, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 57: { # 'Ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 1, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 1, # 'Λ' + 38: 0, # 'Μ' + 49: 2, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 2, # 'Ρ' + 37: 2, # 'Σ' + 33: 2, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 2, # 'ρ' + 14: 2, # 'ς' + 7: 2, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 17: { # 'ά' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 18: { # 'έ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 3, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 22: { # 'ή' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 1, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 15: { # 'ί' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 3, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 1: { # 'α' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 3, # 'ζ' + 13: 1, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 0, # 'ώ' + }, + 29: { # 'β' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 2, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 20: { # 'γ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 21: { # 'δ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 3: { # 'ε' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 32: { # 'ζ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 2, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 13: { # 'η' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 25: { # 'θ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 5: { # 'ι' + 60: 0, # 'e' + 55: 1, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 0, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 0, # 'ύ' + 27: 3, # 'ώ' + }, + 11: { # 'κ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 16: { # 'λ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 1, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 3, # 'λ' + 10: 2, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 10: { # 'μ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 1, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 2, # 'υ' + 28: 3, # 'φ' + 23: 0, # 'χ' + 42: 2, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 6: { # 'ν' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 1, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 30: { # 'ξ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 2, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 2, # 'ό' + 26: 3, # 'ύ' + 27: 1, # 'ώ' + }, + 4: { # 'ο' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 2, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 1, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 9: { # 'π' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 3, # 'λ' + 10: 0, # 'μ' + 6: 2, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 2, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 3, # 'ώ' + }, + 8: { # 'ρ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 1, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 2, # 'π' + 8: 2, # 'ρ' + 14: 0, # 'ς' + 7: 2, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 14: { # 'ς' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 2, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 0, # 'θ' + 5: 0, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 0, # 'τ' + 12: 0, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 7: { # 'σ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 3, # 'β' + 20: 0, # 'γ' + 21: 2, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 3, # 'θ' + 5: 3, # 'ι' + 11: 3, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 3, # 'φ' + 23: 3, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 2, # 'ώ' + }, + 2: { # 'τ' + 60: 0, # 'e' + 55: 2, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 2, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 3, # 'ι' + 11: 2, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 2, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 12: { # 'υ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 2, # 'ί' + 1: 3, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 2, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 3, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 2, # 'ό' + 26: 0, # 'ύ' + 27: 2, # 'ώ' + }, + 28: { # 'φ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 3, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 0, # 'μ' + 6: 1, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 1, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 2, # 'ύ' + 27: 2, # 'ώ' + }, + 23: { # 'χ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 3, # 'ά' + 18: 2, # 'έ' + 22: 3, # 'ή' + 15: 3, # 'ί' + 1: 3, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 3, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 2, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 3, # 'ο' + 9: 0, # 'π' + 8: 3, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 3, # 'τ' + 12: 3, # 'υ' + 28: 0, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 3, # 'ω' + 19: 3, # 'ό' + 26: 3, # 'ύ' + 27: 3, # 'ώ' + }, + 42: { # 'ψ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 2, # 'ά' + 18: 2, # 'έ' + 22: 1, # 'ή' + 15: 2, # 'ί' + 1: 2, # 'α' + 29: 0, # 'β' + 20: 0, # 'γ' + 21: 0, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 3, # 'η' + 25: 0, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 0, # 'λ' + 10: 0, # 'μ' + 6: 0, # 'ν' + 30: 0, # 'ξ' + 4: 2, # 'ο' + 9: 0, # 'π' + 8: 0, # 'ρ' + 14: 0, # 'ς' + 7: 0, # 'σ' + 2: 2, # 'τ' + 12: 1, # 'υ' + 28: 0, # 'φ' + 23: 0, # 'χ' + 42: 0, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 24: { # 'ω' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 1, # 'ά' + 18: 0, # 'έ' + 22: 2, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 2, # 'β' + 20: 3, # 'γ' + 21: 2, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 0, # 'η' + 25: 3, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 0, # 'ξ' + 4: 0, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 19: { # 'ό' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 3, # 'β' + 20: 3, # 'γ' + 21: 3, # 'δ' + 3: 1, # 'ε' + 32: 2, # 'ζ' + 13: 2, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 2, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 3, # 'χ' + 42: 2, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 26: { # 'ύ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 2, # 'α' + 29: 2, # 'β' + 20: 2, # 'γ' + 21: 1, # 'δ' + 3: 3, # 'ε' + 32: 0, # 'ζ' + 13: 2, # 'η' + 25: 3, # 'θ' + 5: 0, # 'ι' + 11: 3, # 'κ' + 16: 3, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 2, # 'ξ' + 4: 3, # 'ο' + 9: 3, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 2, # 'φ' + 23: 2, # 'χ' + 42: 2, # 'ψ' + 24: 2, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, + 27: { # 'ώ' + 60: 0, # 'e' + 55: 0, # 'o' + 58: 0, # 't' + 36: 0, # '·' + 61: 0, # 'Ά' + 46: 0, # 'Έ' + 54: 0, # 'Ό' + 31: 0, # 'Α' + 51: 0, # 'Β' + 43: 0, # 'Γ' + 41: 0, # 'Δ' + 34: 0, # 'Ε' + 40: 0, # 'Η' + 52: 0, # 'Θ' + 47: 0, # 'Ι' + 44: 0, # 'Κ' + 53: 0, # 'Λ' + 38: 0, # 'Μ' + 49: 0, # 'Ν' + 59: 0, # 'Ξ' + 39: 0, # 'Ο' + 35: 0, # 'Π' + 48: 0, # 'Ρ' + 37: 0, # 'Σ' + 33: 0, # 'Τ' + 45: 0, # 'Υ' + 56: 0, # 'Φ' + 50: 0, # 'Χ' + 57: 0, # 'Ω' + 17: 0, # 'ά' + 18: 0, # 'έ' + 22: 0, # 'ή' + 15: 0, # 'ί' + 1: 0, # 'α' + 29: 1, # 'β' + 20: 0, # 'γ' + 21: 3, # 'δ' + 3: 0, # 'ε' + 32: 0, # 'ζ' + 13: 1, # 'η' + 25: 2, # 'θ' + 5: 2, # 'ι' + 11: 0, # 'κ' + 16: 2, # 'λ' + 10: 3, # 'μ' + 6: 3, # 'ν' + 30: 1, # 'ξ' + 4: 0, # 'ο' + 9: 2, # 'π' + 8: 3, # 'ρ' + 14: 3, # 'ς' + 7: 3, # 'σ' + 2: 3, # 'τ' + 12: 0, # 'υ' + 28: 1, # 'φ' + 23: 1, # 'χ' + 42: 0, # 'ψ' + 24: 0, # 'ω' + 19: 0, # 'ό' + 26: 0, # 'ύ' + 27: 0, # 'ώ' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +WINDOWS_1253_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '€' + 129: 255, # None + 130: 255, # '‚' + 131: 255, # 'ƒ' + 132: 255, # '„' + 133: 255, # '…' + 134: 255, # '†' + 135: 255, # '‡' + 136: 255, # None + 137: 255, # '‰' + 138: 255, # None + 139: 255, # '‹' + 140: 255, # None + 141: 255, # None + 142: 255, # None + 143: 255, # None + 144: 255, # None + 145: 255, # '‘' + 146: 255, # '’' + 147: 255, # '“' + 148: 255, # '”' + 149: 255, # '•' + 150: 255, # '–' + 151: 255, # '—' + 152: 255, # None + 153: 255, # '™' + 154: 255, # None + 155: 255, # '›' + 156: 255, # None + 157: 255, # None + 158: 255, # None + 159: 255, # None + 160: 253, # '\xa0' + 161: 233, # '΅' + 162: 61, # 'Ά' + 163: 253, # '£' + 164: 253, # '¤' + 165: 253, # '¥' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # None + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # '®' + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 253, # 'µ' + 182: 253, # '¶' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None +} + +WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel( + charset_name="windows-1253", + language="Greek", + char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ", +) + +ISO_8859_7_GREEK_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 82, # 'A' + 66: 100, # 'B' + 67: 104, # 'C' + 68: 94, # 'D' + 69: 98, # 'E' + 70: 101, # 'F' + 71: 116, # 'G' + 72: 102, # 'H' + 73: 111, # 'I' + 74: 187, # 'J' + 75: 117, # 'K' + 76: 92, # 'L' + 77: 88, # 'M' + 78: 113, # 'N' + 79: 85, # 'O' + 80: 79, # 'P' + 81: 118, # 'Q' + 82: 105, # 'R' + 83: 83, # 'S' + 84: 67, # 'T' + 85: 114, # 'U' + 86: 119, # 'V' + 87: 95, # 'W' + 88: 99, # 'X' + 89: 109, # 'Y' + 90: 188, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 72, # 'a' + 98: 70, # 'b' + 99: 80, # 'c' + 100: 81, # 'd' + 101: 60, # 'e' + 102: 96, # 'f' + 103: 93, # 'g' + 104: 89, # 'h' + 105: 68, # 'i' + 106: 120, # 'j' + 107: 97, # 'k' + 108: 77, # 'l' + 109: 86, # 'm' + 110: 69, # 'n' + 111: 55, # 'o' + 112: 78, # 'p' + 113: 115, # 'q' + 114: 65, # 'r' + 115: 66, # 's' + 116: 58, # 't' + 117: 76, # 'u' + 118: 106, # 'v' + 119: 103, # 'w' + 120: 87, # 'x' + 121: 107, # 'y' + 122: 112, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 255, # '\x80' + 129: 255, # '\x81' + 130: 255, # '\x82' + 131: 255, # '\x83' + 132: 255, # '\x84' + 133: 255, # '\x85' + 134: 255, # '\x86' + 135: 255, # '\x87' + 136: 255, # '\x88' + 137: 255, # '\x89' + 138: 255, # '\x8a' + 139: 255, # '\x8b' + 140: 255, # '\x8c' + 141: 255, # '\x8d' + 142: 255, # '\x8e' + 143: 255, # '\x8f' + 144: 255, # '\x90' + 145: 255, # '\x91' + 146: 255, # '\x92' + 147: 255, # '\x93' + 148: 255, # '\x94' + 149: 255, # '\x95' + 150: 255, # '\x96' + 151: 255, # '\x97' + 152: 255, # '\x98' + 153: 255, # '\x99' + 154: 255, # '\x9a' + 155: 255, # '\x9b' + 156: 255, # '\x9c' + 157: 255, # '\x9d' + 158: 255, # '\x9e' + 159: 255, # '\x9f' + 160: 253, # '\xa0' + 161: 233, # '‘' + 162: 90, # '’' + 163: 253, # '£' + 164: 253, # '€' + 165: 253, # '₯' + 166: 253, # '¦' + 167: 253, # '§' + 168: 253, # '¨' + 169: 253, # '©' + 170: 253, # 'ͺ' + 171: 253, # '«' + 172: 253, # '¬' + 173: 74, # '\xad' + 174: 253, # None + 175: 253, # '―' + 176: 253, # '°' + 177: 253, # '±' + 178: 253, # '²' + 179: 253, # '³' + 180: 247, # '΄' + 181: 248, # '΅' + 182: 61, # 'Ά' + 183: 36, # '·' + 184: 46, # 'Έ' + 185: 71, # 'Ή' + 186: 73, # 'Ί' + 187: 253, # '»' + 188: 54, # 'Ό' + 189: 253, # '½' + 190: 108, # 'Ύ' + 191: 123, # 'Ώ' + 192: 110, # 'ΐ' + 193: 31, # 'Α' + 194: 51, # 'Β' + 195: 43, # 'Γ' + 196: 41, # 'Δ' + 197: 34, # 'Ε' + 198: 91, # 'Ζ' + 199: 40, # 'Η' + 200: 52, # 'Θ' + 201: 47, # 'Ι' + 202: 44, # 'Κ' + 203: 53, # 'Λ' + 204: 38, # 'Μ' + 205: 49, # 'Ν' + 206: 59, # 'Ξ' + 207: 39, # 'Ο' + 208: 35, # 'Π' + 209: 48, # 'Ρ' + 210: 250, # None + 211: 37, # 'Σ' + 212: 33, # 'Τ' + 213: 45, # 'Υ' + 214: 56, # 'Φ' + 215: 50, # 'Χ' + 216: 84, # 'Ψ' + 217: 57, # 'Ω' + 218: 120, # 'Ϊ' + 219: 121, # 'Ϋ' + 220: 17, # 'ά' + 221: 18, # 'έ' + 222: 22, # 'ή' + 223: 15, # 'ί' + 224: 124, # 'ΰ' + 225: 1, # 'α' + 226: 29, # 'β' + 227: 20, # 'γ' + 228: 21, # 'δ' + 229: 3, # 'ε' + 230: 32, # 'ζ' + 231: 13, # 'η' + 232: 25, # 'θ' + 233: 5, # 'ι' + 234: 11, # 'κ' + 235: 16, # 'λ' + 236: 10, # 'μ' + 237: 6, # 'ν' + 238: 30, # 'ξ' + 239: 4, # 'ο' + 240: 9, # 'π' + 241: 8, # 'ρ' + 242: 14, # 'ς' + 243: 7, # 'σ' + 244: 2, # 'τ' + 245: 12, # 'υ' + 246: 28, # 'φ' + 247: 23, # 'χ' + 248: 42, # 'ψ' + 249: 24, # 'ω' + 250: 64, # 'ϊ' + 251: 75, # 'ϋ' + 252: 19, # 'ό' + 253: 26, # 'ύ' + 254: 27, # 'ώ' + 255: 253, # None +} + +ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-7", + language="Greek", + char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, + language_model=GREEK_LANG_MODEL, + typical_positive_ratio=0.982851, + keep_ascii_letters=False, + alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhebrewmodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhebrewmodel.py new file mode 100644 index 0000000..56d2975 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhebrewmodel.py @@ -0,0 +1,4380 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +HEBREW_LANG_MODEL = { + 50: { # 'a' + 50: 0, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 2, # 'l' + 54: 2, # 'n' + 49: 0, # 'o' + 51: 2, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 0, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 60: { # 'c' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 61: { # 'd' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 0, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 42: { # 'e' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 2, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 2, # 'l' + 54: 2, # 'n' + 49: 1, # 'o' + 51: 2, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 1, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 53: { # 'i' + 50: 1, # 'a' + 60: 2, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 0, # 'i' + 56: 1, # 'l' + 54: 2, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 56: { # 'l' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 2, # 'e' + 53: 2, # 'i' + 56: 2, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 54: { # 'n' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 49: { # 'o' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 2, # 'n' + 49: 1, # 'o' + 51: 2, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 51: { # 'r' + 50: 2, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 2, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 2, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 43: { # 's' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 2, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 2, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 44: { # 't' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 0, # 'd' + 42: 2, # 'e' + 53: 2, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 2, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 63: { # 'u' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 34: { # '\xa0' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 1, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 2, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 55: { # '´' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 1, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 2, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 1, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 48: { # '¼' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 39: { # '½' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 57: { # '¾' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 30: { # 'ְ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 2, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 0, # 'ף' + 18: 2, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 59: { # 'ֱ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 1, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 41: { # 'ֲ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 0, # 'ם' + 6: 2, # 'מ' + 23: 0, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 33: { # 'ִ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 2, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 37: { # 'ֵ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 1, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 36: { # 'ֶ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 1, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 2, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 31: { # 'ַ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 1, # 'ֶ' + 31: 0, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 29: { # 'ָ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 2, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 35: { # 'ֹ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 2, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 2, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 62: { # 'ֻ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 1, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 28: { # 'ּ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 3, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 3, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 3, # 'ַ' + 29: 3, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 2, # 'ׁ' + 45: 1, # 'ׂ' + 9: 2, # 'א' + 8: 2, # 'ב' + 20: 1, # 'ג' + 16: 2, # 'ד' + 3: 1, # 'ה' + 2: 2, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 2, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 2, # 'ל' + 11: 1, # 'ם' + 6: 2, # 'מ' + 23: 1, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 2, # 'ר' + 10: 2, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 38: { # 'ׁ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 2, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 45: { # 'ׂ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 2, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 9: { # 'א' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 2, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 8: { # 'ב' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 1, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 20: { # 'ג' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 2, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 0, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 16: { # 'ד' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 3: { # 'ה' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 3, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 0, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 2: { # 'ו' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 3, # 'ֹ' + 62: 0, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 24: { # 'ז' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 1, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 2, # 'ב' + 20: 2, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 2, # 'ח' + 22: 1, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 2, # 'נ' + 19: 1, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 1, # 'ש' + 5: 2, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 14: { # 'ח' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 2, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 1, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 22: { # 'ט' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 1, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 2, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 1: { # 'י' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 25: { # 'ך' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 2, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 1, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 15: { # 'כ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 3, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 4: { # 'ל' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 3, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 11: { # 'ם' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 1, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 6: { # 'מ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 0, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 23: { # 'ן' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 1, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 1, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 12: { # 'נ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 19: { # 'ס' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 2, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 3, # 'ף' + 18: 3, # 'פ' + 27: 0, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 1, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 13: { # 'ע' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 1, # 'ֱ' + 41: 2, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 1, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 2, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 2, # 'ע' + 26: 1, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 26: { # 'ף' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 1, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 18: { # 'פ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 1, # 'ֵ' + 36: 2, # 'ֶ' + 31: 1, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 2, # 'ב' + 20: 3, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 2, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 2, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 27: { # 'ץ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 21: { # 'צ' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 1, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 1, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 0, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 17: { # 'ק' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 1, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 1, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 2, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 1, # 'ך' + 15: 1, # 'כ' + 4: 3, # 'ל' + 11: 2, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 2, # 'ץ' + 21: 3, # 'צ' + 17: 2, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 7: { # 'ר' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 2, # '´' + 48: 1, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 1, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 2, # 'ֹ' + 62: 1, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 3, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 3, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 3, # 'ץ' + 21: 3, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 10: { # 'ש' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 1, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 1, # 'ִ' + 37: 1, # 'ֵ' + 36: 1, # 'ֶ' + 31: 1, # 'ַ' + 29: 1, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 3, # 'ׁ' + 45: 2, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 3, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 3, # 'ט' + 1: 3, # 'י' + 25: 3, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 2, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 1, # '…' + }, + 5: { # 'ת' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 1, # '\xa0' + 55: 0, # '´' + 48: 1, # '¼' + 39: 1, # '½' + 57: 0, # '¾' + 30: 2, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 2, # 'ִ' + 37: 2, # 'ֵ' + 36: 2, # 'ֶ' + 31: 2, # 'ַ' + 29: 2, # 'ָ' + 35: 1, # 'ֹ' + 62: 1, # 'ֻ' + 28: 2, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 3, # 'א' + 8: 3, # 'ב' + 20: 3, # 'ג' + 16: 2, # 'ד' + 3: 3, # 'ה' + 2: 3, # 'ו' + 24: 2, # 'ז' + 14: 3, # 'ח' + 22: 2, # 'ט' + 1: 3, # 'י' + 25: 2, # 'ך' + 15: 3, # 'כ' + 4: 3, # 'ל' + 11: 3, # 'ם' + 6: 3, # 'מ' + 23: 3, # 'ן' + 12: 3, # 'נ' + 19: 2, # 'ס' + 13: 3, # 'ע' + 26: 2, # 'ף' + 18: 3, # 'פ' + 27: 1, # 'ץ' + 21: 2, # 'צ' + 17: 3, # 'ק' + 7: 3, # 'ר' + 10: 3, # 'ש' + 5: 3, # 'ת' + 32: 1, # '–' + 52: 1, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, + 32: { # '–' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 1, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 52: { # '’' + 50: 1, # 'a' + 60: 0, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 2, # 's' + 44: 2, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 1, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 47: { # '“' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 1, # 'l' + 54: 1, # 'n' + 49: 1, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 1, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 2, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 1, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 1, # 'ח' + 22: 1, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 1, # 'ס' + 13: 1, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 1, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 46: { # '”' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 1, # 'ב' + 20: 1, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 1, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 0, # '†' + 40: 0, # '…' + }, + 58: { # '†' + 50: 0, # 'a' + 60: 0, # 'c' + 61: 0, # 'd' + 42: 0, # 'e' + 53: 0, # 'i' + 56: 0, # 'l' + 54: 0, # 'n' + 49: 0, # 'o' + 51: 0, # 'r' + 43: 0, # 's' + 44: 0, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 0, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 0, # 'ה' + 2: 0, # 'ו' + 24: 0, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 0, # 'י' + 25: 0, # 'ך' + 15: 0, # 'כ' + 4: 0, # 'ל' + 11: 0, # 'ם' + 6: 0, # 'מ' + 23: 0, # 'ן' + 12: 0, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 0, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 0, # 'ר' + 10: 0, # 'ש' + 5: 0, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 0, # '”' + 58: 2, # '†' + 40: 0, # '…' + }, + 40: { # '…' + 50: 1, # 'a' + 60: 1, # 'c' + 61: 1, # 'd' + 42: 1, # 'e' + 53: 1, # 'i' + 56: 0, # 'l' + 54: 1, # 'n' + 49: 0, # 'o' + 51: 1, # 'r' + 43: 1, # 's' + 44: 1, # 't' + 63: 0, # 'u' + 34: 0, # '\xa0' + 55: 0, # '´' + 48: 0, # '¼' + 39: 0, # '½' + 57: 0, # '¾' + 30: 0, # 'ְ' + 59: 0, # 'ֱ' + 41: 0, # 'ֲ' + 33: 0, # 'ִ' + 37: 0, # 'ֵ' + 36: 0, # 'ֶ' + 31: 0, # 'ַ' + 29: 0, # 'ָ' + 35: 0, # 'ֹ' + 62: 0, # 'ֻ' + 28: 0, # 'ּ' + 38: 0, # 'ׁ' + 45: 0, # 'ׂ' + 9: 1, # 'א' + 8: 0, # 'ב' + 20: 0, # 'ג' + 16: 0, # 'ד' + 3: 1, # 'ה' + 2: 1, # 'ו' + 24: 1, # 'ז' + 14: 0, # 'ח' + 22: 0, # 'ט' + 1: 1, # 'י' + 25: 0, # 'ך' + 15: 1, # 'כ' + 4: 1, # 'ל' + 11: 0, # 'ם' + 6: 1, # 'מ' + 23: 0, # 'ן' + 12: 1, # 'נ' + 19: 0, # 'ס' + 13: 0, # 'ע' + 26: 0, # 'ף' + 18: 1, # 'פ' + 27: 0, # 'ץ' + 21: 0, # 'צ' + 17: 0, # 'ק' + 7: 1, # 'ר' + 10: 1, # 'ש' + 5: 1, # 'ת' + 32: 0, # '–' + 52: 0, # '’' + 47: 0, # '“' + 46: 1, # '”' + 58: 0, # '†' + 40: 2, # '…' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +WINDOWS_1255_HEBREW_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 69, # 'A' + 66: 91, # 'B' + 67: 79, # 'C' + 68: 80, # 'D' + 69: 92, # 'E' + 70: 89, # 'F' + 71: 97, # 'G' + 72: 90, # 'H' + 73: 68, # 'I' + 74: 111, # 'J' + 75: 112, # 'K' + 76: 82, # 'L' + 77: 73, # 'M' + 78: 95, # 'N' + 79: 85, # 'O' + 80: 78, # 'P' + 81: 121, # 'Q' + 82: 86, # 'R' + 83: 71, # 'S' + 84: 67, # 'T' + 85: 102, # 'U' + 86: 107, # 'V' + 87: 84, # 'W' + 88: 114, # 'X' + 89: 103, # 'Y' + 90: 115, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 50, # 'a' + 98: 74, # 'b' + 99: 60, # 'c' + 100: 61, # 'd' + 101: 42, # 'e' + 102: 76, # 'f' + 103: 70, # 'g' + 104: 64, # 'h' + 105: 53, # 'i' + 106: 105, # 'j' + 107: 93, # 'k' + 108: 56, # 'l' + 109: 65, # 'm' + 110: 54, # 'n' + 111: 49, # 'o' + 112: 66, # 'p' + 113: 110, # 'q' + 114: 51, # 'r' + 115: 43, # 's' + 116: 44, # 't' + 117: 63, # 'u' + 118: 81, # 'v' + 119: 77, # 'w' + 120: 98, # 'x' + 121: 75, # 'y' + 122: 108, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 124, # '€' + 129: 202, # None + 130: 203, # '‚' + 131: 204, # 'ƒ' + 132: 205, # '„' + 133: 40, # '…' + 134: 58, # '†' + 135: 206, # '‡' + 136: 207, # 'ˆ' + 137: 208, # '‰' + 138: 209, # None + 139: 210, # '‹' + 140: 211, # None + 141: 212, # None + 142: 213, # None + 143: 214, # None + 144: 215, # None + 145: 83, # '‘' + 146: 52, # '’' + 147: 47, # '“' + 148: 46, # '”' + 149: 72, # '•' + 150: 32, # '–' + 151: 94, # '—' + 152: 216, # '˜' + 153: 113, # '™' + 154: 217, # None + 155: 109, # '›' + 156: 218, # None + 157: 219, # None + 158: 220, # None + 159: 221, # None + 160: 34, # '\xa0' + 161: 116, # '¡' + 162: 222, # '¢' + 163: 118, # '£' + 164: 100, # '₪' + 165: 223, # '¥' + 166: 224, # '¦' + 167: 117, # '§' + 168: 119, # '¨' + 169: 104, # '©' + 170: 125, # '×' + 171: 225, # '«' + 172: 226, # '¬' + 173: 87, # '\xad' + 174: 99, # '®' + 175: 227, # '¯' + 176: 106, # '°' + 177: 122, # '±' + 178: 123, # '²' + 179: 228, # '³' + 180: 55, # '´' + 181: 229, # 'µ' + 182: 230, # '¶' + 183: 101, # '·' + 184: 231, # '¸' + 185: 232, # '¹' + 186: 120, # '÷' + 187: 233, # '»' + 188: 48, # '¼' + 189: 39, # '½' + 190: 57, # '¾' + 191: 234, # '¿' + 192: 30, # 'ְ' + 193: 59, # 'ֱ' + 194: 41, # 'ֲ' + 195: 88, # 'ֳ' + 196: 33, # 'ִ' + 197: 37, # 'ֵ' + 198: 36, # 'ֶ' + 199: 31, # 'ַ' + 200: 29, # 'ָ' + 201: 35, # 'ֹ' + 202: 235, # None + 203: 62, # 'ֻ' + 204: 28, # 'ּ' + 205: 236, # 'ֽ' + 206: 126, # '־' + 207: 237, # 'ֿ' + 208: 238, # '׀' + 209: 38, # 'ׁ' + 210: 45, # 'ׂ' + 211: 239, # '׃' + 212: 240, # 'װ' + 213: 241, # 'ױ' + 214: 242, # 'ײ' + 215: 243, # '׳' + 216: 127, # '״' + 217: 244, # None + 218: 245, # None + 219: 246, # None + 220: 247, # None + 221: 248, # None + 222: 249, # None + 223: 250, # None + 224: 9, # 'א' + 225: 8, # 'ב' + 226: 20, # 'ג' + 227: 16, # 'ד' + 228: 3, # 'ה' + 229: 2, # 'ו' + 230: 24, # 'ז' + 231: 14, # 'ח' + 232: 22, # 'ט' + 233: 1, # 'י' + 234: 25, # 'ך' + 235: 15, # 'כ' + 236: 4, # 'ל' + 237: 11, # 'ם' + 238: 6, # 'מ' + 239: 23, # 'ן' + 240: 12, # 'נ' + 241: 19, # 'ס' + 242: 13, # 'ע' + 243: 26, # 'ף' + 244: 18, # 'פ' + 245: 27, # 'ץ' + 246: 21, # 'צ' + 247: 17, # 'ק' + 248: 7, # 'ר' + 249: 10, # 'ש' + 250: 5, # 'ת' + 251: 251, # None + 252: 252, # None + 253: 128, # '\u200e' + 254: 96, # '\u200f' + 255: 253, # None +} + +WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel( + charset_name="windows-1255", + language="Hebrew", + char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER, + language_model=HEBREW_LANG_MODEL, + typical_positive_ratio=0.984004, + keep_ascii_letters=False, + alphabet="אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhungarianmodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhungarianmodel.py new file mode 100644 index 0000000..09a0d32 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langhungarianmodel.py @@ -0,0 +1,4649 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +HUNGARIAN_LANG_MODEL = { + 28: { # 'A' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 2, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 2, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 2, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 1, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 40: { # 'B' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 3, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 54: { # 'C' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 3, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 45: { # 'D' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 32: { # 'E' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 2, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 50: { # 'F' + 28: 1, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 0, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 49: { # 'G' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 2, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 38: { # 'H' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 0, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 1, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 2, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 39: { # 'I' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 2, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 53: { # 'J' + 28: 2, # 'A' + 40: 0, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 0, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 36: { # 'K' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 2, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 41: { # 'L' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 1, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 34: { # 'M' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 3, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 1, # 'ű' + }, + 35: { # 'N' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 2, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 2, # 'Y' + 52: 1, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 47: { # 'O' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 2, # 'K' + 41: 2, # 'L' + 34: 2, # 'M' + 35: 2, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 1, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 1, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 46: { # 'P' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 3, # 'á' + 15: 2, # 'é' + 30: 0, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 0, # 'ű' + }, + 43: { # 'R' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 2, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 2, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 33: { # 'S' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 3, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 1, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 37: { # 'T' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 1, # 'S' + 37: 2, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 2, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 1, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 2, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 57: { # 'U' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 48: { # 'V' + 28: 2, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 2, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 2, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 2, # 'o' + 23: 0, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 2, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 0, # 'Ú' + 63: 1, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 55: { # 'Y' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 1, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 2, # 'Z' + 2: 1, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 0, # 'r' + 5: 0, # 's' + 3: 0, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 1, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 52: { # 'Z' + 28: 2, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 2, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 2, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 2, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 1, # 'U' + 48: 1, # 'V' + 55: 1, # 'Y' + 52: 1, # 'Z' + 2: 1, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 0, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 2, # 'Á' + 44: 1, # 'É' + 61: 1, # 'Í' + 58: 1, # 'Ó' + 59: 1, # 'Ö' + 60: 1, # 'Ú' + 63: 1, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 2: { # 'a' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 18: { # 'b' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 26: { # 'c' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 1, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 2, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 2, # 'á' + 15: 2, # 'é' + 30: 2, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 17: { # 'd' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 2, # 'k' + 6: 1, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 1: { # 'e' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 2, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 27: { # 'f' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 3, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 2, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 3, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 12: { # 'g' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 20: { # 'h' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 3, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 1, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 1, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 9: { # 'i' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 2, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 3, # 'ó' + 24: 1, # 'ö' + 31: 2, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 1, # 'ű' + }, + 22: { # 'j' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 1, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 1, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 7: { # 'k' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 2, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 1, # 'ú' + 29: 3, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 6: { # 'l' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 1, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 3, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 3, # 'ő' + 56: 1, # 'ű' + }, + 13: { # 'm' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 1, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 3, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 3, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 2, # 'ű' + }, + 4: { # 'n' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 1, # 'x' + 16: 3, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 8: { # 'o' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 1, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 23: { # 'p' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 3, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 10: { # 'r' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 2, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 2, # 'ű' + }, + 5: { # 's' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 2, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 3: { # 't' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 1, # 'g' + 20: 3, # 'h' + 9: 3, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 3, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 3, # 'ú' + 29: 3, # 'ü' + 42: 3, # 'ő' + 56: 2, # 'ű' + }, + 21: { # 'u' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 2, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 2, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 1, # 'u' + 19: 3, # 'v' + 62: 1, # 'x' + 16: 1, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 2, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 1, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 19: { # 'v' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 2, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 2, # 'ö' + 31: 1, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 1, # 'ű' + }, + 62: { # 'x' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 0, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 1, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 1, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 1, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 16: { # 'y' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 3, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 2, # 'j' + 7: 2, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 2, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 2, # 'í' + 25: 2, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 2, # 'ü' + 42: 1, # 'ő' + 56: 2, # 'ű' + }, + 11: { # 'z' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 3, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 3, # 'd' + 1: 3, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 3, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 3, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 3, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 3, # 'á' + 15: 3, # 'é' + 30: 3, # 'í' + 25: 3, # 'ó' + 24: 3, # 'ö' + 31: 2, # 'ú' + 29: 3, # 'ü' + 42: 2, # 'ő' + 56: 1, # 'ű' + }, + 51: { # 'Á' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 1, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 44: { # 'É' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 1, # 'E' + 50: 0, # 'F' + 49: 2, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 2, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 2, # 'R' + 33: 2, # 'S' + 37: 2, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 3, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 61: { # 'Í' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 0, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 1, # 'm' + 4: 0, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 0, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 58: { # 'Ó' + 28: 1, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 1, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 2, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 2, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 0, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 1, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 59: { # 'Ö' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 1, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 1, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 0, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 2, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 60: { # 'Ú' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 1, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 1, # 'F' + 49: 1, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 0, # 'b' + 26: 0, # 'c' + 17: 0, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 2, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 2, # 'j' + 7: 0, # 'k' + 6: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 0, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 0, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 63: { # 'Ü' + 28: 0, # 'A' + 40: 1, # 'B' + 54: 0, # 'C' + 45: 1, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 1, # 'G' + 38: 1, # 'H' + 39: 0, # 'I' + 53: 1, # 'J' + 36: 1, # 'K' + 41: 1, # 'L' + 34: 1, # 'M' + 35: 1, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 1, # 'R' + 33: 1, # 'S' + 37: 1, # 'T' + 57: 0, # 'U' + 48: 1, # 'V' + 55: 0, # 'Y' + 52: 1, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 0, # 'f' + 12: 1, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 0, # 'j' + 7: 0, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 1, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 14: { # 'á' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 3, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 3, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 2, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 1, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 2, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 15: { # 'é' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 3, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 3, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 30: { # 'í' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 0, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 0, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 2, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 2, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 25: { # 'ó' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 2, # 'a' + 18: 3, # 'b' + 26: 2, # 'c' + 17: 3, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 2, # 'g' + 20: 2, # 'h' + 9: 2, # 'i' + 22: 2, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 8: 1, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 1, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 1, # 'ö' + 31: 1, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 24: { # 'ö' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 0, # 'a' + 18: 3, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 0, # 'e' + 27: 1, # 'f' + 12: 2, # 'g' + 20: 1, # 'h' + 9: 0, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 2, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 3, # 't' + 21: 0, # 'u' + 19: 3, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 3, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 31: { # 'ú' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 2, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 2, # 'f' + 12: 3, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 3, # 'j' + 7: 1, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 3, # 'r' + 5: 3, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 1, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 29: { # 'ü' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 3, # 'g' + 20: 2, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 3, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 8: 0, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 0, # 'u' + 19: 2, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 1, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 42: { # 'ő' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 2, # 'b' + 26: 1, # 'c' + 17: 2, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 2, # 'k' + 6: 3, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 8: 1, # 'o' + 23: 1, # 'p' + 10: 2, # 'r' + 5: 2, # 's' + 3: 2, # 't' + 21: 1, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 1, # 'é' + 30: 1, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 1, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, + 56: { # 'ű' + 28: 0, # 'A' + 40: 0, # 'B' + 54: 0, # 'C' + 45: 0, # 'D' + 32: 0, # 'E' + 50: 0, # 'F' + 49: 0, # 'G' + 38: 0, # 'H' + 39: 0, # 'I' + 53: 0, # 'J' + 36: 0, # 'K' + 41: 0, # 'L' + 34: 0, # 'M' + 35: 0, # 'N' + 47: 0, # 'O' + 46: 0, # 'P' + 43: 0, # 'R' + 33: 0, # 'S' + 37: 0, # 'T' + 57: 0, # 'U' + 48: 0, # 'V' + 55: 0, # 'Y' + 52: 0, # 'Z' + 2: 1, # 'a' + 18: 1, # 'b' + 26: 0, # 'c' + 17: 1, # 'd' + 1: 1, # 'e' + 27: 1, # 'f' + 12: 1, # 'g' + 20: 1, # 'h' + 9: 1, # 'i' + 22: 1, # 'j' + 7: 1, # 'k' + 6: 1, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 8: 0, # 'o' + 23: 0, # 'p' + 10: 1, # 'r' + 5: 1, # 's' + 3: 1, # 't' + 21: 0, # 'u' + 19: 1, # 'v' + 62: 0, # 'x' + 16: 0, # 'y' + 11: 2, # 'z' + 51: 0, # 'Á' + 44: 0, # 'É' + 61: 0, # 'Í' + 58: 0, # 'Ó' + 59: 0, # 'Ö' + 60: 0, # 'Ú' + 63: 0, # 'Ü' + 14: 0, # 'á' + 15: 0, # 'é' + 30: 0, # 'í' + 25: 0, # 'ó' + 24: 0, # 'ö' + 31: 0, # 'ú' + 29: 0, # 'ü' + 42: 0, # 'ő' + 56: 0, # 'ű' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 72, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 161, # '€' + 129: 162, # None + 130: 163, # '‚' + 131: 164, # None + 132: 165, # '„' + 133: 166, # '…' + 134: 167, # '†' + 135: 168, # '‡' + 136: 169, # None + 137: 170, # '‰' + 138: 171, # 'Š' + 139: 172, # '‹' + 140: 173, # 'Ś' + 141: 174, # 'Ť' + 142: 175, # 'Ž' + 143: 176, # 'Ź' + 144: 177, # None + 145: 178, # '‘' + 146: 179, # '’' + 147: 180, # '“' + 148: 78, # '”' + 149: 181, # '•' + 150: 69, # '–' + 151: 182, # '—' + 152: 183, # None + 153: 184, # '™' + 154: 185, # 'š' + 155: 186, # '›' + 156: 187, # 'ś' + 157: 188, # 'ť' + 158: 189, # 'ž' + 159: 190, # 'ź' + 160: 191, # '\xa0' + 161: 192, # 'ˇ' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ą' + 166: 197, # '¦' + 167: 76, # '§' + 168: 198, # '¨' + 169: 199, # '©' + 170: 200, # 'Ş' + 171: 201, # '«' + 172: 202, # '¬' + 173: 203, # '\xad' + 174: 204, # '®' + 175: 205, # 'Ż' + 176: 81, # '°' + 177: 206, # '±' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'µ' + 182: 211, # '¶' + 183: 212, # '·' + 184: 213, # '¸' + 185: 214, # 'ą' + 186: 215, # 'ş' + 187: 216, # '»' + 188: 217, # 'Ľ' + 189: 218, # '˝' + 190: 219, # 'ľ' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 83, # 'Â' + 195: 222, # 'Ă' + 196: 80, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 70, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 84, # 'ŕ' + 225: 14, # 'á' + 226: 75, # 'â' + 227: 242, # 'ă' + 228: 71, # 'ä' + 229: 82, # 'ĺ' + 230: 243, # 'ć' + 231: 73, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 85, # 'ę' + 235: 79, # 'ë' + 236: 86, # 'ě' + 237: 30, # 'í' + 238: 77, # 'î' + 239: 87, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 74, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' +} + +WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel( + charset_name="windows-1250", + language="Hungarian", + char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet="ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű", +) + +ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 28, # 'A' + 66: 40, # 'B' + 67: 54, # 'C' + 68: 45, # 'D' + 69: 32, # 'E' + 70: 50, # 'F' + 71: 49, # 'G' + 72: 38, # 'H' + 73: 39, # 'I' + 74: 53, # 'J' + 75: 36, # 'K' + 76: 41, # 'L' + 77: 34, # 'M' + 78: 35, # 'N' + 79: 47, # 'O' + 80: 46, # 'P' + 81: 71, # 'Q' + 82: 43, # 'R' + 83: 33, # 'S' + 84: 37, # 'T' + 85: 57, # 'U' + 86: 48, # 'V' + 87: 64, # 'W' + 88: 68, # 'X' + 89: 55, # 'Y' + 90: 52, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 2, # 'a' + 98: 18, # 'b' + 99: 26, # 'c' + 100: 17, # 'd' + 101: 1, # 'e' + 102: 27, # 'f' + 103: 12, # 'g' + 104: 20, # 'h' + 105: 9, # 'i' + 106: 22, # 'j' + 107: 7, # 'k' + 108: 6, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 8, # 'o' + 112: 23, # 'p' + 113: 67, # 'q' + 114: 10, # 'r' + 115: 5, # 's' + 116: 3, # 't' + 117: 21, # 'u' + 118: 19, # 'v' + 119: 65, # 'w' + 120: 62, # 'x' + 121: 16, # 'y' + 122: 11, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 159, # '\x80' + 129: 160, # '\x81' + 130: 161, # '\x82' + 131: 162, # '\x83' + 132: 163, # '\x84' + 133: 164, # '\x85' + 134: 165, # '\x86' + 135: 166, # '\x87' + 136: 167, # '\x88' + 137: 168, # '\x89' + 138: 169, # '\x8a' + 139: 170, # '\x8b' + 140: 171, # '\x8c' + 141: 172, # '\x8d' + 142: 173, # '\x8e' + 143: 174, # '\x8f' + 144: 175, # '\x90' + 145: 176, # '\x91' + 146: 177, # '\x92' + 147: 178, # '\x93' + 148: 179, # '\x94' + 149: 180, # '\x95' + 150: 181, # '\x96' + 151: 182, # '\x97' + 152: 183, # '\x98' + 153: 184, # '\x99' + 154: 185, # '\x9a' + 155: 186, # '\x9b' + 156: 187, # '\x9c' + 157: 188, # '\x9d' + 158: 189, # '\x9e' + 159: 190, # '\x9f' + 160: 191, # '\xa0' + 161: 192, # 'Ą' + 162: 193, # '˘' + 163: 194, # 'Ł' + 164: 195, # '¤' + 165: 196, # 'Ľ' + 166: 197, # 'Ś' + 167: 75, # '§' + 168: 198, # '¨' + 169: 199, # 'Š' + 170: 200, # 'Ş' + 171: 201, # 'Ť' + 172: 202, # 'Ź' + 173: 203, # '\xad' + 174: 204, # 'Ž' + 175: 205, # 'Ż' + 176: 79, # '°' + 177: 206, # 'ą' + 178: 207, # '˛' + 179: 208, # 'ł' + 180: 209, # '´' + 181: 210, # 'ľ' + 182: 211, # 'ś' + 183: 212, # 'ˇ' + 184: 213, # '¸' + 185: 214, # 'š' + 186: 215, # 'ş' + 187: 216, # 'ť' + 188: 217, # 'ź' + 189: 218, # '˝' + 190: 219, # 'ž' + 191: 220, # 'ż' + 192: 221, # 'Ŕ' + 193: 51, # 'Á' + 194: 81, # 'Â' + 195: 222, # 'Ă' + 196: 78, # 'Ä' + 197: 223, # 'Ĺ' + 198: 224, # 'Ć' + 199: 225, # 'Ç' + 200: 226, # 'Č' + 201: 44, # 'É' + 202: 227, # 'Ę' + 203: 228, # 'Ë' + 204: 229, # 'Ě' + 205: 61, # 'Í' + 206: 230, # 'Î' + 207: 231, # 'Ď' + 208: 232, # 'Đ' + 209: 233, # 'Ń' + 210: 234, # 'Ň' + 211: 58, # 'Ó' + 212: 235, # 'Ô' + 213: 66, # 'Ő' + 214: 59, # 'Ö' + 215: 236, # '×' + 216: 237, # 'Ř' + 217: 238, # 'Ů' + 218: 60, # 'Ú' + 219: 69, # 'Ű' + 220: 63, # 'Ü' + 221: 239, # 'Ý' + 222: 240, # 'Ţ' + 223: 241, # 'ß' + 224: 82, # 'ŕ' + 225: 14, # 'á' + 226: 74, # 'â' + 227: 242, # 'ă' + 228: 70, # 'ä' + 229: 80, # 'ĺ' + 230: 243, # 'ć' + 231: 72, # 'ç' + 232: 244, # 'č' + 233: 15, # 'é' + 234: 83, # 'ę' + 235: 77, # 'ë' + 236: 84, # 'ě' + 237: 30, # 'í' + 238: 76, # 'î' + 239: 85, # 'ď' + 240: 245, # 'đ' + 241: 246, # 'ń' + 242: 247, # 'ň' + 243: 25, # 'ó' + 244: 73, # 'ô' + 245: 42, # 'ő' + 246: 24, # 'ö' + 247: 248, # '÷' + 248: 249, # 'ř' + 249: 250, # 'ů' + 250: 31, # 'ú' + 251: 56, # 'ű' + 252: 29, # 'ü' + 253: 251, # 'ý' + 254: 252, # 'ţ' + 255: 253, # '˙' +} + +ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-2", + language="Hungarian", + char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER, + language_model=HUNGARIAN_LANG_MODEL, + typical_positive_ratio=0.947368, + keep_ascii_letters=True, + alphabet="ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langrussianmodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langrussianmodel.py new file mode 100644 index 0000000..39a5388 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langrussianmodel.py @@ -0,0 +1,5725 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +RUSSIAN_LANG_MODEL = { + 37: { # 'А' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 44: { # 'Б' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 33: { # 'В' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 0, # 'ю' + 16: 1, # 'я' + }, + 46: { # 'Г' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 41: { # 'Д' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 3, # 'ж' + 20: 1, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 48: { # 'Е' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 2, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 1, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 56: { # 'Ж' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 1, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 2, # 'ю' + 16: 0, # 'я' + }, + 51: { # 'З' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 1, # 'я' + }, + 42: { # 'И' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 2, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 60: { # 'Й' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 36: { # 'К' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 49: { # 'Л' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 1, # 'я' + }, + 38: { # 'М' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 1, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 31: { # 'Н' + 37: 2, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 2, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 34: { # 'О' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 2, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 1, # 'З' + 42: 1, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 2, # 'Л' + 38: 1, # 'М' + 31: 2, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 1, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 35: { # 'П' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 2, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 1, # 'с' + 6: 1, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 2, # 'я' + }, + 45: { # 'Р' + 37: 2, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 2, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 2, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 32: { # 'С' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 2, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 2, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 40: { # 'Т' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 2, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 1, # 'Ь' + 47: 1, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 52: { # 'У' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 1, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 1, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 1, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 1, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 53: { # 'Ф' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 1, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 55: { # 'Х' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 2, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 0, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 58: { # 'Ц' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 1, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 50: { # 'Ч' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 1, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 1, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 57: { # 'Ш' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 1, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 63: { # 'Щ' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 1, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 62: { # 'Ы' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 1, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 61: { # 'Ь' + 37: 0, # 'А' + 44: 1, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 1, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 1, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 47: { # 'Э' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 1, # 'Й' + 36: 1, # 'К' + 49: 1, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 1, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 59: { # 'Ю' + 37: 1, # 'А' + 44: 1, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 1, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 0, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 0, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 43: { # 'Я' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 1, # 'В' + 46: 1, # 'Г' + 41: 0, # 'Д' + 48: 1, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 1, # 'С' + 40: 1, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 1, # 'Х' + 58: 0, # 'Ц' + 50: 1, # 'Ч' + 57: 0, # 'Ш' + 63: 1, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 1, # 'Ю' + 43: 1, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 0, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 1, # 'й' + 11: 1, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 1, # 'п' + 9: 1, # 'р' + 7: 1, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 3: { # 'а' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 1, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 21: { # 'б' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 1, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 10: { # 'в' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 19: { # 'г' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 13: { # 'д' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 3, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 2: { # 'е' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 24: { # 'ж' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 1, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 20: { # 'з' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 4: { # 'и' + 37: 1, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 23: { # 'й' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 1, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 11: { # 'к' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 3, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 1, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 8: { # 'л' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 3, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 1, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 12: { # 'м' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 5: { # 'н' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 3, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 2, # 'щ' + 54: 1, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 1: { # 'о' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 3, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 15: { # 'п' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 3, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 0, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 1, # 'ш' + 29: 1, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 2, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 3, # 'я' + }, + 9: { # 'р' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 7: { # 'с' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 1, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 2, # 'ш' + 29: 1, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 6: { # 'т' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 2, # 'щ' + 54: 2, # 'ъ' + 18: 3, # 'ы' + 17: 3, # 'ь' + 30: 2, # 'э' + 27: 2, # 'ю' + 16: 3, # 'я' + }, + 14: { # 'у' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 3, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 2, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 2, # 'э' + 27: 3, # 'ю' + 16: 2, # 'я' + }, + 39: { # 'ф' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 0, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 2, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 2, # 'ы' + 17: 1, # 'ь' + 30: 2, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 26: { # 'х' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 3, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 1, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 1, # 'п' + 9: 3, # 'р' + 7: 2, # 'с' + 6: 2, # 'т' + 14: 2, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 1, # 'ъ' + 18: 0, # 'ы' + 17: 1, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 28: { # 'ц' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 1, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 2, # 'к' + 8: 1, # 'л' + 12: 1, # 'м' + 5: 1, # 'н' + 1: 3, # 'о' + 15: 0, # 'п' + 9: 1, # 'р' + 7: 0, # 'с' + 6: 1, # 'т' + 14: 3, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 1, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 3, # 'ы' + 17: 1, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 22: { # 'ч' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 2, # 'л' + 12: 1, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 3, # 'т' + 14: 3, # 'у' + 39: 1, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 1, # 'ч' + 25: 2, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 3, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 25: { # 'ш' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 1, # 'б' + 10: 2, # 'в' + 19: 1, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 2, # 'м' + 5: 3, # 'н' + 1: 3, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 1, # 'с' + 6: 2, # 'т' + 14: 3, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 1, # 'ц' + 22: 1, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 3, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 0, # 'я' + }, + 29: { # 'щ' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 3, # 'а' + 21: 0, # 'б' + 10: 1, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 3, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 3, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 1, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 0, # 'п' + 9: 2, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 2, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 2, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 0, # 'я' + }, + 54: { # 'ъ' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 0, # 'б' + 10: 0, # 'в' + 19: 0, # 'г' + 13: 0, # 'д' + 2: 2, # 'е' + 24: 0, # 'ж' + 20: 0, # 'з' + 4: 0, # 'и' + 23: 0, # 'й' + 11: 0, # 'к' + 8: 0, # 'л' + 12: 0, # 'м' + 5: 0, # 'н' + 1: 0, # 'о' + 15: 0, # 'п' + 9: 0, # 'р' + 7: 0, # 'с' + 6: 0, # 'т' + 14: 0, # 'у' + 39: 0, # 'ф' + 26: 0, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 0, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 1, # 'ю' + 16: 2, # 'я' + }, + 18: { # 'ы' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 3, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 2, # 'и' + 23: 3, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 1, # 'о' + 15: 3, # 'п' + 9: 3, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 0, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 3, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 0, # 'ю' + 16: 2, # 'я' + }, + 17: { # 'ь' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 2, # 'б' + 10: 2, # 'в' + 19: 2, # 'г' + 13: 2, # 'д' + 2: 3, # 'е' + 24: 1, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 0, # 'й' + 11: 3, # 'к' + 8: 0, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 2, # 'о' + 15: 2, # 'п' + 9: 1, # 'р' + 7: 3, # 'с' + 6: 2, # 'т' + 14: 0, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 3, # 'ш' + 29: 2, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 3, # 'ю' + 16: 3, # 'я' + }, + 30: { # 'э' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 1, # 'М' + 31: 1, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 1, # 'Р' + 32: 1, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 1, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 1, # 'б' + 10: 1, # 'в' + 19: 1, # 'г' + 13: 2, # 'д' + 2: 1, # 'е' + 24: 0, # 'ж' + 20: 1, # 'з' + 4: 0, # 'и' + 23: 2, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 2, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 2, # 'ф' + 26: 1, # 'х' + 28: 0, # 'ц' + 22: 0, # 'ч' + 25: 1, # 'ш' + 29: 0, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 1, # 'ю' + 16: 1, # 'я' + }, + 27: { # 'ю' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 2, # 'а' + 21: 3, # 'б' + 10: 1, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 1, # 'е' + 24: 2, # 'ж' + 20: 2, # 'з' + 4: 1, # 'и' + 23: 1, # 'й' + 11: 2, # 'к' + 8: 2, # 'л' + 12: 2, # 'м' + 5: 2, # 'н' + 1: 1, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 0, # 'у' + 39: 1, # 'ф' + 26: 2, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 1, # 'э' + 27: 2, # 'ю' + 16: 1, # 'я' + }, + 16: { # 'я' + 37: 0, # 'А' + 44: 0, # 'Б' + 33: 0, # 'В' + 46: 0, # 'Г' + 41: 0, # 'Д' + 48: 0, # 'Е' + 56: 0, # 'Ж' + 51: 0, # 'З' + 42: 0, # 'И' + 60: 0, # 'Й' + 36: 0, # 'К' + 49: 0, # 'Л' + 38: 0, # 'М' + 31: 0, # 'Н' + 34: 0, # 'О' + 35: 0, # 'П' + 45: 0, # 'Р' + 32: 0, # 'С' + 40: 0, # 'Т' + 52: 0, # 'У' + 53: 0, # 'Ф' + 55: 0, # 'Х' + 58: 0, # 'Ц' + 50: 0, # 'Ч' + 57: 0, # 'Ш' + 63: 0, # 'Щ' + 62: 0, # 'Ы' + 61: 0, # 'Ь' + 47: 0, # 'Э' + 59: 0, # 'Ю' + 43: 0, # 'Я' + 3: 0, # 'а' + 21: 2, # 'б' + 10: 3, # 'в' + 19: 2, # 'г' + 13: 3, # 'д' + 2: 3, # 'е' + 24: 3, # 'ж' + 20: 3, # 'з' + 4: 2, # 'и' + 23: 2, # 'й' + 11: 3, # 'к' + 8: 3, # 'л' + 12: 3, # 'м' + 5: 3, # 'н' + 1: 0, # 'о' + 15: 2, # 'п' + 9: 2, # 'р' + 7: 3, # 'с' + 6: 3, # 'т' + 14: 1, # 'у' + 39: 1, # 'ф' + 26: 3, # 'х' + 28: 2, # 'ц' + 22: 2, # 'ч' + 25: 2, # 'ш' + 29: 3, # 'щ' + 54: 0, # 'ъ' + 18: 0, # 'ы' + 17: 0, # 'ь' + 30: 0, # 'э' + 27: 2, # 'ю' + 16: 2, # 'я' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +IBM866_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 3, # 'а' + 161: 21, # 'б' + 162: 10, # 'в' + 163: 19, # 'г' + 164: 13, # 'д' + 165: 2, # 'е' + 166: 24, # 'ж' + 167: 20, # 'з' + 168: 4, # 'и' + 169: 23, # 'й' + 170: 11, # 'к' + 171: 8, # 'л' + 172: 12, # 'м' + 173: 5, # 'н' + 174: 1, # 'о' + 175: 15, # 'п' + 176: 191, # '░' + 177: 192, # '▒' + 178: 193, # '▓' + 179: 194, # '│' + 180: 195, # '┤' + 181: 196, # '╡' + 182: 197, # '╢' + 183: 198, # '╖' + 184: 199, # '╕' + 185: 200, # '╣' + 186: 201, # '║' + 187: 202, # '╗' + 188: 203, # '╝' + 189: 204, # '╜' + 190: 205, # '╛' + 191: 206, # '┐' + 192: 207, # '└' + 193: 208, # '┴' + 194: 209, # '┬' + 195: 210, # '├' + 196: 211, # '─' + 197: 212, # '┼' + 198: 213, # '╞' + 199: 214, # '╟' + 200: 215, # '╚' + 201: 216, # '╔' + 202: 217, # '╩' + 203: 218, # '╦' + 204: 219, # '╠' + 205: 220, # '═' + 206: 221, # '╬' + 207: 222, # '╧' + 208: 223, # '╨' + 209: 224, # '╤' + 210: 225, # '╥' + 211: 226, # '╙' + 212: 227, # '╘' + 213: 228, # '╒' + 214: 229, # '╓' + 215: 230, # '╫' + 216: 231, # '╪' + 217: 232, # '┘' + 218: 233, # '┌' + 219: 234, # '█' + 220: 235, # '▄' + 221: 236, # '▌' + 222: 237, # '▐' + 223: 238, # '▀' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # 'Ё' + 241: 68, # 'ё' + 242: 240, # 'Є' + 243: 241, # 'є' + 244: 242, # 'Ї' + 245: 243, # 'ї' + 246: 244, # 'Ў' + 247: 245, # 'ў' + 248: 246, # '°' + 249: 247, # '∙' + 250: 248, # '·' + 251: 249, # '√' + 252: 250, # '№' + 253: 251, # '¤' + 254: 252, # '■' + 255: 255, # '\xa0' +} + +IBM866_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="IBM866", + language="Russian", + char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'Ђ' + 129: 192, # 'Ѓ' + 130: 193, # '‚' + 131: 194, # 'ѓ' + 132: 195, # '„' + 133: 196, # '…' + 134: 197, # '†' + 135: 198, # '‡' + 136: 199, # '€' + 137: 200, # '‰' + 138: 201, # 'Љ' + 139: 202, # '‹' + 140: 203, # 'Њ' + 141: 204, # 'Ќ' + 142: 205, # 'Ћ' + 143: 206, # 'Џ' + 144: 207, # 'ђ' + 145: 208, # '‘' + 146: 209, # '’' + 147: 210, # '“' + 148: 211, # '”' + 149: 212, # '•' + 150: 213, # '–' + 151: 214, # '—' + 152: 215, # None + 153: 216, # '™' + 154: 217, # 'љ' + 155: 218, # '›' + 156: 219, # 'њ' + 157: 220, # 'ќ' + 158: 221, # 'ћ' + 159: 222, # 'џ' + 160: 223, # '\xa0' + 161: 224, # 'Ў' + 162: 225, # 'ў' + 163: 226, # 'Ј' + 164: 227, # '¤' + 165: 228, # 'Ґ' + 166: 229, # '¦' + 167: 230, # '§' + 168: 231, # 'Ё' + 169: 232, # '©' + 170: 233, # 'Є' + 171: 234, # '«' + 172: 235, # '¬' + 173: 236, # '\xad' + 174: 237, # '®' + 175: 238, # 'Ї' + 176: 239, # '°' + 177: 240, # '±' + 178: 241, # 'І' + 179: 242, # 'і' + 180: 243, # 'ґ' + 181: 244, # 'µ' + 182: 245, # '¶' + 183: 246, # '·' + 184: 68, # 'ё' + 185: 247, # '№' + 186: 248, # 'є' + 187: 249, # '»' + 188: 250, # 'ј' + 189: 251, # 'Ѕ' + 190: 252, # 'ѕ' + 191: 253, # 'ї' + 192: 37, # 'А' + 193: 44, # 'Б' + 194: 33, # 'В' + 195: 46, # 'Г' + 196: 41, # 'Д' + 197: 48, # 'Е' + 198: 56, # 'Ж' + 199: 51, # 'З' + 200: 42, # 'И' + 201: 60, # 'Й' + 202: 36, # 'К' + 203: 49, # 'Л' + 204: 38, # 'М' + 205: 31, # 'Н' + 206: 34, # 'О' + 207: 35, # 'П' + 208: 45, # 'Р' + 209: 32, # 'С' + 210: 40, # 'Т' + 211: 52, # 'У' + 212: 53, # 'Ф' + 213: 55, # 'Х' + 214: 58, # 'Ц' + 215: 50, # 'Ч' + 216: 57, # 'Ш' + 217: 63, # 'Щ' + 218: 70, # 'Ъ' + 219: 62, # 'Ы' + 220: 61, # 'Ь' + 221: 47, # 'Э' + 222: 59, # 'Ю' + 223: 43, # 'Я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 16, # 'я' +} + +WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="windows-1251", + language="Russian", + char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +IBM855_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # 'ђ' + 129: 192, # 'Ђ' + 130: 193, # 'ѓ' + 131: 194, # 'Ѓ' + 132: 68, # 'ё' + 133: 195, # 'Ё' + 134: 196, # 'є' + 135: 197, # 'Є' + 136: 198, # 'ѕ' + 137: 199, # 'Ѕ' + 138: 200, # 'і' + 139: 201, # 'І' + 140: 202, # 'ї' + 141: 203, # 'Ї' + 142: 204, # 'ј' + 143: 205, # 'Ј' + 144: 206, # 'љ' + 145: 207, # 'Љ' + 146: 208, # 'њ' + 147: 209, # 'Њ' + 148: 210, # 'ћ' + 149: 211, # 'Ћ' + 150: 212, # 'ќ' + 151: 213, # 'Ќ' + 152: 214, # 'ў' + 153: 215, # 'Ў' + 154: 216, # 'џ' + 155: 217, # 'Џ' + 156: 27, # 'ю' + 157: 59, # 'Ю' + 158: 54, # 'ъ' + 159: 70, # 'Ъ' + 160: 3, # 'а' + 161: 37, # 'А' + 162: 21, # 'б' + 163: 44, # 'Б' + 164: 28, # 'ц' + 165: 58, # 'Ц' + 166: 13, # 'д' + 167: 41, # 'Д' + 168: 2, # 'е' + 169: 48, # 'Е' + 170: 39, # 'ф' + 171: 53, # 'Ф' + 172: 19, # 'г' + 173: 46, # 'Г' + 174: 218, # '«' + 175: 219, # '»' + 176: 220, # '░' + 177: 221, # '▒' + 178: 222, # '▓' + 179: 223, # '│' + 180: 224, # '┤' + 181: 26, # 'х' + 182: 55, # 'Х' + 183: 4, # 'и' + 184: 42, # 'И' + 185: 225, # '╣' + 186: 226, # '║' + 187: 227, # '╗' + 188: 228, # '╝' + 189: 23, # 'й' + 190: 60, # 'Й' + 191: 229, # '┐' + 192: 230, # '└' + 193: 231, # '┴' + 194: 232, # '┬' + 195: 233, # '├' + 196: 234, # '─' + 197: 235, # '┼' + 198: 11, # 'к' + 199: 36, # 'К' + 200: 236, # '╚' + 201: 237, # '╔' + 202: 238, # '╩' + 203: 239, # '╦' + 204: 240, # '╠' + 205: 241, # '═' + 206: 242, # '╬' + 207: 243, # '¤' + 208: 8, # 'л' + 209: 49, # 'Л' + 210: 12, # 'м' + 211: 38, # 'М' + 212: 5, # 'н' + 213: 31, # 'Н' + 214: 1, # 'о' + 215: 34, # 'О' + 216: 15, # 'п' + 217: 244, # '┘' + 218: 245, # '┌' + 219: 246, # '█' + 220: 247, # '▄' + 221: 35, # 'П' + 222: 16, # 'я' + 223: 248, # '▀' + 224: 43, # 'Я' + 225: 9, # 'р' + 226: 45, # 'Р' + 227: 7, # 'с' + 228: 32, # 'С' + 229: 6, # 'т' + 230: 40, # 'Т' + 231: 14, # 'у' + 232: 52, # 'У' + 233: 24, # 'ж' + 234: 56, # 'Ж' + 235: 10, # 'в' + 236: 33, # 'В' + 237: 17, # 'ь' + 238: 61, # 'Ь' + 239: 249, # '№' + 240: 250, # '\xad' + 241: 18, # 'ы' + 242: 62, # 'Ы' + 243: 20, # 'з' + 244: 51, # 'З' + 245: 25, # 'ш' + 246: 57, # 'Ш' + 247: 30, # 'э' + 248: 47, # 'Э' + 249: 29, # 'щ' + 250: 63, # 'Щ' + 251: 22, # 'ч' + 252: 50, # 'Ч' + 253: 251, # '§' + 254: 252, # '■' + 255: 255, # '\xa0' +} + +IBM855_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="IBM855", + language="Russian", + char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +KOI8_R_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '─' + 129: 192, # '│' + 130: 193, # '┌' + 131: 194, # '┐' + 132: 195, # '└' + 133: 196, # '┘' + 134: 197, # '├' + 135: 198, # '┤' + 136: 199, # '┬' + 137: 200, # '┴' + 138: 201, # '┼' + 139: 202, # '▀' + 140: 203, # '▄' + 141: 204, # '█' + 142: 205, # '▌' + 143: 206, # '▐' + 144: 207, # '░' + 145: 208, # '▒' + 146: 209, # '▓' + 147: 210, # '⌠' + 148: 211, # '■' + 149: 212, # '∙' + 150: 213, # '√' + 151: 214, # '≈' + 152: 215, # '≤' + 153: 216, # '≥' + 154: 217, # '\xa0' + 155: 218, # '⌡' + 156: 219, # '°' + 157: 220, # '²' + 158: 221, # '·' + 159: 222, # '÷' + 160: 223, # '═' + 161: 224, # '║' + 162: 225, # '╒' + 163: 68, # 'ё' + 164: 226, # '╓' + 165: 227, # '╔' + 166: 228, # '╕' + 167: 229, # '╖' + 168: 230, # '╗' + 169: 231, # '╘' + 170: 232, # '╙' + 171: 233, # '╚' + 172: 234, # '╛' + 173: 235, # '╜' + 174: 236, # '╝' + 175: 237, # '╞' + 176: 238, # '╟' + 177: 239, # '╠' + 178: 240, # '╡' + 179: 241, # 'Ё' + 180: 242, # '╢' + 181: 243, # '╣' + 182: 244, # '╤' + 183: 245, # '╥' + 184: 246, # '╦' + 185: 247, # '╧' + 186: 248, # '╨' + 187: 249, # '╩' + 188: 250, # '╪' + 189: 251, # '╫' + 190: 252, # '╬' + 191: 253, # '©' + 192: 27, # 'ю' + 193: 3, # 'а' + 194: 21, # 'б' + 195: 28, # 'ц' + 196: 13, # 'д' + 197: 2, # 'е' + 198: 39, # 'ф' + 199: 19, # 'г' + 200: 26, # 'х' + 201: 4, # 'и' + 202: 23, # 'й' + 203: 11, # 'к' + 204: 8, # 'л' + 205: 12, # 'м' + 206: 5, # 'н' + 207: 1, # 'о' + 208: 15, # 'п' + 209: 16, # 'я' + 210: 9, # 'р' + 211: 7, # 'с' + 212: 6, # 'т' + 213: 14, # 'у' + 214: 24, # 'ж' + 215: 10, # 'в' + 216: 17, # 'ь' + 217: 18, # 'ы' + 218: 20, # 'з' + 219: 25, # 'ш' + 220: 30, # 'э' + 221: 29, # 'щ' + 222: 22, # 'ч' + 223: 54, # 'ъ' + 224: 59, # 'Ю' + 225: 37, # 'А' + 226: 44, # 'Б' + 227: 58, # 'Ц' + 228: 41, # 'Д' + 229: 48, # 'Е' + 230: 53, # 'Ф' + 231: 46, # 'Г' + 232: 55, # 'Х' + 233: 42, # 'И' + 234: 60, # 'Й' + 235: 36, # 'К' + 236: 49, # 'Л' + 237: 38, # 'М' + 238: 31, # 'Н' + 239: 34, # 'О' + 240: 35, # 'П' + 241: 43, # 'Я' + 242: 45, # 'Р' + 243: 32, # 'С' + 244: 40, # 'Т' + 245: 52, # 'У' + 246: 56, # 'Ж' + 247: 33, # 'В' + 248: 61, # 'Ь' + 249: 62, # 'Ы' + 250: 51, # 'З' + 251: 57, # 'Ш' + 252: 47, # 'Э' + 253: 63, # 'Щ' + 254: 50, # 'Ч' + 255: 70, # 'Ъ' +} + +KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="KOI8-R", + language="Russian", + char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 37, # 'А' + 129: 44, # 'Б' + 130: 33, # 'В' + 131: 46, # 'Г' + 132: 41, # 'Д' + 133: 48, # 'Е' + 134: 56, # 'Ж' + 135: 51, # 'З' + 136: 42, # 'И' + 137: 60, # 'Й' + 138: 36, # 'К' + 139: 49, # 'Л' + 140: 38, # 'М' + 141: 31, # 'Н' + 142: 34, # 'О' + 143: 35, # 'П' + 144: 45, # 'Р' + 145: 32, # 'С' + 146: 40, # 'Т' + 147: 52, # 'У' + 148: 53, # 'Ф' + 149: 55, # 'Х' + 150: 58, # 'Ц' + 151: 50, # 'Ч' + 152: 57, # 'Ш' + 153: 63, # 'Щ' + 154: 70, # 'Ъ' + 155: 62, # 'Ы' + 156: 61, # 'Ь' + 157: 47, # 'Э' + 158: 59, # 'Ю' + 159: 43, # 'Я' + 160: 191, # '†' + 161: 192, # '°' + 162: 193, # 'Ґ' + 163: 194, # '£' + 164: 195, # '§' + 165: 196, # '•' + 166: 197, # '¶' + 167: 198, # 'І' + 168: 199, # '®' + 169: 200, # '©' + 170: 201, # '™' + 171: 202, # 'Ђ' + 172: 203, # 'ђ' + 173: 204, # '≠' + 174: 205, # 'Ѓ' + 175: 206, # 'ѓ' + 176: 207, # '∞' + 177: 208, # '±' + 178: 209, # '≤' + 179: 210, # '≥' + 180: 211, # 'і' + 181: 212, # 'µ' + 182: 213, # 'ґ' + 183: 214, # 'Ј' + 184: 215, # 'Є' + 185: 216, # 'є' + 186: 217, # 'Ї' + 187: 218, # 'ї' + 188: 219, # 'Љ' + 189: 220, # 'љ' + 190: 221, # 'Њ' + 191: 222, # 'њ' + 192: 223, # 'ј' + 193: 224, # 'Ѕ' + 194: 225, # '¬' + 195: 226, # '√' + 196: 227, # 'ƒ' + 197: 228, # '≈' + 198: 229, # '∆' + 199: 230, # '«' + 200: 231, # '»' + 201: 232, # '…' + 202: 233, # '\xa0' + 203: 234, # 'Ћ' + 204: 235, # 'ћ' + 205: 236, # 'Ќ' + 206: 237, # 'ќ' + 207: 238, # 'ѕ' + 208: 239, # '–' + 209: 240, # '—' + 210: 241, # '“' + 211: 242, # '”' + 212: 243, # '‘' + 213: 244, # '’' + 214: 245, # '÷' + 215: 246, # '„' + 216: 247, # 'Ў' + 217: 248, # 'ў' + 218: 249, # 'Џ' + 219: 250, # 'џ' + 220: 251, # '№' + 221: 252, # 'Ё' + 222: 68, # 'ё' + 223: 16, # 'я' + 224: 3, # 'а' + 225: 21, # 'б' + 226: 10, # 'в' + 227: 19, # 'г' + 228: 13, # 'д' + 229: 2, # 'е' + 230: 24, # 'ж' + 231: 20, # 'з' + 232: 4, # 'и' + 233: 23, # 'й' + 234: 11, # 'к' + 235: 8, # 'л' + 236: 12, # 'м' + 237: 5, # 'н' + 238: 1, # 'о' + 239: 15, # 'п' + 240: 9, # 'р' + 241: 7, # 'с' + 242: 6, # 'т' + 243: 14, # 'у' + 244: 39, # 'ф' + 245: 26, # 'х' + 246: 28, # 'ц' + 247: 22, # 'ч' + 248: 25, # 'ш' + 249: 29, # 'щ' + 250: 54, # 'ъ' + 251: 18, # 'ы' + 252: 17, # 'ь' + 253: 30, # 'э' + 254: 27, # 'ю' + 255: 255, # '€' +} + +MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="MacCyrillic", + language="Russian", + char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) + +ISO_8859_5_RUSSIAN_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 142, # 'A' + 66: 143, # 'B' + 67: 144, # 'C' + 68: 145, # 'D' + 69: 146, # 'E' + 70: 147, # 'F' + 71: 148, # 'G' + 72: 149, # 'H' + 73: 150, # 'I' + 74: 151, # 'J' + 75: 152, # 'K' + 76: 74, # 'L' + 77: 153, # 'M' + 78: 75, # 'N' + 79: 154, # 'O' + 80: 155, # 'P' + 81: 156, # 'Q' + 82: 157, # 'R' + 83: 158, # 'S' + 84: 159, # 'T' + 85: 160, # 'U' + 86: 161, # 'V' + 87: 162, # 'W' + 88: 163, # 'X' + 89: 164, # 'Y' + 90: 165, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 71, # 'a' + 98: 172, # 'b' + 99: 66, # 'c' + 100: 173, # 'd' + 101: 65, # 'e' + 102: 174, # 'f' + 103: 76, # 'g' + 104: 175, # 'h' + 105: 64, # 'i' + 106: 176, # 'j' + 107: 177, # 'k' + 108: 77, # 'l' + 109: 72, # 'm' + 110: 178, # 'n' + 111: 69, # 'o' + 112: 67, # 'p' + 113: 179, # 'q' + 114: 78, # 'r' + 115: 73, # 's' + 116: 180, # 't' + 117: 181, # 'u' + 118: 79, # 'v' + 119: 182, # 'w' + 120: 183, # 'x' + 121: 184, # 'y' + 122: 185, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 191, # '\x80' + 129: 192, # '\x81' + 130: 193, # '\x82' + 131: 194, # '\x83' + 132: 195, # '\x84' + 133: 196, # '\x85' + 134: 197, # '\x86' + 135: 198, # '\x87' + 136: 199, # '\x88' + 137: 200, # '\x89' + 138: 201, # '\x8a' + 139: 202, # '\x8b' + 140: 203, # '\x8c' + 141: 204, # '\x8d' + 142: 205, # '\x8e' + 143: 206, # '\x8f' + 144: 207, # '\x90' + 145: 208, # '\x91' + 146: 209, # '\x92' + 147: 210, # '\x93' + 148: 211, # '\x94' + 149: 212, # '\x95' + 150: 213, # '\x96' + 151: 214, # '\x97' + 152: 215, # '\x98' + 153: 216, # '\x99' + 154: 217, # '\x9a' + 155: 218, # '\x9b' + 156: 219, # '\x9c' + 157: 220, # '\x9d' + 158: 221, # '\x9e' + 159: 222, # '\x9f' + 160: 223, # '\xa0' + 161: 224, # 'Ё' + 162: 225, # 'Ђ' + 163: 226, # 'Ѓ' + 164: 227, # 'Є' + 165: 228, # 'Ѕ' + 166: 229, # 'І' + 167: 230, # 'Ї' + 168: 231, # 'Ј' + 169: 232, # 'Љ' + 170: 233, # 'Њ' + 171: 234, # 'Ћ' + 172: 235, # 'Ќ' + 173: 236, # '\xad' + 174: 237, # 'Ў' + 175: 238, # 'Џ' + 176: 37, # 'А' + 177: 44, # 'Б' + 178: 33, # 'В' + 179: 46, # 'Г' + 180: 41, # 'Д' + 181: 48, # 'Е' + 182: 56, # 'Ж' + 183: 51, # 'З' + 184: 42, # 'И' + 185: 60, # 'Й' + 186: 36, # 'К' + 187: 49, # 'Л' + 188: 38, # 'М' + 189: 31, # 'Н' + 190: 34, # 'О' + 191: 35, # 'П' + 192: 45, # 'Р' + 193: 32, # 'С' + 194: 40, # 'Т' + 195: 52, # 'У' + 196: 53, # 'Ф' + 197: 55, # 'Х' + 198: 58, # 'Ц' + 199: 50, # 'Ч' + 200: 57, # 'Ш' + 201: 63, # 'Щ' + 202: 70, # 'Ъ' + 203: 62, # 'Ы' + 204: 61, # 'Ь' + 205: 47, # 'Э' + 206: 59, # 'Ю' + 207: 43, # 'Я' + 208: 3, # 'а' + 209: 21, # 'б' + 210: 10, # 'в' + 211: 19, # 'г' + 212: 13, # 'д' + 213: 2, # 'е' + 214: 24, # 'ж' + 215: 20, # 'з' + 216: 4, # 'и' + 217: 23, # 'й' + 218: 11, # 'к' + 219: 8, # 'л' + 220: 12, # 'м' + 221: 5, # 'н' + 222: 1, # 'о' + 223: 15, # 'п' + 224: 9, # 'р' + 225: 7, # 'с' + 226: 6, # 'т' + 227: 14, # 'у' + 228: 39, # 'ф' + 229: 26, # 'х' + 230: 28, # 'ц' + 231: 22, # 'ч' + 232: 25, # 'ш' + 233: 29, # 'щ' + 234: 54, # 'ъ' + 235: 18, # 'ы' + 236: 17, # 'ь' + 237: 30, # 'э' + 238: 27, # 'ю' + 239: 16, # 'я' + 240: 239, # '№' + 241: 68, # 'ё' + 242: 240, # 'ђ' + 243: 241, # 'ѓ' + 244: 242, # 'є' + 245: 243, # 'ѕ' + 246: 244, # 'і' + 247: 245, # 'ї' + 248: 246, # 'ј' + 249: 247, # 'љ' + 250: 248, # 'њ' + 251: 249, # 'ћ' + 252: 250, # 'ќ' + 253: 251, # '§' + 254: 252, # 'ў' + 255: 255, # 'џ' +} + +ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-5", + language="Russian", + char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER, + language_model=RUSSIAN_LANG_MODEL, + typical_positive_ratio=0.976601, + keep_ascii_letters=False, + alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langthaimodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langthaimodel.py new file mode 100644 index 0000000..489cad9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langthaimodel.py @@ -0,0 +1,4380 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +THAI_LANG_MODEL = { + 5: { # 'ก' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 3, # 'ฎ' + 57: 2, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 1, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 30: { # 'ข' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 2, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 24: { # 'ค' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 2, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 3, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 8: { # 'ง' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 1, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 2, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 3, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 26: { # 'จ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 52: { # 'ฉ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 34: { # 'ช' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 1, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 51: { # 'ซ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 47: { # 'ญ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 58: { # 'ฎ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 57: { # 'ฏ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 49: { # 'ฐ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 53: { # 'ฑ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 55: { # 'ฒ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 43: { # 'ณ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 3, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 3, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 20: { # 'ด' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 2, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 2, # '็' + 6: 1, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 19: { # 'ต' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 2, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 1, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 44: { # 'ถ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 1, # 'ี' + 40: 3, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 14: { # 'ท' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 3, # 'ศ' + 46: 1, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 1, # 'ื' + 32: 3, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 48: { # 'ธ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 2, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 3: { # 'น' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 1, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 3, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 3, # 'โ' + 29: 3, # 'ใ' + 33: 3, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 17: { # 'บ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 2, # 'ื' + 32: 3, # 'ุ' + 35: 2, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 25: { # 'ป' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 1, # 'ฎ' + 57: 3, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 1, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 1, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 2, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 1, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 39: { # 'ผ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 0, # 'ุ' + 35: 3, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 62: { # 'ฝ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 1, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 2, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 2, # '่' + 7: 1, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 31: { # 'พ' + 5: 1, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 1, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 1, # 'ึ' + 27: 3, # 'ื' + 32: 1, # 'ุ' + 35: 2, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 1, # '็' + 6: 0, # '่' + 7: 1, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 54: { # 'ฟ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 1, # 'ื' + 32: 1, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 45: { # 'ภ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 9: { # 'ม' + 5: 2, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 2, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 1, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 2, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 16: { # 'ย' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 1, # 'ึ' + 27: 2, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 2, # 'ๆ' + 37: 1, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 2: { # 'ร' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 2, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 3, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 3, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 2, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 3, # 'เ' + 28: 3, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 3, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 61: { # 'ฤ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 2, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 15: { # 'ล' + 5: 2, # 'ก' + 30: 3, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 3, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 2, # 'ฯ' + 22: 3, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 2, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 2, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 12: { # 'ว' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 3, # 'ิ' + 13: 2, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 2, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 42: { # 'ศ' + 5: 1, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 2, # 'ิ' + 13: 0, # 'ี' + 40: 3, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 2, # 'ู' + 11: 0, # 'เ' + 28: 1, # 'แ' + 41: 0, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 46: { # 'ษ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 2, # 'ฎ' + 57: 1, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 0, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 2, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 18: { # 'ส' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 3, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 3, # 'ำ' + 23: 3, # 'ิ' + 13: 3, # 'ี' + 40: 2, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 3, # 'ู' + 11: 2, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 1, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 21: { # 'ห' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 1, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 0, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 0, # 'ำ' + 23: 1, # 'ิ' + 13: 1, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 1, # 'ุ' + 35: 1, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 3, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 4: { # 'อ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 2, # 'ะ' + 10: 3, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 2, # 'ิ' + 13: 3, # 'ี' + 40: 0, # 'ึ' + 27: 3, # 'ื' + 32: 3, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 1, # '็' + 6: 2, # '่' + 7: 2, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 63: { # 'ฯ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 22: { # 'ะ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 10: { # 'ั' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 3, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 2, # 'ฐ' + 53: 0, # 'ฑ' + 55: 3, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 1: { # 'า' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 1, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 2, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 3, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 3, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 36: { # 'ำ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 23: { # 'ิ' + 5: 3, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 3, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 2, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 3, # 'ศ' + 46: 2, # 'ษ' + 18: 2, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 2, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 13: { # 'ี' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 1, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 2, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 40: { # 'ึ' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 1, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 27: { # 'ื' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 32: { # 'ุ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 3, # 'ค' + 8: 3, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 1, # 'ฒ' + 43: 3, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 2, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 1, # 'ภ' + 9: 3, # 'ม' + 16: 1, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 1, # 'ว' + 42: 1, # 'ศ' + 46: 2, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 2, # '้' + 38: 1, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 35: { # 'ู' + 5: 3, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 2, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 2, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 2, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 2, # 'น' + 17: 0, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 1, # 'แ' + 41: 1, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 3, # '่' + 7: 3, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 11: { # 'เ' + 5: 3, # 'ก' + 30: 3, # 'ข' + 24: 3, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 3, # 'ฉ' + 34: 3, # 'ช' + 51: 2, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 1, # 'ณ' + 20: 3, # 'ด' + 19: 3, # 'ต' + 44: 1, # 'ถ' + 14: 3, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 3, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 3, # 'พ' + 54: 1, # 'ฟ' + 45: 3, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 3, # 'ว' + 42: 2, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 28: { # 'แ' + 5: 3, # 'ก' + 30: 2, # 'ข' + 24: 2, # 'ค' + 8: 1, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 3, # 'ต' + 44: 2, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 2, # 'ป' + 39: 3, # 'ผ' + 62: 0, # 'ฝ' + 31: 2, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 41: { # 'โ' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 1, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 1, # 'ภ' + 9: 1, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 3, # 'ล' + 12: 0, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 0, # 'ห' + 4: 2, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 29: { # 'ใ' + 5: 2, # 'ก' + 30: 0, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 3, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 3, # 'ส' + 21: 3, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 33: { # 'ไ' + 5: 1, # 'ก' + 30: 2, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 3, # 'ด' + 19: 1, # 'ต' + 44: 0, # 'ถ' + 14: 3, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 1, # 'บ' + 25: 3, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 2, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 0, # 'ย' + 2: 3, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 2, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 50: { # 'ๆ' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 37: { # '็' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 2, # 'ง' + 26: 3, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 1, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 0, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 3, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 1, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 2, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 0, # 'ห' + 4: 1, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 1, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 6: { # '่' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 1, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 1, # 'ธ' + 3: 3, # 'น' + 17: 1, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 1, # 'ฝ' + 31: 1, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 3, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 2, # 'ล' + 12: 3, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 1, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 1, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 3, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 1, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 7: { # '้' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 2, # 'ค' + 8: 3, # 'ง' + 26: 2, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 1, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 1, # 'ด' + 19: 2, # 'ต' + 44: 1, # 'ถ' + 14: 2, # 'ท' + 48: 0, # 'ธ' + 3: 3, # 'น' + 17: 2, # 'บ' + 25: 2, # 'ป' + 39: 2, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 3, # 'ม' + 16: 2, # 'ย' + 2: 2, # 'ร' + 61: 0, # 'ฤ' + 15: 1, # 'ล' + 12: 3, # 'ว' + 42: 1, # 'ศ' + 46: 0, # 'ษ' + 18: 2, # 'ส' + 21: 2, # 'ห' + 4: 3, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 3, # 'า' + 36: 2, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 2, # 'ใ' + 33: 2, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 38: { # '์' + 5: 2, # 'ก' + 30: 1, # 'ข' + 24: 1, # 'ค' + 8: 0, # 'ง' + 26: 1, # 'จ' + 52: 0, # 'ฉ' + 34: 1, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 2, # 'ด' + 19: 1, # 'ต' + 44: 1, # 'ถ' + 14: 1, # 'ท' + 48: 0, # 'ธ' + 3: 1, # 'น' + 17: 1, # 'บ' + 25: 1, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 1, # 'พ' + 54: 1, # 'ฟ' + 45: 0, # 'ภ' + 9: 2, # 'ม' + 16: 0, # 'ย' + 2: 1, # 'ร' + 61: 1, # 'ฤ' + 15: 1, # 'ล' + 12: 1, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 1, # 'ส' + 21: 1, # 'ห' + 4: 2, # 'อ' + 63: 1, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 2, # 'เ' + 28: 2, # 'แ' + 41: 1, # 'โ' + 29: 1, # 'ใ' + 33: 1, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 0, # '๑' + 59: 0, # '๒' + 60: 0, # '๕' + }, + 56: { # '๑' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 1, # '๕' + }, + 59: { # '๒' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 1, # '๑' + 59: 1, # '๒' + 60: 3, # '๕' + }, + 60: { # '๕' + 5: 0, # 'ก' + 30: 0, # 'ข' + 24: 0, # 'ค' + 8: 0, # 'ง' + 26: 0, # 'จ' + 52: 0, # 'ฉ' + 34: 0, # 'ช' + 51: 0, # 'ซ' + 47: 0, # 'ญ' + 58: 0, # 'ฎ' + 57: 0, # 'ฏ' + 49: 0, # 'ฐ' + 53: 0, # 'ฑ' + 55: 0, # 'ฒ' + 43: 0, # 'ณ' + 20: 0, # 'ด' + 19: 0, # 'ต' + 44: 0, # 'ถ' + 14: 0, # 'ท' + 48: 0, # 'ธ' + 3: 0, # 'น' + 17: 0, # 'บ' + 25: 0, # 'ป' + 39: 0, # 'ผ' + 62: 0, # 'ฝ' + 31: 0, # 'พ' + 54: 0, # 'ฟ' + 45: 0, # 'ภ' + 9: 0, # 'ม' + 16: 0, # 'ย' + 2: 0, # 'ร' + 61: 0, # 'ฤ' + 15: 0, # 'ล' + 12: 0, # 'ว' + 42: 0, # 'ศ' + 46: 0, # 'ษ' + 18: 0, # 'ส' + 21: 0, # 'ห' + 4: 0, # 'อ' + 63: 0, # 'ฯ' + 22: 0, # 'ะ' + 10: 0, # 'ั' + 1: 0, # 'า' + 36: 0, # 'ำ' + 23: 0, # 'ิ' + 13: 0, # 'ี' + 40: 0, # 'ึ' + 27: 0, # 'ื' + 32: 0, # 'ุ' + 35: 0, # 'ู' + 11: 0, # 'เ' + 28: 0, # 'แ' + 41: 0, # 'โ' + 29: 0, # 'ใ' + 33: 0, # 'ไ' + 50: 0, # 'ๆ' + 37: 0, # '็' + 6: 0, # '่' + 7: 0, # '้' + 38: 0, # '์' + 56: 2, # '๑' + 59: 1, # '๒' + 60: 0, # '๕' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +TIS_620_THAI_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 254, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 254, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 253, # ' ' + 33: 253, # '!' + 34: 253, # '"' + 35: 253, # '#' + 36: 253, # '$' + 37: 253, # '%' + 38: 253, # '&' + 39: 253, # "'" + 40: 253, # '(' + 41: 253, # ')' + 42: 253, # '*' + 43: 253, # '+' + 44: 253, # ',' + 45: 253, # '-' + 46: 253, # '.' + 47: 253, # '/' + 48: 252, # '0' + 49: 252, # '1' + 50: 252, # '2' + 51: 252, # '3' + 52: 252, # '4' + 53: 252, # '5' + 54: 252, # '6' + 55: 252, # '7' + 56: 252, # '8' + 57: 252, # '9' + 58: 253, # ':' + 59: 253, # ';' + 60: 253, # '<' + 61: 253, # '=' + 62: 253, # '>' + 63: 253, # '?' + 64: 253, # '@' + 65: 182, # 'A' + 66: 106, # 'B' + 67: 107, # 'C' + 68: 100, # 'D' + 69: 183, # 'E' + 70: 184, # 'F' + 71: 185, # 'G' + 72: 101, # 'H' + 73: 94, # 'I' + 74: 186, # 'J' + 75: 187, # 'K' + 76: 108, # 'L' + 77: 109, # 'M' + 78: 110, # 'N' + 79: 111, # 'O' + 80: 188, # 'P' + 81: 189, # 'Q' + 82: 190, # 'R' + 83: 89, # 'S' + 84: 95, # 'T' + 85: 112, # 'U' + 86: 113, # 'V' + 87: 191, # 'W' + 88: 192, # 'X' + 89: 193, # 'Y' + 90: 194, # 'Z' + 91: 253, # '[' + 92: 253, # '\\' + 93: 253, # ']' + 94: 253, # '^' + 95: 253, # '_' + 96: 253, # '`' + 97: 64, # 'a' + 98: 72, # 'b' + 99: 73, # 'c' + 100: 114, # 'd' + 101: 74, # 'e' + 102: 115, # 'f' + 103: 116, # 'g' + 104: 102, # 'h' + 105: 81, # 'i' + 106: 201, # 'j' + 107: 117, # 'k' + 108: 90, # 'l' + 109: 103, # 'm' + 110: 78, # 'n' + 111: 82, # 'o' + 112: 96, # 'p' + 113: 202, # 'q' + 114: 91, # 'r' + 115: 79, # 's' + 116: 84, # 't' + 117: 104, # 'u' + 118: 105, # 'v' + 119: 97, # 'w' + 120: 98, # 'x' + 121: 92, # 'y' + 122: 203, # 'z' + 123: 253, # '{' + 124: 253, # '|' + 125: 253, # '}' + 126: 253, # '~' + 127: 253, # '\x7f' + 128: 209, # '\x80' + 129: 210, # '\x81' + 130: 211, # '\x82' + 131: 212, # '\x83' + 132: 213, # '\x84' + 133: 88, # '\x85' + 134: 214, # '\x86' + 135: 215, # '\x87' + 136: 216, # '\x88' + 137: 217, # '\x89' + 138: 218, # '\x8a' + 139: 219, # '\x8b' + 140: 220, # '\x8c' + 141: 118, # '\x8d' + 142: 221, # '\x8e' + 143: 222, # '\x8f' + 144: 223, # '\x90' + 145: 224, # '\x91' + 146: 99, # '\x92' + 147: 85, # '\x93' + 148: 83, # '\x94' + 149: 225, # '\x95' + 150: 226, # '\x96' + 151: 227, # '\x97' + 152: 228, # '\x98' + 153: 229, # '\x99' + 154: 230, # '\x9a' + 155: 231, # '\x9b' + 156: 232, # '\x9c' + 157: 233, # '\x9d' + 158: 234, # '\x9e' + 159: 235, # '\x9f' + 160: 236, # None + 161: 5, # 'ก' + 162: 30, # 'ข' + 163: 237, # 'ฃ' + 164: 24, # 'ค' + 165: 238, # 'ฅ' + 166: 75, # 'ฆ' + 167: 8, # 'ง' + 168: 26, # 'จ' + 169: 52, # 'ฉ' + 170: 34, # 'ช' + 171: 51, # 'ซ' + 172: 119, # 'ฌ' + 173: 47, # 'ญ' + 174: 58, # 'ฎ' + 175: 57, # 'ฏ' + 176: 49, # 'ฐ' + 177: 53, # 'ฑ' + 178: 55, # 'ฒ' + 179: 43, # 'ณ' + 180: 20, # 'ด' + 181: 19, # 'ต' + 182: 44, # 'ถ' + 183: 14, # 'ท' + 184: 48, # 'ธ' + 185: 3, # 'น' + 186: 17, # 'บ' + 187: 25, # 'ป' + 188: 39, # 'ผ' + 189: 62, # 'ฝ' + 190: 31, # 'พ' + 191: 54, # 'ฟ' + 192: 45, # 'ภ' + 193: 9, # 'ม' + 194: 16, # 'ย' + 195: 2, # 'ร' + 196: 61, # 'ฤ' + 197: 15, # 'ล' + 198: 239, # 'ฦ' + 199: 12, # 'ว' + 200: 42, # 'ศ' + 201: 46, # 'ษ' + 202: 18, # 'ส' + 203: 21, # 'ห' + 204: 76, # 'ฬ' + 205: 4, # 'อ' + 206: 66, # 'ฮ' + 207: 63, # 'ฯ' + 208: 22, # 'ะ' + 209: 10, # 'ั' + 210: 1, # 'า' + 211: 36, # 'ำ' + 212: 23, # 'ิ' + 213: 13, # 'ี' + 214: 40, # 'ึ' + 215: 27, # 'ื' + 216: 32, # 'ุ' + 217: 35, # 'ู' + 218: 86, # 'ฺ' + 219: 240, # None + 220: 241, # None + 221: 242, # None + 222: 243, # None + 223: 244, # '฿' + 224: 11, # 'เ' + 225: 28, # 'แ' + 226: 41, # 'โ' + 227: 29, # 'ใ' + 228: 33, # 'ไ' + 229: 245, # 'ๅ' + 230: 50, # 'ๆ' + 231: 37, # '็' + 232: 6, # '่' + 233: 7, # '้' + 234: 67, # '๊' + 235: 77, # '๋' + 236: 38, # '์' + 237: 93, # 'ํ' + 238: 246, # '๎' + 239: 247, # '๏' + 240: 68, # '๐' + 241: 56, # '๑' + 242: 59, # '๒' + 243: 65, # '๓' + 244: 69, # '๔' + 245: 60, # '๕' + 246: 70, # '๖' + 247: 80, # '๗' + 248: 71, # '๘' + 249: 87, # '๙' + 250: 248, # '๚' + 251: 249, # '๛' + 252: 250, # None + 253: 251, # None + 254: 252, # None + 255: 253, # None +} + +TIS_620_THAI_MODEL = SingleByteCharSetModel( + charset_name="TIS-620", + language="Thai", + char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, + language_model=THAI_LANG_MODEL, + typical_positive_ratio=0.926386, + keep_ascii_letters=False, + alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langturkishmodel.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langturkishmodel.py new file mode 100644 index 0000000..291857c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/langturkishmodel.py @@ -0,0 +1,4380 @@ +from pip._vendor.chardet.sbcharsetprober import SingleByteCharSetModel + +# 3: Positive +# 2: Likely +# 1: Unlikely +# 0: Negative + +TURKISH_LANG_MODEL = { + 23: { # 'A' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 37: { # 'B' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 47: { # 'C' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 39: { # 'D' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 29: { # 'E' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 52: { # 'F' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 36: { # 'G' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 45: { # 'H' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 2, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 2, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 53: { # 'I' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 60: { # 'J' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 16: { # 'K' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 49: { # 'L' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 2, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 20: { # 'M' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 46: { # 'N' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 42: { # 'O' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 48: { # 'P' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 44: { # 'R' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, + 35: { # 'S' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 2, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 31: { # 'T' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 51: { # 'U' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 1, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 38: { # 'V' + 23: 1, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 2, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 1, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 62: { # 'W' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 43: { # 'Y' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 1, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 56: { # 'Z' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 1: { # 'a' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 21: { # 'b' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 2, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 28: { # 'c' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 3, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 1, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 2, # 'ş' + }, + 12: { # 'd' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 2: { # 'e' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 2, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 18: { # 'f' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 1, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 1, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 27: { # 'g' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 25: { # 'h' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 3: { # 'i' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 1, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 1, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 1, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 24: { # 'j' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 2, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 10: { # 'k' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 5: { # 'l' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 1, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 13: { # 'm' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 2, # 'u' + 32: 2, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 4: { # 'n' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 3, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 15: { # 'o' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 2, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 2, # 'İ' + 6: 3, # 'ı' + 40: 2, # 'Ş' + 19: 2, # 'ş' + }, + 26: { # 'p' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 1, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 7: { # 'r' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 1, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 3, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 8: { # 's' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 2, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 9: { # 't' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 2, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 2, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 14: { # 'u' + 23: 3, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 3, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 2, # 'Z' + 1: 2, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 2, # 'e' + 18: 2, # 'f' + 27: 3, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 2, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 32: { # 'v' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 1, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 57: { # 'w' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 1, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 1, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 2, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 58: { # 'x' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 2, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 11: { # 'y' + 23: 1, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 2, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 3, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 22: { # 'z' + 23: 2, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 2, # 'D' + 29: 3, # 'E' + 52: 1, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 3, # 'T' + 51: 2, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 2, # 'e' + 18: 3, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 3, # 'y' + 22: 2, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 3, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 63: { # '·' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 54: { # 'Ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 1, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 0, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 2, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 2, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 2, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 50: { # 'Ö' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 2, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 1, # 'N' + 42: 2, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 1, # 'f' + 27: 1, # 'g' + 25: 1, # 'h' + 3: 2, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 2, # 'p' + 7: 3, # 'r' + 8: 1, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 1, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 2, # 'ü' + 30: 1, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 55: { # 'Ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 1, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 1, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 1, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 1, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 1, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 0, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 59: { # 'â' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 0, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 2, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 2, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 0, # 'ş' + }, + 33: { # 'ç' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 3, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 0, # 'Z' + 1: 0, # 'a' + 21: 3, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 2, # 'f' + 27: 1, # 'g' + 25: 3, # 'h' + 3: 3, # 'i' + 24: 0, # 'j' + 10: 3, # 'k' + 5: 0, # 'l' + 13: 0, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 3, # 't' + 14: 0, # 'u' + 32: 2, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 1, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 61: { # 'î' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 0, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 0, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 2, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 1, # 'j' + 10: 0, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 1, # 'n' + 15: 0, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 1, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 1, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 1, # 'î' + 34: 0, # 'ö' + 17: 0, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 1, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 34: { # 'ö' + 23: 0, # 'A' + 37: 1, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 1, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 1, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 2, # 'h' + 3: 1, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 2, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 0, # 'r' + 8: 3, # 's' + 9: 1, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 1, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 2, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 17: { # 'ü' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 0, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 1, # 'J' + 16: 1, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 0, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 0, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 0, # 'c' + 12: 1, # 'd' + 2: 3, # 'e' + 18: 1, # 'f' + 27: 2, # 'g' + 25: 0, # 'h' + 3: 1, # 'i' + 24: 1, # 'j' + 10: 2, # 'k' + 5: 3, # 'l' + 13: 2, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 2, # 'p' + 7: 2, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 3, # 'u' + 32: 1, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 2, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 30: { # 'ğ' + 23: 0, # 'A' + 37: 2, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 1, # 'M' + 46: 2, # 'N' + 42: 2, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 0, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 2, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 0, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 2, # 'e' + 18: 0, # 'f' + 27: 0, # 'g' + 25: 0, # 'h' + 3: 0, # 'i' + 24: 3, # 'j' + 10: 1, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 1, # 'o' + 26: 0, # 'p' + 7: 1, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 2, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 2, # 'İ' + 6: 2, # 'ı' + 40: 2, # 'Ş' + 19: 1, # 'ş' + }, + 41: { # 'İ' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 2, # 'G' + 45: 2, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 0, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 0, # 'Z' + 1: 1, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 2, # 'd' + 2: 1, # 'e' + 18: 0, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 2, # 'i' + 24: 2, # 'j' + 10: 2, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 1, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 2, # 't' + 14: 0, # 'u' + 32: 0, # 'v' + 57: 1, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 1, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 1, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 1, # 'ş' + }, + 6: { # 'ı' + 23: 2, # 'A' + 37: 0, # 'B' + 47: 0, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 2, # 'J' + 16: 3, # 'K' + 49: 0, # 'L' + 20: 3, # 'M' + 46: 1, # 'N' + 42: 0, # 'O' + 48: 0, # 'P' + 44: 0, # 'R' + 35: 0, # 'S' + 31: 2, # 'T' + 51: 0, # 'U' + 38: 0, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 3, # 'a' + 21: 2, # 'b' + 28: 1, # 'c' + 12: 3, # 'd' + 2: 3, # 'e' + 18: 3, # 'f' + 27: 3, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 3, # 'j' + 10: 3, # 'k' + 5: 3, # 'l' + 13: 3, # 'm' + 4: 3, # 'n' + 15: 0, # 'o' + 26: 3, # 'p' + 7: 3, # 'r' + 8: 3, # 's' + 9: 3, # 't' + 14: 3, # 'u' + 32: 3, # 'v' + 57: 1, # 'w' + 58: 1, # 'x' + 11: 3, # 'y' + 22: 0, # 'z' + 63: 1, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 2, # 'ç' + 61: 0, # 'î' + 34: 0, # 'ö' + 17: 3, # 'ü' + 30: 0, # 'ğ' + 41: 0, # 'İ' + 6: 3, # 'ı' + 40: 0, # 'Ş' + 19: 0, # 'ş' + }, + 40: { # 'Ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 1, # 'D' + 29: 1, # 'E' + 52: 0, # 'F' + 36: 1, # 'G' + 45: 2, # 'H' + 53: 1, # 'I' + 60: 0, # 'J' + 16: 0, # 'K' + 49: 0, # 'L' + 20: 2, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 2, # 'P' + 44: 2, # 'R' + 35: 1, # 'S' + 31: 1, # 'T' + 51: 0, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 2, # 'Y' + 56: 1, # 'Z' + 1: 0, # 'a' + 21: 2, # 'b' + 28: 0, # 'c' + 12: 2, # 'd' + 2: 0, # 'e' + 18: 3, # 'f' + 27: 0, # 'g' + 25: 2, # 'h' + 3: 3, # 'i' + 24: 2, # 'j' + 10: 1, # 'k' + 5: 0, # 'l' + 13: 1, # 'm' + 4: 3, # 'n' + 15: 2, # 'o' + 26: 0, # 'p' + 7: 3, # 'r' + 8: 2, # 's' + 9: 2, # 't' + 14: 1, # 'u' + 32: 3, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 2, # 'y' + 22: 0, # 'z' + 63: 0, # '·' + 54: 0, # 'Ç' + 50: 0, # 'Ö' + 55: 1, # 'Ü' + 59: 0, # 'â' + 33: 0, # 'ç' + 61: 0, # 'î' + 34: 2, # 'ö' + 17: 1, # 'ü' + 30: 2, # 'ğ' + 41: 0, # 'İ' + 6: 2, # 'ı' + 40: 1, # 'Ş' + 19: 2, # 'ş' + }, + 19: { # 'ş' + 23: 0, # 'A' + 37: 0, # 'B' + 47: 1, # 'C' + 39: 0, # 'D' + 29: 0, # 'E' + 52: 2, # 'F' + 36: 1, # 'G' + 45: 0, # 'H' + 53: 0, # 'I' + 60: 0, # 'J' + 16: 3, # 'K' + 49: 2, # 'L' + 20: 0, # 'M' + 46: 1, # 'N' + 42: 1, # 'O' + 48: 1, # 'P' + 44: 1, # 'R' + 35: 1, # 'S' + 31: 0, # 'T' + 51: 1, # 'U' + 38: 1, # 'V' + 62: 0, # 'W' + 43: 1, # 'Y' + 56: 0, # 'Z' + 1: 3, # 'a' + 21: 1, # 'b' + 28: 2, # 'c' + 12: 0, # 'd' + 2: 3, # 'e' + 18: 0, # 'f' + 27: 2, # 'g' + 25: 1, # 'h' + 3: 1, # 'i' + 24: 0, # 'j' + 10: 2, # 'k' + 5: 2, # 'l' + 13: 3, # 'm' + 4: 0, # 'n' + 15: 0, # 'o' + 26: 1, # 'p' + 7: 3, # 'r' + 8: 0, # 's' + 9: 0, # 't' + 14: 3, # 'u' + 32: 0, # 'v' + 57: 0, # 'w' + 58: 0, # 'x' + 11: 0, # 'y' + 22: 2, # 'z' + 63: 0, # '·' + 54: 1, # 'Ç' + 50: 2, # 'Ö' + 55: 0, # 'Ü' + 59: 0, # 'â' + 33: 1, # 'ç' + 61: 1, # 'î' + 34: 2, # 'ö' + 17: 0, # 'ü' + 30: 1, # 'ğ' + 41: 1, # 'İ' + 6: 1, # 'ı' + 40: 1, # 'Ş' + 19: 1, # 'ş' + }, +} + +# 255: Undefined characters that did not exist in training text +# 254: Carriage/Return +# 253: symbol (punctuation) that does not belong to word +# 252: 0 - 9 +# 251: Control characters + +# Character Mapping Table(s): +ISO_8859_9_TURKISH_CHAR_TO_ORDER = { + 0: 255, # '\x00' + 1: 255, # '\x01' + 2: 255, # '\x02' + 3: 255, # '\x03' + 4: 255, # '\x04' + 5: 255, # '\x05' + 6: 255, # '\x06' + 7: 255, # '\x07' + 8: 255, # '\x08' + 9: 255, # '\t' + 10: 255, # '\n' + 11: 255, # '\x0b' + 12: 255, # '\x0c' + 13: 255, # '\r' + 14: 255, # '\x0e' + 15: 255, # '\x0f' + 16: 255, # '\x10' + 17: 255, # '\x11' + 18: 255, # '\x12' + 19: 255, # '\x13' + 20: 255, # '\x14' + 21: 255, # '\x15' + 22: 255, # '\x16' + 23: 255, # '\x17' + 24: 255, # '\x18' + 25: 255, # '\x19' + 26: 255, # '\x1a' + 27: 255, # '\x1b' + 28: 255, # '\x1c' + 29: 255, # '\x1d' + 30: 255, # '\x1e' + 31: 255, # '\x1f' + 32: 255, # ' ' + 33: 255, # '!' + 34: 255, # '"' + 35: 255, # '#' + 36: 255, # '$' + 37: 255, # '%' + 38: 255, # '&' + 39: 255, # "'" + 40: 255, # '(' + 41: 255, # ')' + 42: 255, # '*' + 43: 255, # '+' + 44: 255, # ',' + 45: 255, # '-' + 46: 255, # '.' + 47: 255, # '/' + 48: 255, # '0' + 49: 255, # '1' + 50: 255, # '2' + 51: 255, # '3' + 52: 255, # '4' + 53: 255, # '5' + 54: 255, # '6' + 55: 255, # '7' + 56: 255, # '8' + 57: 255, # '9' + 58: 255, # ':' + 59: 255, # ';' + 60: 255, # '<' + 61: 255, # '=' + 62: 255, # '>' + 63: 255, # '?' + 64: 255, # '@' + 65: 23, # 'A' + 66: 37, # 'B' + 67: 47, # 'C' + 68: 39, # 'D' + 69: 29, # 'E' + 70: 52, # 'F' + 71: 36, # 'G' + 72: 45, # 'H' + 73: 53, # 'I' + 74: 60, # 'J' + 75: 16, # 'K' + 76: 49, # 'L' + 77: 20, # 'M' + 78: 46, # 'N' + 79: 42, # 'O' + 80: 48, # 'P' + 81: 69, # 'Q' + 82: 44, # 'R' + 83: 35, # 'S' + 84: 31, # 'T' + 85: 51, # 'U' + 86: 38, # 'V' + 87: 62, # 'W' + 88: 65, # 'X' + 89: 43, # 'Y' + 90: 56, # 'Z' + 91: 255, # '[' + 92: 255, # '\\' + 93: 255, # ']' + 94: 255, # '^' + 95: 255, # '_' + 96: 255, # '`' + 97: 1, # 'a' + 98: 21, # 'b' + 99: 28, # 'c' + 100: 12, # 'd' + 101: 2, # 'e' + 102: 18, # 'f' + 103: 27, # 'g' + 104: 25, # 'h' + 105: 3, # 'i' + 106: 24, # 'j' + 107: 10, # 'k' + 108: 5, # 'l' + 109: 13, # 'm' + 110: 4, # 'n' + 111: 15, # 'o' + 112: 26, # 'p' + 113: 64, # 'q' + 114: 7, # 'r' + 115: 8, # 's' + 116: 9, # 't' + 117: 14, # 'u' + 118: 32, # 'v' + 119: 57, # 'w' + 120: 58, # 'x' + 121: 11, # 'y' + 122: 22, # 'z' + 123: 255, # '{' + 124: 255, # '|' + 125: 255, # '}' + 126: 255, # '~' + 127: 255, # '\x7f' + 128: 180, # '\x80' + 129: 179, # '\x81' + 130: 178, # '\x82' + 131: 177, # '\x83' + 132: 176, # '\x84' + 133: 175, # '\x85' + 134: 174, # '\x86' + 135: 173, # '\x87' + 136: 172, # '\x88' + 137: 171, # '\x89' + 138: 170, # '\x8a' + 139: 169, # '\x8b' + 140: 168, # '\x8c' + 141: 167, # '\x8d' + 142: 166, # '\x8e' + 143: 165, # '\x8f' + 144: 164, # '\x90' + 145: 163, # '\x91' + 146: 162, # '\x92' + 147: 161, # '\x93' + 148: 160, # '\x94' + 149: 159, # '\x95' + 150: 101, # '\x96' + 151: 158, # '\x97' + 152: 157, # '\x98' + 153: 156, # '\x99' + 154: 155, # '\x9a' + 155: 154, # '\x9b' + 156: 153, # '\x9c' + 157: 152, # '\x9d' + 158: 151, # '\x9e' + 159: 106, # '\x9f' + 160: 150, # '\xa0' + 161: 149, # '¡' + 162: 148, # '¢' + 163: 147, # '£' + 164: 146, # '¤' + 165: 145, # '¥' + 166: 144, # '¦' + 167: 100, # '§' + 168: 143, # '¨' + 169: 142, # '©' + 170: 141, # 'ª' + 171: 140, # '«' + 172: 139, # '¬' + 173: 138, # '\xad' + 174: 137, # '®' + 175: 136, # '¯' + 176: 94, # '°' + 177: 80, # '±' + 178: 93, # '²' + 179: 135, # '³' + 180: 105, # '´' + 181: 134, # 'µ' + 182: 133, # '¶' + 183: 63, # '·' + 184: 132, # '¸' + 185: 131, # '¹' + 186: 130, # 'º' + 187: 129, # '»' + 188: 128, # '¼' + 189: 127, # '½' + 190: 126, # '¾' + 191: 125, # '¿' + 192: 124, # 'À' + 193: 104, # 'Á' + 194: 73, # 'Â' + 195: 99, # 'Ã' + 196: 79, # 'Ä' + 197: 85, # 'Å' + 198: 123, # 'Æ' + 199: 54, # 'Ç' + 200: 122, # 'È' + 201: 98, # 'É' + 202: 92, # 'Ê' + 203: 121, # 'Ë' + 204: 120, # 'Ì' + 205: 91, # 'Í' + 206: 103, # 'Î' + 207: 119, # 'Ï' + 208: 68, # 'Ğ' + 209: 118, # 'Ñ' + 210: 117, # 'Ò' + 211: 97, # 'Ó' + 212: 116, # 'Ô' + 213: 115, # 'Õ' + 214: 50, # 'Ö' + 215: 90, # '×' + 216: 114, # 'Ø' + 217: 113, # 'Ù' + 218: 112, # 'Ú' + 219: 111, # 'Û' + 220: 55, # 'Ü' + 221: 41, # 'İ' + 222: 40, # 'Ş' + 223: 86, # 'ß' + 224: 89, # 'à' + 225: 70, # 'á' + 226: 59, # 'â' + 227: 78, # 'ã' + 228: 71, # 'ä' + 229: 82, # 'å' + 230: 88, # 'æ' + 231: 33, # 'ç' + 232: 77, # 'è' + 233: 66, # 'é' + 234: 84, # 'ê' + 235: 83, # 'ë' + 236: 110, # 'ì' + 237: 75, # 'í' + 238: 61, # 'î' + 239: 96, # 'ï' + 240: 30, # 'ğ' + 241: 67, # 'ñ' + 242: 109, # 'ò' + 243: 74, # 'ó' + 244: 87, # 'ô' + 245: 102, # 'õ' + 246: 34, # 'ö' + 247: 95, # '÷' + 248: 81, # 'ø' + 249: 108, # 'ù' + 250: 76, # 'ú' + 251: 72, # 'û' + 252: 17, # 'ü' + 253: 6, # 'ı' + 254: 19, # 'ş' + 255: 107, # 'ÿ' +} + +ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel( + charset_name="ISO-8859-9", + language="Turkish", + char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, + language_model=TURKISH_LANG_MODEL, + typical_positive_ratio=0.97029, + keep_ascii_letters=True, + alphabet="ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş", +) diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/latin1prober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/latin1prober.py new file mode 100644 index 0000000..59a01d9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/latin1prober.py @@ -0,0 +1,147 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import List, Union + +from .charsetprober import CharSetProber +from .enums import ProbingState + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +CLASS_NUM = 8 # total classes + +# fmt: off +Latin1_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 + OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F + UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 + OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF + ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF + ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 + ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF + ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 + ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +Latin1ClassModel = ( +# UDF OTH ASC ASS ACV ACO ASV ASO + 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, # ASO +) +# fmt: on + + +class Latin1Prober(CharSetProber): + def __init__(self) -> None: + super().__init__() + self._last_char_class = OTH + self._freq_counter: List[int] = [] + self.reset() + + def reset(self) -> None: + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM + super().reset() + + @property + def charset_name(self) -> str: + return "ISO-8859-1" + + @property + def language(self) -> str: + return "" + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + byte_str = self.remove_xml_tags(byte_str) + for c in byte_str: + char_class = Latin1_CharToClass[c] + freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + char_class] + if freq == 0: + self._state = ProbingState.NOT_ME + break + self._freq_counter[freq] += 1 + self._last_char_class = char_class + + return self.state + + def get_confidence(self) -> float: + if self.state == ProbingState.NOT_ME: + return 0.01 + + total = sum(self._freq_counter) + confidence = ( + 0.0 + if total < 0.01 + else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total + ) + confidence = max(confidence, 0.0) + # lower the confidence of latin1 so that other more accurate + # detector can take priority. + confidence *= 0.73 + return confidence diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/macromanprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/macromanprober.py new file mode 100644 index 0000000..1425d10 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/macromanprober.py @@ -0,0 +1,162 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This code was modified from latin1prober.py by Rob Speer . +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Rob Speer - adapt to MacRoman encoding +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import List, Union + +from .charsetprober import CharSetProber +from .enums import ProbingState + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +ODD = 8 # character that is unlikely to appear +CLASS_NUM = 9 # total classes + +# The change from Latin1 is that we explicitly look for extended characters +# that are infrequently-occurring symbols, and consider them to always be +# improbable. This should let MacRoman get out of the way of more likely +# encodings in most situations. + +# fmt: off +MacRoman_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + ACV, ACV, ACO, ACV, ACO, ACV, ACV, ASV, # 80 - 87 + ASV, ASV, ASV, ASV, ASV, ASO, ASV, ASV, # 88 - 8F + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASV, # 90 - 97 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, ASO, # A0 - A7 + OTH, OTH, ODD, ODD, OTH, OTH, ACV, ACV, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, ASV, ASV, # B8 - BF + OTH, OTH, ODD, OTH, ODD, OTH, OTH, OTH, # C0 - C7 + OTH, OTH, OTH, ACV, ACV, ACV, ACV, ASV, # C8 - CF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, ODD, # D0 - D7 + ASV, ACV, ODD, OTH, OTH, OTH, OTH, OTH, # D8 - DF + OTH, OTH, OTH, OTH, OTH, ACV, ACV, ACV, # E0 - E7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # E8 - EF + ODD, ACV, ACV, ACV, ACV, ASV, ODD, ODD, # F0 - F7 + ODD, ODD, ODD, ODD, ODD, ODD, ODD, ODD, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +MacRomanClassModel = ( +# UDF OTH ASC ASS ACV ACO ASV ASO ODD + 0, 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, 1, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, 1, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, 1, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, 1, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, 1, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, 1, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, 1, # ASO + 0, 1, 1, 1, 1, 1, 1, 1, 1, # ODD +) +# fmt: on + + +class MacRomanProber(CharSetProber): + def __init__(self) -> None: + super().__init__() + self._last_char_class = OTH + self._freq_counter: List[int] = [] + self.reset() + + def reset(self) -> None: + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM + + # express the prior that MacRoman is a somewhat rare encoding; + # this can be done by starting out in a slightly improbable state + # that must be overcome + self._freq_counter[2] = 10 + + super().reset() + + @property + def charset_name(self) -> str: + return "MacRoman" + + @property + def language(self) -> str: + return "" + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + byte_str = self.remove_xml_tags(byte_str) + for c in byte_str: + char_class = MacRoman_CharToClass[c] + freq = MacRomanClassModel[(self._last_char_class * CLASS_NUM) + char_class] + if freq == 0: + self._state = ProbingState.NOT_ME + break + self._freq_counter[freq] += 1 + self._last_char_class = char_class + + return self.state + + def get_confidence(self) -> float: + if self.state == ProbingState.NOT_ME: + return 0.01 + + total = sum(self._freq_counter) + confidence = ( + 0.0 + if total < 0.01 + else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total + ) + confidence = max(confidence, 0.0) + # lower the confidence of MacRoman so that other more accurate + # detector can take priority. + confidence *= 0.73 + return confidence diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcharsetprober.py new file mode 100644 index 0000000..666307e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcharsetprober.py @@ -0,0 +1,95 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import Optional, Union + +from .chardistribution import CharDistributionAnalysis +from .charsetprober import CharSetProber +from .codingstatemachine import CodingStateMachine +from .enums import LanguageFilter, MachineState, ProbingState + + +class MultiByteCharSetProber(CharSetProber): + """ + MultiByteCharSetProber + """ + + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: + super().__init__(lang_filter=lang_filter) + self.distribution_analyzer: Optional[CharDistributionAnalysis] = None + self.coding_sm: Optional[CodingStateMachine] = None + self._last_char = bytearray(b"\0\0") + + def reset(self) -> None: + super().reset() + if self.coding_sm: + self.coding_sm.reset() + if self.distribution_analyzer: + self.distribution_analyzer.reset() + self._last_char = bytearray(b"\0\0") + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + assert self.coding_sm is not None + assert self.distribution_analyzer is not None + + for i, byte in enumerate(byte_str): + coding_state = self.coding_sm.next_state(byte) + if coding_state == MachineState.ERROR: + self.logger.debug( + "%s %s prober hit error at byte %s", + self.charset_name, + self.language, + i, + ) + self._state = ProbingState.NOT_ME + break + if coding_state == MachineState.ITS_ME: + self._state = ProbingState.FOUND_IT + break + if coding_state == MachineState.START: + char_len = self.coding_sm.get_current_charlen() + if i == 0: + self._last_char[1] = byte + self.distribution_analyzer.feed(self._last_char, char_len) + else: + self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len) + + self._last_char[0] = byte_str[-1] + + if self.state == ProbingState.DETECTING: + if self.distribution_analyzer.got_enough_data() and ( + self.get_confidence() > self.SHORTCUT_THRESHOLD + ): + self._state = ProbingState.FOUND_IT + + return self.state + + def get_confidence(self) -> float: + assert self.distribution_analyzer is not None + return self.distribution_analyzer.get_confidence() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcsgroupprober.py new file mode 100644 index 0000000..6cb9cc7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcsgroupprober.py @@ -0,0 +1,57 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# Proofpoint, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .big5prober import Big5Prober +from .charsetgroupprober import CharSetGroupProber +from .cp949prober import CP949Prober +from .enums import LanguageFilter +from .eucjpprober import EUCJPProber +from .euckrprober import EUCKRProber +from .euctwprober import EUCTWProber +from .gb2312prober import GB2312Prober +from .johabprober import JOHABProber +from .sjisprober import SJISProber +from .utf8prober import UTF8Prober + + +class MBCSGroupProber(CharSetGroupProber): + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: + super().__init__(lang_filter=lang_filter) + self.probers = [ + UTF8Prober(), + SJISProber(), + EUCJPProber(), + GB2312Prober(), + EUCKRProber(), + CP949Prober(), + Big5Prober(), + EUCTWProber(), + JOHABProber(), + ] + self.reset() diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcssm.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcssm.py new file mode 100644 index 0000000..7bbe97e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/mbcssm.py @@ -0,0 +1,661 @@ +######################## BEGIN LICENSE BLOCK ######################## +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Mark Pilgrim - port to Python +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from .codingstatemachinedict import CodingStateMachineDict +from .enums import MachineState + +# BIG5 + +# fmt: off +BIG5_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as legal value + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f + 4, 4, 4, 4, 4, 4, 4, 4, # 80 - 87 + 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f + 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97 + 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f + 4, 3, 3, 3, 3, 3, 3, 3, # a0 - a7 + 3, 3, 3, 3, 3, 3, 3, 3, # a8 - af + 3, 3, 3, 3, 3, 3, 3, 3, # b0 - b7 + 3, 3, 3, 3, 3, 3, 3, 3, # b8 - bf + 3, 3, 3, 3, 3, 3, 3, 3, # c0 - c7 + 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf + 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7 + 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df + 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 + 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef + 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 + 3, 3, 3, 3, 3, 3, 3, 0 # f8 - ff +) + +BIG5_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 +) +# fmt: on + +BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) + +BIG5_SM_MODEL: CodingStateMachineDict = { + "class_table": BIG5_CLS, + "class_factor": 5, + "state_table": BIG5_ST, + "char_len_table": BIG5_CHAR_LEN_TABLE, + "name": "Big5", +} + +# CP949 +# fmt: off +CP949_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, # 00 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, # 10 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3f + 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 4f + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 50 - 5f + 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, # 60 - 6f + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 70 - 7f + 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 80 - 8f + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 9f + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, # a0 - af + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, # b0 - bf + 7, 7, 7, 7, 7, 7, 9, 2, 2, 3, 2, 2, 2, 2, 2, 2, # c0 - cf + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # d0 - df + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # e0 - ef + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, # f0 - ff +) + +CP949_ST = ( +#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 +) +# fmt: on + +CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) + +CP949_SM_MODEL: CodingStateMachineDict = { + "class_table": CP949_CLS, + "class_factor": 10, + "state_table": CP949_ST, + "char_len_table": CP949_CHAR_LEN_TABLE, + "name": "CP949", +} + +# EUC-JP +# fmt: off +EUCJP_CLS = ( + 4, 4, 4, 4, 4, 4, 4, 4, # 00 - 07 + 4, 4, 4, 4, 4, 4, 5, 5, # 08 - 0f + 4, 4, 4, 4, 4, 4, 4, 4, # 10 - 17 + 4, 4, 4, 5, 4, 4, 4, 4, # 18 - 1f + 4, 4, 4, 4, 4, 4, 4, 4, # 20 - 27 + 4, 4, 4, 4, 4, 4, 4, 4, # 28 - 2f + 4, 4, 4, 4, 4, 4, 4, 4, # 30 - 37 + 4, 4, 4, 4, 4, 4, 4, 4, # 38 - 3f + 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 47 + 4, 4, 4, 4, 4, 4, 4, 4, # 48 - 4f + 4, 4, 4, 4, 4, 4, 4, 4, # 50 - 57 + 4, 4, 4, 4, 4, 4, 4, 4, # 58 - 5f + 4, 4, 4, 4, 4, 4, 4, 4, # 60 - 67 + 4, 4, 4, 4, 4, 4, 4, 4, # 68 - 6f + 4, 4, 4, 4, 4, 4, 4, 4, # 70 - 77 + 4, 4, 4, 4, 4, 4, 4, 4, # 78 - 7f + 5, 5, 5, 5, 5, 5, 5, 5, # 80 - 87 + 5, 5, 5, 5, 5, 5, 1, 3, # 88 - 8f + 5, 5, 5, 5, 5, 5, 5, 5, # 90 - 97 + 5, 5, 5, 5, 5, 5, 5, 5, # 98 - 9f + 5, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 + 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef + 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 + 0, 0, 0, 0, 0, 0, 0, 5 # f8 - ff +) + +EUCJP_ST = ( + 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f + 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 +) +# fmt: on + +EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) + +EUCJP_SM_MODEL: CodingStateMachineDict = { + "class_table": EUCJP_CLS, + "class_factor": 6, + "state_table": EUCJP_ST, + "char_len_table": EUCJP_CHAR_LEN_TABLE, + "name": "EUC-JP", +} + +# EUC-KR +# fmt: off +EUCKR_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f + 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57 + 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f + 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67 + 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f + 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77 + 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 3, 3, 3, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 3, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 + 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef + 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 + 2, 2, 2, 2, 2, 2, 2, 0 # f8 - ff +) + +EUCKR_ST = ( + MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f +) +# fmt: on + +EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) + +EUCKR_SM_MODEL: CodingStateMachineDict = { + "class_table": EUCKR_CLS, + "class_factor": 4, + "state_table": EUCKR_ST, + "char_len_table": EUCKR_CHAR_LEN_TABLE, + "name": "EUC-KR", +} + +# JOHAB +# fmt: off +JOHAB_CLS = ( + 4,4,4,4,4,4,4,4, # 00 - 07 + 4,4,4,4,4,4,0,0, # 08 - 0f + 4,4,4,4,4,4,4,4, # 10 - 17 + 4,4,4,0,4,4,4,4, # 18 - 1f + 4,4,4,4,4,4,4,4, # 20 - 27 + 4,4,4,4,4,4,4,4, # 28 - 2f + 4,3,3,3,3,3,3,3, # 30 - 37 + 3,3,3,3,3,3,3,3, # 38 - 3f + 3,1,1,1,1,1,1,1, # 40 - 47 + 1,1,1,1,1,1,1,1, # 48 - 4f + 1,1,1,1,1,1,1,1, # 50 - 57 + 1,1,1,1,1,1,1,1, # 58 - 5f + 1,1,1,1,1,1,1,1, # 60 - 67 + 1,1,1,1,1,1,1,1, # 68 - 6f + 1,1,1,1,1,1,1,1, # 70 - 77 + 1,1,1,1,1,1,1,2, # 78 - 7f + 6,6,6,6,8,8,8,8, # 80 - 87 + 8,8,8,8,8,8,8,8, # 88 - 8f + 8,7,7,7,7,7,7,7, # 90 - 97 + 7,7,7,7,7,7,7,7, # 98 - 9f + 7,7,7,7,7,7,7,7, # a0 - a7 + 7,7,7,7,7,7,7,7, # a8 - af + 7,7,7,7,7,7,7,7, # b0 - b7 + 7,7,7,7,7,7,7,7, # b8 - bf + 7,7,7,7,7,7,7,7, # c0 - c7 + 7,7,7,7,7,7,7,7, # c8 - cf + 7,7,7,7,5,5,5,5, # d0 - d7 + 5,9,9,9,9,9,9,5, # d8 - df + 9,9,9,9,9,9,9,9, # e0 - e7 + 9,9,9,9,9,9,9,9, # e8 - ef + 9,9,9,9,9,9,9,9, # f0 - f7 + 9,9,5,5,5,5,5,0 # f8 - ff +) + +JOHAB_ST = ( +# cls = 0 1 2 3 4 5 6 7 8 9 + MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.ERROR ,MachineState.ERROR ,3 ,3 ,4 , # MachineState.START + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME + MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR , # MachineState.ERROR + MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.ERROR ,MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START , # 3 + MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START , # 4 +) +# fmt: on + +JOHAB_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 0, 0, 2, 2, 2) + +JOHAB_SM_MODEL: CodingStateMachineDict = { + "class_table": JOHAB_CLS, + "class_factor": 10, + "state_table": JOHAB_ST, + "char_len_table": JOHAB_CHAR_LEN_TABLE, + "name": "Johab", +} + +# EUC-TW +# fmt: off +EUCTW_CLS = ( + 2, 2, 2, 2, 2, 2, 2, 2, # 00 - 07 + 2, 2, 2, 2, 2, 2, 0, 0, # 08 - 0f + 2, 2, 2, 2, 2, 2, 2, 2, # 10 - 17 + 2, 2, 2, 0, 2, 2, 2, 2, # 18 - 1f + 2, 2, 2, 2, 2, 2, 2, 2, # 20 - 27 + 2, 2, 2, 2, 2, 2, 2, 2, # 28 - 2f + 2, 2, 2, 2, 2, 2, 2, 2, # 30 - 37 + 2, 2, 2, 2, 2, 2, 2, 2, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 2, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 6, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 3, 4, 4, 4, 4, 4, 4, # a0 - a7 + 5, 5, 1, 1, 1, 1, 1, 1, # a8 - af + 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7 + 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf + 1, 1, 3, 1, 3, 3, 3, 3, # c0 - c7 + 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf + 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7 + 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df + 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 + 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef + 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 + 3, 3, 3, 3, 3, 3, 3, 0 # f8 - ff +) + +EUCTW_ST = ( + MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 + MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 + MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) +# fmt: on + +EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) + +EUCTW_SM_MODEL: CodingStateMachineDict = { + "class_table": EUCTW_CLS, + "class_factor": 7, + "state_table": EUCTW_ST, + "char_len_table": EUCTW_CHAR_LEN_TABLE, + "name": "x-euc-tw", +} + +# GB2312 +# fmt: off +GB2312_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 3, 3, 3, 3, 3, 3, 3, 3, # 30 - 37 + 3, 3, 1, 1, 1, 1, 1, 1, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 4, # 78 - 7f + 5, 6, 6, 6, 6, 6, 6, 6, # 80 - 87 + 6, 6, 6, 6, 6, 6, 6, 6, # 88 - 8f + 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 97 + 6, 6, 6, 6, 6, 6, 6, 6, # 98 - 9f + 6, 6, 6, 6, 6, 6, 6, 6, # a0 - a7 + 6, 6, 6, 6, 6, 6, 6, 6, # a8 - af + 6, 6, 6, 6, 6, 6, 6, 6, # b0 - b7 + 6, 6, 6, 6, 6, 6, 6, 6, # b8 - bf + 6, 6, 6, 6, 6, 6, 6, 6, # c0 - c7 + 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf + 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7 + 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df + 6, 6, 6, 6, 6, 6, 6, 6, # e0 - e7 + 6, 6, 6, 6, 6, 6, 6, 6, # e8 - ef + 6, 6, 6, 6, 6, 6, 6, 6, # f0 - f7 + 6, 6, 6, 6, 6, 6, 6, 0 # f8 - ff +) + +GB2312_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 + 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f +) +# fmt: on + +# To be accurate, the length of class 6 can be either 2 or 4. +# But it is not necessary to discriminate between the two since +# it is used for frequency analysis only, and we are validating +# each code range there as well. So it is safe to set it to be +# 2 here. +GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) + +GB2312_SM_MODEL: CodingStateMachineDict = { + "class_table": GB2312_CLS, + "class_factor": 7, + "state_table": GB2312_ST, + "char_len_table": GB2312_CHAR_LEN_TABLE, + "name": "GB2312", +} + +# Shift_JIS +# fmt: off +SJIS_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 + 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f + 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 + 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f + 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 + 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f + 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 + 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f + 3, 3, 3, 3, 3, 2, 2, 3, # 80 - 87 + 3, 3, 3, 3, 3, 3, 3, 3, # 88 - 8f + 3, 3, 3, 3, 3, 3, 3, 3, # 90 - 97 + 3, 3, 3, 3, 3, 3, 3, 3, # 98 - 9f + #0xa0 is illegal in sjis encoding, but some pages does + #contain such byte. We need to be more error forgiven. + 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 + 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af + 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 + 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf + 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 + 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf + 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 + 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df + 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 + 3, 3, 3, 3, 3, 4, 4, 4, # e8 - ef + 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 + 3, 3, 3, 3, 3, 0, 0, 0, # f8 - ff +) + +SJIS_ST = ( + MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 +) +# fmt: on + +SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) + +SJIS_SM_MODEL: CodingStateMachineDict = { + "class_table": SJIS_CLS, + "class_factor": 6, + "state_table": SJIS_ST, + "char_len_table": SJIS_CHAR_LEN_TABLE, + "name": "Shift_JIS", +} + +# UCS2-BE +# fmt: off +UCS2BE_CLS = ( + 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7 + 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af + 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7 + 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf + 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7 + 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf + 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7 + 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df + 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 + 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef + 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 + 0, 0, 0, 0, 0, 0, 4, 5 # f8 - ff +) + +UCS2BE_ST = ( + 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 + 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f + 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 + 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f + 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) +# fmt: on + +UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) + +UCS2BE_SM_MODEL: CodingStateMachineDict = { + "class_table": UCS2BE_CLS, + "class_factor": 6, + "state_table": UCS2BE_ST, + "char_len_table": UCS2BE_CHAR_LEN_TABLE, + "name": "UTF-16BE", +} + +# UCS2-LE +# fmt: off +UCS2LE_CLS = ( + 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 + 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f + 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 + 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f + 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 + 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f + 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 + 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f + 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 + 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f + 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 + 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f + 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 + 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f + 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 + 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f + 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 + 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f + 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 + 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f + 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7 + 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af + 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7 + 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf + 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7 + 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf + 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7 + 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df + 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 + 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef + 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 + 0, 0, 0, 0, 0, 0, 4, 5 # f8 - ff +) + +UCS2LE_ST = ( + 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f + MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f + 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 + 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f + 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 +) +# fmt: on + +UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) + +UCS2LE_SM_MODEL: CodingStateMachineDict = { + "class_table": UCS2LE_CLS, + "class_factor": 6, + "state_table": UCS2LE_ST, + "char_len_table": UCS2LE_CHAR_LEN_TABLE, + "name": "UTF-16LE", +} + +# UTF-8 +# fmt: off +UTF8_CLS = ( + 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as a legal value + 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f + 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 + 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f + 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 + 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f + 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 + 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f + 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f + 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57 + 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f + 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67 + 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f + 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77 + 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f + 2, 2, 2, 2, 3, 3, 3, 3, # 80 - 87 + 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f + 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97 + 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f + 5, 5, 5, 5, 5, 5, 5, 5, # a0 - a7 + 5, 5, 5, 5, 5, 5, 5, 5, # a8 - af + 5, 5, 5, 5, 5, 5, 5, 5, # b0 - b7 + 5, 5, 5, 5, 5, 5, 5, 5, # b8 - bf + 0, 0, 6, 6, 6, 6, 6, 6, # c0 - c7 + 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf + 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7 + 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df + 7, 8, 8, 8, 8, 8, 8, 8, # e0 - e7 + 8, 8, 8, 8, 8, 9, 8, 8, # e8 - ef + 10, 11, 11, 11, 11, 11, 11, 11, # f0 - f7 + 12, 13, 13, 13, 14, 15, 0, 0 # f8 - ff +) + +UTF8_ST = ( + MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 + 9, 11, 8, 7, 6, 5, 4, 3,#08-0f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 + MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f + MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f + MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f + MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f + MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af + MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf + MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 + MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf +) +# fmt: on + +UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) + +UTF8_SM_MODEL: CodingStateMachineDict = { + "class_table": UTF8_CLS, + "class_factor": 16, + "state_table": UTF8_ST, + "char_len_table": UTF8_CHAR_LEN_TABLE, + "name": "UTF-8", +} diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__init__.py b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..43edb855787d0e1233556c5e76c0ab9ded98d410 GIT binary patch literal 222 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/LP.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e7dfc8dd2a3efc243f9b2a2a69a94ec10434456 GIT binary patch literal 2489 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 Nc$;|e->3+_@E?C4c9{SG literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..9ae89b845495b77df2f2282a158f76e80a3fd1b4 GIT binary patch literal 663 zcmZuu&ui2`6n-<=ZMHQFO0iau@`>7UZmo)+RwPu?o=)RQx_S+w9B-uu4q{p8JQx4Q!R-8#j|l@`DcLl$qT0reLR zj)4I~DTK&+93qZJi;P8u_W#o`JOPG}fH}IR^G|XEM|Ovc`pCP;4Lfn>+DQ`%-4gX$ zKMf1wMzanzq`s`dF>t$eh#3l<68D^{9Xt&lKY!<0ceCKJC|J;IRUJz6DC3k?E0kt} zO;UqBO5aSPw28QsG7(ey6>2l(JR|!e<3vU>i*oX!5F;KdNnVP9P)RJwGm#I)qyF}e zl*us9c&>ef3riBFU@eze#QrtK3D< z-Fvmy*IDzC%cj2_a!I4}&i(m$9u%vZntRK>HfIh(sJ;#8>NmJ?wgkBSVdMSA46RSm l`frTTwT}-!?|#~yd7D#jbLMSLy{#IwTswfT|1t@7#vkCwm+=4q literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/NS.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08edfb2d8bf05a5e45e76622eff0775aa203c6a6 GIT binary patch literal 651 zcmZutzi-n(6n=M3lejbzB7j;&m7rqDP@jMmAvLPRgoVgdFDJ)$N^R`B;BzXO4XF|^ zF_wR!;!o+6sgl{~#8%`^oqBh1R4Va?_rC9YKlRRfy>-y`^bE%r4#0&WD{D8P{-MDM zFkmQ!0NIEG#8I-USas<5KMlhhVE7oAqkFpl6gM!jH{8{ejl0~ipZY=@mjQxKiH5B| zhWSyW*?<~S-`C&-xV<{S3E+GIMEG8s{N3bmPkosdJBaiT(%g@U}xWx}IEk@s>Ui#Sr`trSzaKib_>DxQjr zi-LGZTpW=!o{(8t97^%hd;Wr`xZu09F!~Tqxgty`lCz?mX}sqj5P$5=%Dkl)TK8B3 zwWi%M{BHGg^Es;zyH!uOdU2M`i*Sxu%@}1-1v&6K}@$Zh-PNhc|ELy?Gx0 z-f!N_cbQBNg7(;Vy!~Ywp`Yj=X>zQz4{->sArqOLgNhvM`M4M2UW|Jw?&Tt9ai#tw zRJVWF9FJZ{CclDA0oDw3eG{!TlxW?)w7aV(+NKa`oAE@k=YQ@&ggr>GW+aK&?Mq`_ zJ<c;S5F>&nGk!Ate}-NON1)sGl`kE``lu#n`6$?`u*{1!^RfEaHam zqTP3qdKV2aWfTqwFMTv|WunE6HMp_+JjadJW>(ME&$coXjm$(VlWk$ zBP4D+A`VC@U6fp+hhg~LO@V67{KxqRk0}MbU(5AzNlM&~%Bk0s!{c$xFozUs< z@Trqs;UJ|mJTFC4BG2m{o=>HP$t0@#c>c;{G-*;;b$UX?Y#$RFNc_W8=yEzGhLot1 zib|p9XD^rf^Kk13&ZX=yAy(0`y$QQ~7#N|e-4=c}UhYA6}M6q=Y;FQ=uh z&Vz?SN?a8WOhjYj(J@g836c_$1*A*~^@U#w;YoH*Ov^Or#MyqRC~nBE5%+4F-yD>E z7Uhv;Fn62SkbwMF#X^h`g5x)2gvt@4q~N^9L}-D!MnqhK2P}<@xJ8B~1h3$_M%vcG z3O>ONG>^y#et{{Yc%jV##g`+lk`ezf5v-BXDa`orhd_XUVL6)<_~tB+!_Oly%-TO(aY3)+#-Ymn!3>EI}aGYt>6-mM-J9PzcH8N<3CA z>k>#n1yeiBgRv>q`P~)#_PVHz_DDu;vg#JzC|=b@tt?~BZ-!A@EY2*G*lN{FrNs#| z^IN^sM9>)?x&I$%2Wxa%nVb-1badiUT!rJp@==AKj7#d%GWuW{ojy2EX6M0Pxej$; zu1nLZsL1t*D}cG;N?cOZs1y^ih3m9BIgu3Q1|SAKU>IU@RCmilEE!c4-DMn)k`^qv zBn}S)MLcHbc?J23Rx8cQ=!4`fkY#F&!)1L!FZ3z%kbOH#eB&b6nmKoY*|OZ6?^t7k z>n+mZk7w zIN!a}u%c)UUC_r?>$}UfEbsZKca7V3w`tq*#dp)WGk2Sz@*)2|uQeaYQSbQh0gqX* z2J^$G0H1`vKm*Q!O-q=*2r*d+EYP-GtB)Y89Zn!5sYt4AjW$;md$*7wVOvN^j76Iv zvt%{<7f4Go;sC^OgddW}gN*LF6jj9DZk@fa6G4Z(1Ez!pC}?3?Tk+< zCr}U&R3wvv0H7ja3XOB>10w7CfcUis?1u{Db$&=5cu2Mez4Cq-h9N;`F2$v&5{t)m zH<(H?J~}N25fQDJ0TphrB4i5{h!S*T^U+=4vc}?)?vX{Xzxb>1jHo-tr?6QWDkwA> zyJQn(42(Gl8Zv&vzDyNop-ag@HcKE=d1LMkjcWx%@pAJ==8h~oKleVd9%z~y_-EjW zVqjMxuq!`sWAOT*7U(PndJ2IaEpRwD0A{%$EF~9{8n>&+?JscqH4cDcy}oIwbFp)^ zt}S1e@4j*L`qBIkR-(74KXSk4RzoLllegP%H{HH+r{Rw4j(TTwl{>%6oHwkq90QPb zVr;n#RtMJU+|)YV-$Q4BUT*s!93|T_SfJasYtSB{d$OJ~OZQ0asy)|)jZ&p?Sx+_3 z3^BTS!mM(}v$@{QrEG?H)zD70ZQs`K&3Y5a_f;gpjyL4SvM;T24g@>e_!(K|EjGy7I)HPV^#O^UR;^T)BCQ#p z15gM>XIXca$+B5b)|>U+G8_p*)dTy!VcYIRS)(Wb>s!4F*yXBIy)(u$6t3Djzy;@Cm@88I}jN=6<|Of z#qcdUBT9lY1<|TbpOWRYtUHsUB)4N>;DQWaW5g@$sGLq=3<>&mXDq46Xf@_Sa{2}M9x1>}^17;uF6mZBi%YQ-+NG_d6qbZ0mNTWsaeL#IN)vdt0?7@~2@ z(z(TR?+)eI_1eIaf6@Ogm!sFA7s3{;ad*zM&ehMqKKHues=VAn*UyiYz-M)Hwca=Wy;j-2c7T>Zk~<2-O;5os6yihxh{+h-InnLA@+hKtR0IiAaajzyjHrMb zjVDE&iKSB$vZyHdgzLUoR7y+nSTu=(1Tv|hV9YqZM&!vLd_iLDyNB5x1xDpP$RO(U zaSPNEyU4yB&XMbkZ{9QKd9Qcr*y6F}sTJyXH6Pb}LjBqEC(rGHzYcvmw0i!67Pz>^ zyl8SRbT1uQJhH}Y-$aa>e_jjhS!4EoXAA=eb3Qwl)tGHXroF(l=STj=>@$Ld@*`je z34eeoKQ2fp1^GXLV>d(3xMoneuvs|pz2SmNfkK?BU0li>j>mR5bk=1#oic~x2xll{ z9Ps>u{f21@b;yUr(vy-Z3cPVoMc=L0Vvt2U$4^9MMU)}NJPm|0pm!Mr1MBT8aOLH6 z5C$nIrZ&t1=LV!-gw9M;8KjqKOQTV*KsHO<3x_`BM>~t$)|uB2lCcM4tdc>E^;iswIOE97up?Dh_@9obd9T@ zzdCod$h8%?wtV}I(Djh^WY;aYu|2cOJcErJ?w=#*bt4Q}__Q2>aqx(RCNAS(1Gx^} z&GSM!hMzikC7D(gp2zP9#$^LV`??puIiy5*+!KuX4wyDT_qyr*nEJJYE|F06-!&wC9Dg)c6KuvI4GxmzXF;9rhaKn?ug1g) z^e!jAZ1=@esYx|@3Bev>LCdbQs3r?hHEN8*RrGqP*BTW3lG!<_#*;dY&y&ukqT?dI z&dVo2w!8-^poY)(9n@Vz&y5V7AJN&8DtPw6B6JGLFrSAEh4c$ZFa8P{ohaj@P{8z% zdB%9g{h@pXnrL~IPa%8YBuR3kogizzAh<6G|5rrA7w8A|T3l(q*U+*wv^ca*?O6>z zyWw;+I#!9g2QH#iKJ}Hw(QhS=KMjDW(*b!X`FO+f}!@D#2yf(Ncv~fhH|t>E3_%n zdyh_HLoPhPK;>;kEVP4MbVHr(5EnkIJFPp-JN3|uU3Ig@?$E8f^ybAm6uE5QBSlK} zl#C$W`~JO;kKg-!-}^ppZIuu_$3Bi2!$5sOnc#`mh`nC{QAQfl7!zgunaMC7AIY%Z z%++Q-!|Lo*OW>kDHOG1X%1dUr_GEqGyB2CozB_{lWph;R3 zbfO;9;##ahBNfqICKJDjKL{~T#l%wlZ`d}0S`3&i9HvFUovIngv#Yu0lf+O0#k@=M0_!W3TTS1 z+4mIK4aOH0kP-ytNw!>rEf+Wfzwv;s&jr50Hh>ECo2c6aR3WM{S|m6+*oP2k=8(*$ zU;6Stz{{e`li3*^yMm$^mf33Wy5T3n&X~2A|i8)6nm^zjP zh^B5%x-qO}O_dNgiZy2rJcmBKb)GRSRy-p`A$0Lk|5<#DQ@8BAo+2vAt5)hoY)|P~ zhoo-U6SiYyN$Q$yg#XazhvLQHtp>d^$zV}xc%Jf)a-v4OK^f%#9SS_-^S!bMZ{Zt5@ z`ikZr0tiht0>8E>$iD>+)`ozodwdt_257wcRhLq#rfE`zJPr3Q`gII)mP_Mquzt*( zdCVM@V?G>&3ByuJ)-c@IE&Z-(OwPGc4N_R!j~{$myJ6xf0e*$mQF{yzWM( z?|9w_1)K*c+(m6;j;%QQU1z?t;f!mtQ(!=T2*V<(#uN7jmj_qbPvVEF$pg!y|4JU( zNgm%$9$y=MFn)i$lI-6}p5IQMuOy!@j#fJll>1BlTOG&NI@Sgr4Ba1E`_{Vp0r!W* z?-N_87dDyA6PpLN5@TD!nD@V5D>5{$UXghS1e`NFRVx<2hEI4BvHznNKp&zYT#mx^ zBIuixs8BBzqy|h$)^R7qMKc5xq-KRF@D^ZeL5cu=X{jv43PM3FNE@|s1sGt{_!-!! zi{z*)=5)s|Hm($qXD|3Paj;?Ka4)qLXRQz#x<#QO$6YpS65Ii5+(~U%$DYRDpllyy zTkGIZ9J^T^B5|B+Z3Wz1wfzTsfxENmUY`C341(J$lAh;}jf{+qz^%iYy5(09 z{tjI9x*G-9%w`>zqxFB^smoEnesGh9sk=hf&d*?-5DzqNOIEdP%gCxGEhmggC}Mqd z_?1Ke7%6uQtbhHws0lBDH5rEiZi|-03Rf0O;?L7XrYf}Dla{5oFO)BqF0S5L=YH4r ze%puKpQJxZo1-6H|MT^&mtUzQU;R}0e$8@apgdR_{EN`{zsDX{j!F2=lPZVC1R?$r zKxBbmh%V3?^0XQugxSg$qBLV0vK4~O(O}DwFe_28S z!wVI8WLtQ)*t>Fhb*P%?-btL=PMoSF(CQP zijudrS(DN&it>|L)vRe^ilW(BuWeF@X*)zwyp{qVfnB+Aug%Hp(3xoXG)V9O9R{hc zwble>b<F&g{0K zXvgQ*Z$4z>{1~&x0=FCS$b&3@cvk}cVT27-k37=DmgiiSyb6nHS#w*VSL)aAX7w4` zA-gQFzRbwyXB~9{vPPsPSW_L<73)&om+&{J!t?t+^1neZ;m5)yKYZW8G(+e=k}ELm zvJAuQoj^?cXQ=fH)b$yC+jpZV!oSh?)4m<1XPfDH6gka|F)PV(Z>hI@q;zBtQ67FX L()({u<$dSBQi{yl literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..473526b8eba7a0d4f5efe7f865c68a54709509b0 GIT binary patch literal 2329 zcmaJ?&2JM&6rb4-d+o$)NTNcZ;VTr_16dU)MJE@eaq2C_91y3z*-Ot%tgk1ga;||5$w3TVUAdwOf>?^LwS}q3^i0ZtxktoI9=7^ zJj^gBXuur71c)Y)l@KnrJ^*duEi-TWHEME;*DTMRWPXL(0XMJuQ-0vu-2Bq_X8iM? zA3w*rJLA=;7nozWsCUb(x>IJo5#02>lVcyAFu5Dh<8{lPwPq+c9gmyw*4#W-x?y&~ z8>=^%2D{G>_SiZXIsguS2*jHO^js?}XIt9tN4wUJ{Hhtx26wHFuZ*vaZVVn-QtoSQ zh(dM>5e&qg`rvDqK|lf$AQ7r0po2MWmEPnfR!B+bU8HeHunD;3;->r~{uTo-q*9o( zZ_atM+?}V86&WrYs?4zhE6kHAi__Dm-04PyxuGy|q%h+%cgFR?Jfl@BaBsoPP@28X z#VJG{SW!e}1D^!+PB1^vgH2*P3<3ND5P(c^;6Y=#u~{5#6-U?3|8)6>%dO(X&&t!{ z#4i$PPqhgqo|g@;K5YV>h@eeSR#8)~r1+#UsPjH5y|^asj_+ z<-pr>sF4e0yULlUMyvoLlmma3y(fmd#IVo>ngo6*!^|ilJO^HZc+_IpbJNVP#lR2s z0H0cq!W8}qhy}Ezq2j=%ezK*X+|WPT)X%o`vm5&OlKfmB+SK>8^nH)`hCaF^|6Uw; z`0=BQYZDvAV^6hXaXwPqU@d`gln=2m*}8MYsm?8$tlZZS?-oP1H} zxL{asf_Frz6c;0L?EKz$^W5V+A(jw(ET9)6tA|?pp$+};_Bn-WaNUqg*GnKu7vpU7 zi3;smH3}6vPe{#o=Bh&PC*>%C%Rr zsv2z>23wNQbgYg}aN^7*Hk!K^YVpxD$H%9N+v>v#=#|BfITdU0l))Wvf zCqm+C7)8Uoq#lrsg}F3{4T*{BRZ%me^&g{nFHr{HYG-4}#@VMZ6j$UEAhu+Tar-dB z`b#wQhxqLP3Fns7dn4bBY~tN5y!*AZ2OAFuSBF=ISNE^%ZzCAOO{L+#g(KSJANBA9 ADgXcg literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..659a2d200e3d1ba3fd14b10a4dead929be563b3c GIT binary patch literal 3543 zcma)8U2Gf25#GHY{)wU_OBQX1mg&e1O4kzOx^db-RsAD7i47}3oE9*V^mOK(q@&3@ zcK2lcgi6&wKqMf6Wb{W4P#_9WAU2GmDO#Wb`qtOHSdj}0S3kJOOZ!sDrh%k->df99 zMXOHHl{mXQJG-+t^UchD*4mmN&<=hkSrkTJ;3RC3U{L=x3|5Fi3|b}yzo!LB%;lh0 zf?h31rc_FXM{M^`;z{I1Vo3AEkYP`1TQ(e(+QZRye@phqw*ponK23AVKXEo(40xgo zB!Fa@S#+4;MV+c^IW{jUO&7v3-bvi&mvn`=jm1T^ zM&4HmsnMErOY+$itUXU=l^Q9*%Fb4kYf30tBU<>aWLnC}Gyi(owp>duTQg=>x3l76 zhVJV9i9|MNhnZ0<>zp4xf)XPmmhFy=WG$X`$IUE5VX>TbMg`|J9h`Y1yM71m=M@BAByCxqQY22nro2-LY z?u1|600K5{tksAcSeB4dvtNSEr4a4G9J|oMyv2=&Y=hnk&U}Z~2>4$bo;l#(6fI&q zF5K3zdB2IoU&yIm!s3?AUEMC4o(wCVg6m%76@e5l@gu!FVV+^kVL9qWf-gr!*{v)2 zai?PDxy~!Poqv@%C9~-A{AFCpovq?22i-`Tnb>eKlXUF6XP$ z?zm$g>p%MKJhxo4uc{ZX=wl|&gZ4Z#(EdDsa`;jnP|{zW_ToC%eC;y_kfmuBfJxK( z2g*(n+&$Re5O@}D$G-t&j@;|qe|_{f#gE578voe&$hp&b{9bqV_Q6x@-KTH2pWf)) zyKte71{}^Qp2{bxCi7z9$zEK8BZYq%B0U%J6mFJBy=Z_@FXG=*tg1dq zn*jA3!wkZ90hviQU%eMNybs7%bL3GsiKiB$Yq34o)2q+iiRB(Da(v%rg6zqxynXHM z)v+7X>*?<;DDNj3=AL46=wYJ7K&yK|RtN+p^gG)SdGifIX4RTnLfIWHOdqq68huh& zyU}n%DHe{ldxOHMB3*CuaOAdz^I9YXXCqApV5N{;o}_Vm2PNr+OE2Z3 z!r)%ib+k!~nVvey06|`ayKJK9dJ00xQyqkmCznkdHgv&7JZ01>n_jHwRI1G6fN_Ue zW0vhDi@NRD7N7`{geL>aU^kx3@vDQxB%BD~2$Tk%3}(-R9KQf$j%;+MR|c*PJS1|w zXW{flS9<0Awevr}uyAH0*}imc@!T)2uDo^at<~pl^!_e)Gxx`fpYHxcVLg4~PV!_R zusreCCwWr; zv+NKY%JFj9tgz`;d|=G9&8aFIoaw>v2*kGoyRklqTf2e$14HKX^x%!&>+**!AGCbf zx|SaNH%IflvoAwIlTval+Yehsc%MBDi(Z16W!<%|qNAlNlVS%t*bmWffyhit(;dDK z4E_`l@N3(yrK!a!(DO;xpEAGC+({3vw+-Ho4gPmLV&sc>90XgfkTHxp`G48XpjU<< zLvWC4_BI@Y&5%NGy#Z#Mjm$w8)Z&KqzbL7 zGY&aEqF1Y?ZRFyfTr6|88+RbCyofI#z~ZO%9%L&Hq!Ayvcse3RG(Rb{?>v8|3o*2l zdjfXy4}i>(`@6apq8q8MyQ!YFRL^=UyL$BV)S->c(|0p{Yni?q=6dEkix(DtxIDDc zk-po}yVlXWI<(%Adq8OGS$eN~@5=bK@w?r}*Se2?GV$r?diR-S`AZqs?k78!h8Ksg zAG?!0uo)+9yPH(3rw%Uw5}Hl%i^1U53#n zjy89ZNa}$ksTt&@9^puQv^X}GU>P{ni{aA^-)92kUaOc+6;1!GCZ<-UGU4i%q0q%~ zRXmx!_%>JdIjVR$i^FdcUlCe;D>mTUagbp-LvQiZKsIGcY5fqPsV_*|-$$$tPfzZRVU literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..edba0886a10635d652ab9172330112d1cac0d4eb GIT binary patch literal 654 zcmZutL2DC16n-(m+ld7~o4O(yx?|t9*zBljjE;}6;@IAbw(GLgUyCuultU>t+ z!378qBnF>UnfgSM@zSyE!2Car!V3`e9E5{C?SITQ99KKs)W>T#xn*B)!zwQl_^q7u z8b2)?gtb;BQj_vFf(ww<);<-)cXHZu3g_VU;hBr!K`KL?ib1bYv^iHnA~`Qy&J!)B zvBhhgpGezw6t;b7FD=apsVYj5DiMk0hMoni2&oY*Vj!T>0dBD_XDhwr?i|ie)nltv zi83*kzkv8Doiar_HkjCgj;Ry2;|MC@;l1zs-uL{T-c_r$4%F*hp=9m=@I#a3TeYD6 ztiUCpfWj1n&}0-s28T<>vP0$nX$U+66rBU=sGjCOW($XAhnxCv<0jYatJ=`yMFOE) zLcj4-vtDd9lb|K_Z3Qj?Gh2s zYMqdb(@CoF1|jbzQEDqLA(Y32d;ztdbj|TG&lnbw$f6v-D)^AaQs6`0=Q4=}e!=qr zf7;#M6CxSp8OtROPFQ||)1;5br99^Oqu{|qEE36f$5A|r225a@3tUiHjuqbPy~TFd zU|bdrHPwVW3WyaI&cXLayU>R;y5Fo?sy|4wY$Bt6$|^_EYEx_W&p@8aek546_SfXv zTIDW_?$Mj0u5v9#uIc~HkXs5}HyR1FV7g+OgZ8U<9T7SeXJyGb^zz3$8= z#L)&)DH66+~v;JN!_h5aSj767_ z&aNPx1E0j}_LQ4V{av=f1+tA$l@m*h#Q#CLF)ft&OK69hUjcg?{@d{XImhZuj?-D4 zn`U!-8BH-dzar)YJ*EpQOfF(XV@Qwdkrih51ied-f-Yu6^@J{VXyRqGz~s8FASvmw z6XT1qiSa>fXg1b8K0j$Mc$~A~XhO$t@j%l*Yu<$E2WAa9ZDmDp(H*dL8JFE)12KbK zG~4MPTD#l^6Ob-aS~uOxmhZ5o=li3C?lWVO;0a`|XkbrN6tiGDiUO2Ml!BT!Fg+L3 z;7bvYCx$iUMX|0~Dj{A3>rM&$+9qC1QGAu65b82W$MUOLdB)BgGEqrhEy&~8o;5Uw z$P@OY?U)*oFWZG_`@+Y^&l6%!7xG5Ikw@l@!n|ymlX9`-%-DqwM&3Um6Vow{7gg=L zI&BbHFAzES0V1Ct`%<15A1RhFO}fP{pj%fN1OYh#)5~S_R7_T)n_{-MzdrO>l(ze_ zYnSg{t{-Xiy;tVHjcsT4ubsJjrk-hJj+Xg5ViUZO)&>?BdebiWnm@y|MxloF%`w~Q z5h^>wxx|BCI|RGl|LP|=(U!H_4(B~6#16U83iCbm69*~Dj=f?ACEkyb*Q3qYHZc@z zSgK>rn@-7(tj7@tOA$>%!@9#O|u9Am**j05Z; zeH0Yr5=_fzJDsVPDy6OTvCZ_c^^*@q8|kxUemm7$ovF-hrG__C!|VGVywgaXE^|+N z`&aevq}2np^FM0!!}n%txku^YN8&KI%Zp5usc>aoTYF(vV^vr&$CiL5^vYfnU>*+nw>9VgAgBFFQKUgyI^XnWiA?o-pmIx z08#{~{e6Ni5Hhp@_+0?iuMtX?WXgZm{ z1x@Cv#Eo4YgK6xdUnf$6J|^@5fyX4SD0y3-vnZWVl&|MhE7U|4MYlB&!^1%=+aZeL zcW^uazv6ZIZ%N*O2SCSzAi+a)lBg~jzBHO)S&AYtI1N%lTfjL#4=@GMpR!c(8B8x% z&~JPOs;Dt9VTfAWIogrR1f6QrFuiu64D{K|7q5acEY%nnPb6f2wphe*$JmJ1ax zDCOsbzdJO6B)mi;#)4)P9n&s&9I%1R%;)DEb&@79HKBL*VwA$V>Zo39S1$w-VS+0B zeea{-4BBB|iKhen&f-yE=_ScIn07dZVVXw})BgnZJwfRwsCy@ZSmE};Zw_uT*-a+< ef<45@yZj literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..1900d24c34bc4473b001f473514d8122084bcad8 GIT binary patch literal 6616 zcmbVQT~Hg>72eg)YW0sm<{yl0NG;n)wZS-b90LwMQ@T6m=ts=Awtk4hHU2F?4 z4oy81F>V4(rUg&hG0Ai$c-+=J^uc|qA3Kv5RJkL%<{@qR;yzL1hfJP&&RwmvV0D`I zg6_{b=l<-y=X~ef`-R)>pdju0!X#X%p{TF0p(mzT*=A^pTB10LqZ8DyR?@>XX`6;k zq%;qkNog6jl9DNwwqnT^OZ%|ZM7>6FrkfOJ=B;tRK3Tbp*Ou#g?Veey4ZAqYO={T9 zSt0j8|5jb1TvnoyIT@JK27TJfvU)APU$2!*ea?GD(r|T#4zN$K6^@m~C<=Cmr0psw zmMEUWy@7q9hs_yNz^ppXM7beh?2?e=r%z(uCZyU1!b5FCgRixv#kRNJ!ivaC=_xVB zLnW3Lxwcp|l}gKPBYfLrRFrrw7!FueHWEoilYAtiIwFx|nwv^s-4%(vI~7e7IkrfI zOUELSyVN!=>zia~Je}l2QdCMtQ=vD+bexaLQs}MpNLm(RQs_cDHI^PY)7dXc!dNQF zr{qxZ3ZJ?XN(dvN$&5UnPIU#39t%l=%y&*kV-wLaUJ7w3DI{`oW)kZC;maZN5y8of zXn{FNhDRe=A-hSbmH`p8HegT1U!Vk}O`yHZVa*3x<_lk9no@j)wO$rq+Y5{iC zlYTm)Q^vVSD)$|tDvoHHH~}LvvaaFO`?Rq)XXY%8rTJ%e@+_Ryz%%XWXXO|J&%A@j zaI}GE*}4^t2(S$WTttYq*Pgu-ckOdgkoZwTcO^`TYOpsNxkdn#Cscb9?94;!Vj0}1_mJHZb z%hmCytXeWWP_4;yN*-6uTr{Iv#zCD`CMl$*WL{+?(CQQ?*35LLQkynMw)>}Z_)a_HhJiY0yUg*A+S!e4F%uP={ z5}cl_ywJZ8&1P<$o*me9c^51TZ!WyMV80cfeRb2}&T07-Z z?5(YgW0nvk%Tb?0@*~pZ2XcozFWjtXnP+ zMc<+0xV=QjFF|F=ppkJrf?bKD%NiTU)7h2Ex_z(M8_v}fo_2Ly?nriU9p^eObPNIL zSa2UzR+7b0Qe(!FSO#dZ9!8YVw%Up(P}+7pgW_?B?I7@7lx&6!PQdA%e|hfZ<<{T$ z?>F6TTDz1FoXk64TDQEUeJ===l5VBjqd@v#7b1=U_J2kXTe8$B&6#haUqD1>I6GiE zJ#6DyJHF!mr4eBr;Qn_f#cGf(!41gP?Usb^ zVhpmSwSa6>1zDRx7Qu+5{~*g6WJ})zvitOY0k1Z^-Di^L~JRh!7g5>ZJ~8O@WEm6BzUxyh8}X~@uIKr#x98X>(S z5y=gs7EzrPvjkbhE+9{Pw8%i;oB^OozlZGWS?XCcW%tb4a%}x_-O3A(*uX|}TcJ6a zYYwg|`Q}qu%Okd7v!VIU-rIW%4aaf~#|jN6at$XQ*z*l%vX-BDHfkG}Qj4jTxAV0} zpzVb2ch28FzuKH{?jfBw8e8v#ZiiM6+RN6nDAKE@}cK>Y)2d=1`i<$ARVX=l)KXqB_0JDU@xiy z6unRwA($cfBo)CcsI`8CLTDVb)P}dF;BCu!+t%4OZ4D(*06r=t`qJ%HSd9^KD*2a+ za6`pQkr0eiOuEW{0&zclR|zU~K1CHdC17}tQ-Xn=9NB2b7&Pxn#+i4>R__vorcoAF zS>ZlWTxEq@MZDqp@r>|{Jq_L%k9b##qj%huU=NcMBl;8SS3csizoFX# zRpN2zuG&jzq1wO+%ltLDqA|9Ea3zaWeX;R$TH+%jp8)4|MUXS9m5d3HF%p&do^D|@ zqglU&sGrJceouAiU8Z@}G;viz7luZxneaJiO}jM{E-vgHAW3VG%~JpHR?pAO%@n-t zIdA*Qr9wwfuA?XKJ+bb2@c|7=JAY&D#=56h~y_olQuX~QKy{QqxpN8|^u60lMT0ah=%qiK_GO1plyI$}f$$5{gzP&b_ z_x7!O`XBlr6r9h@WeVN{IWit}&w0PMHUinY=j4`~ai7{Uvv!AOIT(TJ32}>9?`epo z{x8ch$Bp9^$JHYjT{mZJuSQaYr9dg-q ze!K5;{?9#J|7DGC;Fp|od5zl5M!%WQctEP;j>}T|msAQE__7#qj~3YK|o&u@C3#$AQgqdO|#p%?sFcmkl2R|B7&P7=EhzWg1@A2Fp|et@O0%oU3t&(g6CAub1LsSoi%Uz8w>vSoWFf#DDMws?SJ)F zZ@6kUT#(mqxc2CkT2j~Od$#LbRgNvnr9+OtTBKfsgkoMju!!WR{|GP z!C_Z+(m<`?pn+P!IRmv6jhCkcY9%LK3Et9l-buu?R2RJ4rBebtlnCHZnQ=bKft})MX=Xy~0|NBo zfTi%b*!} zBg<_0DZ6{#G3Qw5dBiquu&#OMoU_0-<=7^8#Ash-)?AO+mywP#-AktzPydD8uenO2 z-r{)>y?q}l5a^oYCCvpwF>$nh0-zPuyu;OK)8bFXCuc@MjgZMyEzR^gi(K?uE z=Tt?&3r{|x^_#9WfSQ5+3+O6+AF^5MOJ7~Vw?F6Gzj89~3j(mfIqqHDyHcC?9fTI* z$-$ieVBUX-pvl6C<+JPF-j%l@Tg7bc@}IrE>ufJM19;lG6flP`g&_-{)vgh45w}f5 zof0wn5OLjj9g`AiS&Br+y-OTG4u)^4lU&o1JiO#`A|kUmhFJo6?&2jb#wQYy2wY9h z0F_)#`ZOee0Yx$EMn)L2uWwR+v(!GdIDE|P;IsNVYuD3bv~}M$W4AiCYAMF|<;9_= z<_4={+YF5@6V`g%)&pCu(0ppLSo>(gc}A+_E6D_b8u14>83yWN1hajRsb07<2MzaT zEns#dmA|IH&yc2&Oiszs5wJ-+a<%Z6aLUoOz1+tIPw=nmBWTao=zqz}7rg!GgNb;m z@VbQ8d9qFsUtqOmqxb)YJ*7RwpB6LFM5~alLAGV4X?nYzqCJl(?_D z#FT-SKNO#?xS$8(o?M*0H zPaf-65I;&!IThBMp1f67FFiRk*`NjI@P2>Zd-EReqSILg`rW@k;co|k??xNW{y`jYg0KZkh zLF0!}!=Uyoz*uV*x_Gc7`x3<&uQD)aw!gpXvxv7#!W0zb=S#DiSnk(|gp3=}?;Vk!^zxA&9^ zry}7Z$KEj)$2blrI4$xcDIR${53ve!zMazG6`gX0nNT=md68hHYd?Sv{`x_@h0b*gOEx!!XT z$Ry3UQk)&gHzJ@a6w|{Oiw#^e&d$80*>(Hz;#5zvu9oKNYF~#d2SQjq0C3|g*t}{2 twDNxI-PR@S&0+5sf^hw;`?3F_f9dw;u8zBNceeuCt{i|if0_b2;}286oB#j- literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..1766731fbd83a106fa21720ad38afa75938b70dc GIT binary patch literal 669 zcmZutF>BjE6uvt-wvt9rXiHp5ARZlvj;3oV*ffMfZA#pw%5a=@ha*?Ib9Hh+-3o!W zQ^({ll>C%To(w8Xa#WlH4TF8{YfA@4a{5*Dxpl7A1ZDsXA?@e!~^Vuo$M$CN~vwpjl*@OrhaYC{c zLL$kgp~0(!e45g*sx$~;(kJ96$c@jnzy~toSWy*Gf#1b)%>6{+Jvo#~;49pdVkBR8 zw>nA%BN1_tVE2%VLmUP}JV}!SDPFkGx3LNmzBQr#M>^sPGof(Il60bRr~d($tGbgk zu4&UEG&E34Ivj!X+FER`SmX-wrxm-B-FU8;io{fkxN%%ko68;zydAtE;XKR~CdYuLJnx7kGA62WaW*`j_=N oY|dcwFM_c7ZSZ6N`~KW%&79WU*_=6>InZ)$0rd2bX(&8y09NCgcmMzZ literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SOA.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c8c24f374c2295bbccf64bec705734e04fba3a37 GIT binary patch literal 3826 zcmbVPOKcm*8J>M|xm-%LO+6wD#ts}CsZh#|9V2FvZt2~nv=qrz zXIHW%tP2$o5D92%5k^4)NdP97I1mabrzof7s33<(VG6069#Z7gn`-(H_|$)Pmybv! z)OH}v%>SC5$2Z^n{5%>J5tM%#x7eG(lsRs1&b#B)0IplKrLOQRz9QGRC(y@`wxgo4)ux?F( zUbpeQ%jJ}?+j-CvzRWlI3N?Eh-%yip z_y}L2$+y0DZN?2Z`PSDnk3OK1aD<#SJELP)P!u=`TT!5wwiI%BnCvMg3v@-In>7md z=vf>hiV#uQb`Fy@VKP}vW`W7@a>QjVJ(qz)*VJ6vvRoc(_B?#0KA<;9t5`RzC|-=B zSY)iz^|JVQIx%JD^@NqS^65h2DmJsaYFmky%v+{ys8-_FW?|C2^!)He%Q7YldA(pK zM&|UwTq0-OO3cjLQ)Xdwtzaem=~;=3$(soP^T^CRCTF=I z5uvs*KLm}n1l4zo=n;>?9V?+7p?9sP{Nt|#d9Uk<((A?7%gS!9Q2bmB%XGKRxXKrc{M_Ahix^ z5Tfu;cC1PR=t~5SARH{&=7_-9e3kC95LbtPBl^fV&SBpIs&qK7M%ntcXp9k9>X+b< zr4HjmmKM|+jMjoc-$L(1ft={%W9qalc$jmKsZ*wD=?d0!Y1^1H?0FB@ZDA<$_yo+t zIx!pOnEllBF__RvyZLJWXcnsU&gN4Zk zOj&avt;0GT@K*qtyCLVl@e;tR1<2Nef$xUF-h!*^cJT;%dT&UA8t zA9#@zIpVVz;g;KwHTiEcZ`pWOwOy7xWo=>Qa4%9dH?*o=QrXX^?lg4dHs`DKYn%V%ojtS4FTA? z#O3flyt`5Ifz_Y?P3S+2JF>$b?i}dABiM$qYWWbH#8Do6oiBh3Ct2`s!EPE=8}U8r zul%18u!sy3kZC*sjZYx{X~`jc22@SF!Ox^IB*{K?)Dt9&AU9wGp9W&yy(h=7_z?^7 zIvX@;e+6;Yb5Jd!Z(`l0&SGbouf(1vbkHopm0mr!b{X#Tv)i2$8)Lhpmv%-kefmme zH2G!c#I`Wu?JYc_*JUPoJqcCvvd@eXNfk+Y2&tl|D0x$x&5^#OD8HXg=K@PeQ8ZHp zHRN+v&a^E>@lq=M1DKi{@lw3J4k?+2p9T#+OR6zqiv|gxs^@ZwB2%~LbLhBK&r5%I^qnYi|lkvOr+fUr-K-jh6c zYCi_zLz>~PQl90J*Y%d|jV_CL5XN?+kRXoecT|1GHq3&{favRte16tW-y+wExOhKz z!lZ|_w4HXt4Lj#+xR;2*8~g+0e~6?EkA?I8CjJs%0gN}`I03=5*4__t^BzVjbqO#~wV literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SPF.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a6898648a5267b42e3df474b2dbd6af1479c9bb GIT binary patch literal 657 zcmZutF>4e-6rP#AWG`zDgd`G$14UCDAvQ9kROkblZTpX(P!(&jH43A{a3B=rnW8z@#R%|j?=6Kr{ZWxO zV4@9o0H_5Br|4T_H8;nzdZ$^opx2MnbgaXEB1#8nxni{XKVQGo{ZNT=>95JHYo$9c zy8CbTyWpCQTr&ULA-4csH=oXqvy-m?HJ4Uh2v-iqxO#-}+E;Y%Y7vozv-bP;6t7S4 l`Y(d<+WEten;$l(UVGxTr{2cI+o%x8m4nFLpT=Nk`~kRPmaG5( literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/SSHFP.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1e2bf3b86de9c2dee2c434b70101ab8f71f0205 GIT binary patch literal 3159 zcmai0O>7&-6`uVgcll$9{#i+p<=Re~h;by=iPHqNW5=pOM}SQSk&7U_Y*)J@YH4zp zon6_cKm{rwAQbxJ2th#yX%V>y5E<%0?WH;Po{I=3AiMD)MNYmcmQe&g_02AKDYy(W zz|Op%nR&DGzW3gIkxa%Av=d*j#(yP*zNJjiB3)wd&p~Jz{*}q|bu`cD+^U!tbW!J5nS2CCC8SHbu*!rML_MZQKquj- z9@nG$G_g9m#pL6&D4lSH+1WR#R?{_s3?YMIEvy*ifSv1 z@SCp^dQL)@_`Yyn=EIneK4-JPK`dWf85 z-U)G24E-2xg^FU24k*Bcmn#C^A0d928gDQ)?o;NE%(4Kp`b7W75aXcf6XTvn_!#{~ zfGFZ~uB0t2n%8Y(2|ri?3y+!OY0>31t5S8jidAtraEvPuU3FA9sx07J#(dR-M-MIO z2%(1D2*h2%Fx?ozCDk!*z@9F9{U$jAN^glx|J;WWIY?JT9ZPZWEoUj|`?t%EgU&8N zw}Lv!f!pt_z4Nul$++{mtH@0{J_T8Ce#OZVlGhOut6<>#96bDhLsJ8`s`IQkvO z4#hg-r+2;aR2QqCbKXu|G-z6!pt1fo@rH!gKXSiWKCOWr>BnLvvOwS`%y1Ru^@% z=pK_x3$9eaj-Mag6Pjges)J4V6oShtq!6>97&-*3F>hc3c>$Z6rDIo|GfdUi48xTc zO@KfNFp!S9oK~{Q0Nf3sb;IQx>pB^u>@mtFdniyGtA`dJ2^85r5@_$EL7FF4*~i0x zkB3n3Gnj5)gKh=wis;C2yOGxcak9%;*GoATL~{90R{Zpzaw zIa`n4myfmO@uob!Iogt6uE+m9aPWgy?qoM+HgA3&Zw*ZEh||93!}tU%l=eLT4C#w1YgI8fuKLkKXy!#`I^oySeS@e_sCkV)mDj#jj&o|}gTk;D% z#Ai28-<{dKv8~>#etxPoklhip-rW+4!Zeq=mV+*rCA7H0`wAWvrdr1EsE8>_+0qwF zlujthZx+>3R})nf-O{{whhmp3$5s@tUX$anha2}^4`mD$Uni$PLe9{Rp4m-y-xeA! zl@uk-kQ7L5datj0$$-#+^17#};xcsKucH6(@3@HVZI-l6Xy-vX_2kHvHT^_?d(z|w!Tv(W8w48!a_ zgP6guQSw`K_-h2jVJK9r@c!6u$J)$rlNo-*jx(2-`Y&(Ktj*lMx^{IBQ67Gh9r?~v Hd3OE>oV%jp literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TKEY.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8d864f9e8b6115ddb814222bbfb13d16dd2dca1 GIT binary patch literal 5103 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 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..acc497853f9e240d13539692326eb421cb2d28b8 GIT binary patch literal 663 zcmZutF>ljA6uvvViCZ@k5)>7os<1UfeS(ffl@cMbv=IUpyqqq+Q|e&*g3qaBHl#|0 ziLv|z)SuERQ>0^qiLJ;?op|RsKqYQ?@B6;@-hEH+veQ{Z@E%^0*g*bn$?~-tP<;k) zjsy}oK_RX)39-cEWyf*?^8Yk~o*_X_k#Jzf`=_~q<9dgi`gr{&x9q}MXctW>Y?rv} z{;+HiHCml;L#kT<&XKIQ4vD~_QVj#ai7JbFxZT*%!CkKXW+)Kp}e=AtMkfO~`2)GiuK%G?FZ zI>JWwfEM22v-iqxN0H1@fF>_Y9X?6w)K8%jyGp`^B2MR h?uWfk`ycn`{`Sn@p8Gp9f2TqqR}Lcge%glG<2UDPm|g$? literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TSIG.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26a0f20ea5e7acf8f8fea5761bb96f28fefafd2a GIT binary patch literal 5923 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/TXT.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75ae3476284f90b3b6a9361d2fe5f9eef9572b9a GIT binary patch literal 657 zcmZutL2J}N6n-<=Hrv<*p;oG}plA!2Car!c!3R6oi8_?SGmZIJP@n)yL~sxnW;$Lz@=~0=Fc+ z)=$F*QKQ*J8d6_Ja0b$D9Z*36r=&fnat>Y|yzwyH$z`N+G3d3bHs@KGO3o{f^Hhmx zV(>cWucu+sR9w!5ia7rawQ;ySVuvb~OouuRGxj`JV;L2iy--6{#F1vtR5nsi`n&sD z$D=HjS;72cnH{qv9(0IHnYUd7%C!UZ)-TgwJ^ldR$Cldw literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..afe329049a14e59701a4a173adbd91ed46cc71bc GIT binary patch literal 4182 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 Z58RYiq?_rL^ftjYD8aYqE1P6r^&gLAaqs{D literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/WALLET.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/ANY/__pycache__/WALLET.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae6180ce67647423ecfb79aac2fcf2cb47f0fe65 GIT binary patch literal 666 zcmZutziZn-6uvt-mh(eU2%&Bu;B4^_9c|ZAuxZJXn$Q~BD#Ib`4o9wZ=jzTO>Q)Fe znKGvTLdlBNix$-;04zVIc84!H42a zce^8HIOH+o3cH7lAL1w+;Bl%B1b^v1e}QGF*!I{DzW75XG362`RHb8$JH5}iT-6~dQ@^WS<;X?ze=%fPqnpO7@;(_OF7Jh7e-^`=g!P-8)!_tY0o`hbMHOp z{Lb0C*=!oY+Id&9Zzd6XD1_!p#7yG{VCskmzTx`r;{LfbIP zX9D@NeFvw~K>8T9JVt_~NeH8yTgrw&2=o;9oxGF75ks6~a zDv58NTUD80A~iEnMMg7LG>P)^(D_IIzz2CHQ2BU;vOqHo+qHee2vUa0jgd*8awd+= ztAWC)Ga4iru^f~0Ai;<~3Ev1ScpIVCFu0KWU`;<8(=T{ss&kW_$!MFG6-1g}}#k9uEBIP=9 zxgK53^%Fy%>yXI7$|Os{wNn`(G(*@08r~1&=`{LN%g!d3wVqqu3;p-B{A%}(`jz=B z3&mgjJHsp8M{3HoOanZT)k+Ttq8x4T-}nLufEurtpcJPa46U_k`cbLb8ZAGB7I7qe z4`==;11O}SXLuJ>L5AQ0l@Zf7A*GaCX2l+zj0&JI@s=RrF}rBHL7Gv=^zDoG6b+=Y z2`(Nh($3n2SJL+kpI-8(+C)ZTLF9t(1~QFSx8`o_uOFE|a=){GxwC)4|GD~8b*1yb zQtsfLvrFxVm$bu7bd&{+-dduvAtVn0Q%6;~6vxr>0H)atmy;4eQb4qtY{G9C-Nr+C z9HfiXH@MGix0u%g*>X4&V}NCW1hVgqv28-^7GiciPQ&-YRB#OpB37}YcvGXyD@PG$ zMZ_&J53c+bAk%0~L!H~|gY$zcxm~s7@9nv{!?#W?oL*_)yQJ-nq7@M~m#AA9?XSQ< z`?X?)M%$$N&6-|Tt17``;K8L2(Kon?H^YVB%4jSfCOxJU5 z%X9?lu){@;!YagN(4a)dgw1H&iLvw+xbc%fpb<0eb4ookpIOQD*6?b3$K3vJCu&=2 z6RTS0dU`f}<52zR{Lx$f!r7J1*Y9a>#Ok>N^<(qLe$)D1tkeHPrP@NJx{gY=1KtL> z+$wNsdE>56Ni_Q)9lc=N0MA6lWSp`Hq2lfrlF$)?Ph`P}e5blvkldgZO%x;ZL9XY1 zZqIUV&q{9Z=E#HzabAq;As|DiqW5ORw06xhg=w8OjIu|@9l>V|btu@RokskQ0Tg1A p)T_N;_uj`n%ed!>^fDf(y?Q-Ao4>Bl>J0=UL@I6nTNuJi{snn01FZl6 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..365b2d349afa15c3c3c26fbef772d8382b776585 GIT binary patch literal 4212 zcmai1U2GHC6~5!K$74GtnQZD1Ai-Id-I|{m2+LB15`x%J1el6YR(PdOCo_|H9DByS zGYKSeq!b~oqjnptRpqQuwV_&RLFog1>h>*?RFH%uDu>c`OQk8R?@QBG z>v!6K(=usVrn5WTr5bH3oe!NylJ5?Z$efPGSXat+x>BQErEPiDQKiGPRKov^^irau zvl?d{+WHlAKZ5U&5jlI>CsAp7688NQmG+m=1SQdT*mOW*CI1~N&B(zJl0s784(08@ zN@0lsTSyK{5h>VY3760)-H?8fh_?op_JZQ_c15Imt zXM}I;ZgCE#X{>j47~29+5;gT>gmA>z?XDyF+}XD&nA7Fz8*Pg@-mTuIUh}Mm1 ziB2=1PZwltvOKS-iox?{m=|<@T2YO`W0*kNq$O21Lvxxc8?yyjH`~rx{xW8JlRqy^ z$(T@<`G#%y8Ei4)dKV^MWroO_v6huZLr-4S#x+9`_2gwuoz%{qJv^f8%A}fy`%4bY$m&cor;H~H zvqnZ!2M3NGOX`XtA1(;ulmHx(gqup*r_z%nsT)a~iUta^I0C!RwL5+Ht_j-p|3dZs z0(!zm7lT!H=W_ST+mG4AMr>C_D{Ctgwb2uCRir=n9#BW?x8;h zA$a*yW=PCTt5XE+&lX_imsFepUepS+CSA}9I0{NLfF;2Y%pjkUZz&VA7Sy-FKfz$a zJthOeZBkLqFqU(Iq0A6B@=e{uaWGo*baFAaJK$b2BWP&6A>T6Qb~q??`fq_zpMh!t zZA9CbK3M$Vw>L_@jjmW_v^=^z^nk8)9V&&M07!buJ$3HQD);6_TgTGH#f#t3zRvK* zzC)WfyKe?7uBd`8DvBAJRsldcKu{uL(qc}>J#a?63o4T~v?=@!=|Z4J zX0!nx-q5yT&BhUNU58dlV}mI)fwjC1rw#{Bp5_ot(8r-#K$|S;{86Q^+*jlJOU#B% zG@tARv1iX`y`T2p+gsWJ+B$cl%AKfjC+pmBl^d>cBPHeu2VmP*<@T)r3mpU&YVW-J z-ty?m=!4NmskPBs`^9zkq79zr=Ofo@f#>faKw9TfP1Yd~PnmTCAR$<0o} z7%M@0MzHgYz$W3d{nNSnDDh9v8558@k|_-zV+sO>Y|eJGVPYd6r4uG`9LaW~4$Ec8 z1@rcdx$b6PYBHQ6^GBdsKu@`Dn}k=uVGaonS2&|`|`u-03m%sZti?|VL;ld05j=&XaE6xNiQ=1)#;htU@drq@~OVkz(y-lAb zcF}?OW*GXS&3*!v8HJo~K*{H)4PiVd+i^HVbapJZ%$7)U zQG0YFRSq>K?k9@{u95(5ZSoM=8C$`8;duV_(gsTkv@Ezujf0)TAH^;|)L)+sUt3X}D_}M1^&AJb6AL`(nksB*x zZRF;6b7k(WA^P&v*`!Jh2wM)Gtz{|eA{!3sJZ;iJv){=<)|RCj4aZ=Usc_HWhy-y& zeX;~ZE*wA(A<%PNCf-v`Ij(??L!%gMhmnA4CzedhT>k=W_ds>;#g^9E?xAecbMN#u zwC?38bY?u%rMbtZA)vX(q2mdc0rDqf4O)Tx&;iTmU6*)vq-B7amT&1ONa4 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py new file mode 100644 index 0000000..e3e0752 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/A.py @@ -0,0 +1,60 @@ +# 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.immutable +import dns.rdata +import dns.rdtypes.mxbase + + +@dns.immutable.immutable +class A(dns.rdata.Rdata): + """A record for Chaosnet""" + + # domain: the domain of the address + # address: the 16-bit address + + __slots__ = ["domain", "address"] + + def __init__(self, rdclass, rdtype, domain, address): + super().__init__(rdclass, rdtype) + self.domain = self._as_name(domain) + self.address = self._as_uint16(address) + + def to_text(self, origin=None, relativize=True, **kw): + domain = self.domain.choose_relativity(origin, relativize) + return f"{domain} {self.address:o}" + + @classmethod + def from_text( + cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None + ): + domain = tok.get_name(origin, relativize, relativize_to) + address = tok.get_uint16(base=8) + return cls(rdclass, rdtype, domain, address) + + def _to_wire(self, file, compress=None, origin=None, canonicalize=False): + self.domain.to_wire(file, compress, origin, canonicalize) + pref = struct.pack("!H", self.address) + file.write(pref) + + @classmethod + def from_wire_parser(cls, rdclass, rdtype, parser, origin=None): + domain = parser.get_name(origin) + address = parser.get_uint16() + return cls(rdclass, rdtype, domain, address) diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py new file mode 100644 index 0000000..0760c26 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__init__.py @@ -0,0 +1,22 @@ +# 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 CH rdata type classes.""" + +__all__ = [ + "A", +] diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/CH/__pycache__/A.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb633c9b455ab98a915e1b8342d72abde7982381 GIT binary patch literal 2564 zcmb7F&2JM&6rcU{+KzE-5%Nh1UoCEmmyb}|3RSqEKqZ=1z{!VhH@ll;jqOcmHVFxv zl1h5@c=o;D zdviY)iy&D0@3ZD_5<*X?&{$%?)V~F$jC7`?s+5O2Y~4U}bc1ovdJ zoX+ap2%F_gXqeIYIVmgXVO^MGvZ4_RBR!&vbIjI1=utfcy08(_TXbogCQ?GznQZh5 zO2u3zGu@Wy#fD~Ky?5Bcz2`<%ixdsp6?LninngFH>N++E(S&XH8Y9{bL3>D7W1|uy(i=#%CR*G(Mf$hMVDL6$LnzC`GkkU~20|s5ovG?c?uaiVSe!iIE?6Ls-}HDj}|jb$be8P;<}3 z4J(SLQWQd6dg+>dIVz7@1w$q(DX2yHGi>Dy%_j0?Ysj)qjmV!^#S!cLM@KV+m?Oo4 zQMBdsgi)N3^X8B|Hf4`m#gpmdCuCyU#?diVyQYp9MAnN$_Afx>a~I`IdTa_)JWfY4 z>Bcu50GXTs@?sV}l4A3rHL0W0UVZa{l-k(SRW2+Qsspt>eI@>z=tg^Y`R3Bi>SV25 zF7dZp>fnX6W`;qa6^z2K{yC5`%{lBlS8lsUpxj>98Q$UVKx2V7jkjaz>>d6G4q(O& zzGQk`#!7L1F%sISWf4QcMqahe3DchPYa-wngm6U*nk ze1B${S?zjzHQu)>_2DEqX(1quMa%BNd>~mM54F8XO1W za<+91w^Ol$im|QCDYmr*X+rzT%N`|u2E)VHD)>2!X3|Ze4=l-PAhT#wLdmvr&r(li zxSG3nw3awabJ$i+FQu#MV!D<*QVRVMPcELW3{(e}FVy1wt5Uz8%x0fLpk^|E2I2oA z&f%c{gG4dNZpO{gs5_R2N1DeSt1*pc1c%8{?C)5P&?pdtrcjC6OK8(bS?z^GT#|eM1kfzCEJWv{>r(fc)Ll7WJ+eG-@6rP)^C;f7aAW>PrN6rS zm!6+`YVmZ5r?!##$YTHRQpbOkA`5|EkLyM{AYre>bmOg=5u13XozTQHrbWPS07-@L ze#&nlRvc3?F|ZHX=FBCu`vQRa@ND%ENi(nb9@EJHrQ6Ql11};2nMIEi?d5$-`>K0t zi6dUCuO|<#B@fn;hhGO$DD+hrQCA0n3=VjAPPNdNguWi|l0+1xVCmy|%C{)Wm*Z+a z(1a94w=_70mxAOin<$Dmv~d?)#f^H8N5O!Juj4~-g42{7r@C11v}i^?uP7-7x56o* z!{w~^*_h_gBb`)n3CN2%^e4Y>GlG&`mG@U$56^!5oacmN^$07x105Kq80Eai4_LMM2LiDxVqD4tW!LxdMT6~=U4x-ocw(q^GBZmUCpUJgVzOwX{c+NzuM zS^7Ydbm2N3PHuS1T=1=g%hcG5dl&itL5K89>5QMi4{-)W`jzA?kWG$ZnED~a?0SNB yKSZq$QRk+JSmD;*ulKGq9cxU-bM^pps>CgX=fex}`FI_H2%pDx{zVnuKl}~mZY@Uu literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6af6caa945d3b5a4db75f41dfcc5200784cdbd80 GIT binary patch literal 283 zcmXw!u};G<5Qc3>v=OL`ObkJ=bZAb%ijYbex_}U47t6J+)R5Rw&QuB;Pr<+w^jZ1_ zU3r0^Zk@0t_=jKL-AVV~ZAPO*5_Gm<<|`rOI~M<VC;YqMQp_Kky5PIGZ$U)jC?1+5~+d&xp0s~i3BLDyZ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000..e09d611 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/A.py @@ -0,0 +1,51 @@ +# 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 new file mode 100644 index 0000000..0cd139e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/AAAA.py @@ -0,0 +1,51 @@ +# 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 new file mode 100644 index 0000000..c4ce6e4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/APL.py @@ -0,0 +1,150 @@ +# 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 f"!{self.family}:{self.address}/{self.prefix}" + else: + return f"{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 new file mode 100644 index 0000000..8de8cdf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/DHCID.py @@ -0,0 +1,54 @@ +# 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) # pyright: ignore + + @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 new file mode 100644 index 0000000..15464cb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/HTTPS.py @@ -0,0 +1,9 @@ +# 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 new file mode 100644 index 0000000..aef93ae --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/IPSECKEY.py @@ -0,0 +1,87 @@ +# 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.rdata +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) + key = dns.rdata._base64ify(self.key, **kw) # pyright: ignore + return f"{self.precedence} {self.gateway_type} {self.algorithm} {gateway} {key}" + + @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 new file mode 100644 index 0000000..6073df4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/KX.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..98bbf4a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NAPTR.py @@ -0,0 +1,109 @@ +# 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 ( + f"{self.order} {self.preference} " + f'"{dns.rdata._escapify(self.flags)}" ' + f'"{dns.rdata._escapify(self.service)}" ' + f'"{dns.rdata._escapify(self.regexp)}" ' + f"{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 new file mode 100644 index 0000000..d55edb7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP.py @@ -0,0 +1,60 @@ +# 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 f"0x{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 new file mode 100644 index 0000000..ce1c663 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/NSAP_PTR.py @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 0000000..20143bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/PX.py @@ -0,0 +1,73 @@ +# 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 f"{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 new file mode 100644 index 0000000..044c10e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SRV.py @@ -0,0 +1,75 @@ +# 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 f"{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 new file mode 100644 index 0000000..ff3e932 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/SVCB.py @@ -0,0 +1,9 @@ +# 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 new file mode 100644 index 0000000..cc6c373 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/WKS.py @@ -0,0 +1,100 @@ +# 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 f"{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 new file mode 100644 index 0000000..dcec4dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__init__.py @@ -0,0 +1,35 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..3063cc423564d3e20740ae23e2f5f9072e227142 GIT binary patch literal 2131 zcma)7&2Jk;6rb4-d+peD>d+=_DYPPuYALaSeo!QY2%uIyU{G5L$p=Q;or$~dde_Ws z(k3!ekRmlXR4EcDJ#Z)|sPd=u(kMkKTMk8>xP`ihswdvtT{}^z5Kr>F&)IqJ&F_sL z6pMKRbo_zl9?TN*Cn|l)q(bjU5Y`AMoO+}|W3HuKPq~qDvq1%I75bmp;jgl32&^T@ATx-&X zu}o$t*O#n@$t`Xy(MCpObA;!(xkLxww|Jgsz?TzQKE$)TJo#mElQxDflS(0?(+j!j zV^TPQvSBx&XZ4%O@U2R=U%#?IV3v7F-tFP0< z)+MZ8B$+4oX}w}Zrkd>t8Ci_Ez8f+Y4KrIYw=;K|r8!CbX2pyQCA^s^E4ky@N<|sT z!+F?>-YCadY-LQLehg^w<+a+ipe<_3R&Cp_eIbLEa6(nP6kH8L*HN|40>2r2`tj7X zQf||43qPz?=Y&63^W3Yo&U|<+@K06WdAFwAP)v1f=epe#s>Xd)OO95xi}l)cwKFes zaOOgOy4o+|P0-aiEH4(x?^baoyJ3yp9lba4$f`UZJ+d`AxiLEV%a@y@XO@lIg&xEa zXV4f>uw6s&>m7z=4ciKQFI^@Q@J}i<>5n0CN0bSq+jRXXFNJ4^?wq?IBJKJOg?EWv zktK}Eh5-x3%@80^O~tpsu5b(%$>Y+pcwA_pg+DUOk)76180fFi`EQ@5@A|nU4%Htd5@y+tYa`v}U`ObTH&)>VS zS(8olE(Rq{vPVjN}p4hl;on#$h;u| zijfWwj0_x+DChdnL^}-pbV?Ew7&ERXBFhQd9h~a8iBZ9^{lIq}+XGMx#w2Z<1ubPa zTq$B0r}7l!mNxCNRanih)BYDw%mGl$ ze*>k6K#`(t!wcazD;YTkdF{eUrejMbWC9huEQNmrF(%ULhGQIHc`S)tD5u!iWqTWU z;^Z!pr{%G&^2A1YVzYem)c~nz^(z&Hfoah88bAH#GBX&!Xn*+ZmGC|Mc_A&ve6_>gk|t zYbX_{c_#Kf)3ns_9Hsbr<1bh8Q4!!*6*nEx30=V58*ofwbKC9N(7uYu!;l1^C`oRI zb`k?4EquVA1n3UJ%_P9QgmLjP_epXjzE&@-50X}XDBlMa>s6n?vaM4}d*g&2ctT!# xLP}4_FbI&oX5Kpb?a?hdwn4|9YlrC6vc8&I$*q=FN<9K96s#Tk8y)c>{{VNn(?b9N literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/AAAA.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a215a13f72ee9ec87a770d83b59fbfaa53cf894 GIT binary patch literal 2155 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 aEL~WsELT=8Enn&)P~lz5zJH`6KH*<@XztDc literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/HTTPS.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c0efc9043d3526838181db9191a39dee763db3e GIT binary patch literal 665 zcmZut&ui2`6rP#vkF;h%uxdpL>oJFBuwJF$R!}J}#TE~_3`u6#W=%3{Cc6-CLP0%w ztp9@aPw8n-3(=FFyj9|$=cT4QH zerPs~8qFl!kor1+QzXpRKH=E6O5$2o`{~iq!QmUnq<$_Une(CBs%*xxAQg;N>x`w6 zPZNzD#$Hc@q^Wcn<1%9GGpe=AWkQc-DyRxn8f5e!mtzqXioTGcEaFJf=Q10~$Aj%h zO2wlr6FXhN>poqySh!Wvj0@= z!_U(nr)4kGP$TW_}(cyo?7 ke-Vstzu)_~|6zaO^yf~0;ka|htr5tzg~*+sy1{t-23{SRSO5S3 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/IN/__pycache__/IPSECKEY.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f2087b8a3de632b4b3cc6ff5d77c1eedf76ede4 GIT binary patch literal 4263 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 d2JlexLzchlN6q9|RV;DVPuY^Lf}gcd zQs4|IP?SJ`Otu2V&~(+Yno#+Fng!2*TBks5HRJr#+`y^X;i^8}yvjBEqBb;nnIPzt zXwdqhnIAQpNvI+94F%4CnXLnhqQEY#fn7QN!S0BRUhcnfH5w{(FK3a+sXu5{4k0{D z86njMA*rCVMB`0Dj%H!fR62xE5fSnkYVCC?@S#Wu|y(rd;Bj7GDOVHjC?MBVIE8PmUan7R^np`#)w~y{>8uFH39D>HtD9PD{`9yUhmuw6e|tJj zscJX1>#uku(ky3EGU^{Jeb=>#ak{3^rSo`sr6>6La4qE==N9WU9`bk td$;{|dx5s*XzQ1S(B1d)7j@#~cLNzKNG!)Q4(L+I{K~XjcP`P^1qaXSoW`DE$Wp{U&Ks)v&Z~Oze?@;iYdz~whRRVF;oC58W>G^Khvr}OMc za}LChU>`cka2kQ{mOdnY8R96>^`;bl)J!LpFcy)|cAzM0s=8PSL{{F=2)2bhO+<^dp+(h&5K>o;8i3PpeR-`idSR|y(mHAEH)p4Xu za-iGa%7IPJ)s>0j2L)|I`z>KUp~JQ{*9&aL4%^mu&yxq-R5ECbY__CRTUHdKXjqD3 zhm|Z-W{sjXewO0QFa(NXwxCmVra>U!ConxfZ3n5Q=Ch3164k7Em35=5*C!>Fefg(=t?+{*_srF()sNU1H3bDHimil}^p+ z#kq9exScM|TX)Rj>#6ZGX=YgZSShRC&Ccj7trc0?{S21AI+eb5D^;4OU9jvz7x4ZD ze*y}28ORSyXDwsSu;!g4JGo3s z6l;VnnbepuigwsHTF`C&?!9)Z87}PRmtr-rgO;gS`h5#gmGbx`46t{BERo$VG)gh)-66s7w_~r?j@7Qz&Touw zyti?6J9>6oK1&b7bSfS$zaftiZvGw!1!!x?&Vat}0M_47pc0kQD8U-7Q#nld2=3P2~;vkCM z?U*Z;xesCtE(I^{7=1VPp=oLs+~`Lj`YkC1w(K1sOJq+b{X^CF*WPdRy;ceD#(Jxx zYoqJawcOTNBX$up(TAB>pQ|lwU1-EFRf1ndj`3;Wcfd}2Ml#61S%XOyKb1b(ino~<e~aJ|hr+obNn>Xn{ZWOQ?ksv*@g9*#ed#w`hv@DAXnwUxbZZ zHuAbHt7ZYN8OEG++YvQeG>e9s%^QF(0WYBEa3#YTaHeysnkSrt4`LddPbMD8gUENSbnVC^b$MicvUYq!+KN1qFQ7iM64{YQ z>+7^FI7P0`9<={SK(NXx0q)Zdg zb-+j}Whv9Ci=_X(m>8ZJ#0xeTGd&JUFi(o>w{X~pPKWXW__4QuERk>fMt1s6)cZ~} z`c5K(2C9j*M6JIOJBjES#EMlr(TFGi4{}=QfC-UzkTVR-i>?l@lq5N~ICD;MX6Wke zqE{(VQ-uR%6lZ`npln<73j=un9WXEnjtn6H^*DNX^~049x8+yde0RbXKg;>Fo4|BF zZw>Zu=JJCcvn%IfHQIEdy@MI(U_O|@C6BI!F&17Ke z<`j^rD^5{|mm@r#u#7uTC#7I&vw7qrit?M;Y~C9QDvD;RpoRw#%bONc6z63^G1+#P z^F}J@@UGG5tDvE$kff1}BRLPm?)KhNs-Dj)3LJqLXr0_&bWSi8&=koC4on02VVV3( z=zSuS*u>J+r-C4+nvsBbZZAxv*tfsF@gyLLm$+sCq&?nYdj#>Vy$Hxpc%eOn?2`y5 zCiWy4`kv>+N!RPC!#c5X7EX52E=-46T zsFt;|c8|+C=}$S?54Al+*G9aKrp|H?h`ThtH=fDAaAW@_y$T{if+4dP;5e>%oN$M} zBfXFD7kx~69+Lss3om|j>>xCb`Pt)uEN4)x=7oNl=E^@I&7_L(WqF E1xvfyPXGV_ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..5120af1413511a36b659e6ecec795ab0a2ff0bde GIT binary patch literal 3724 zcmbtXO>7(25q@vEOD;)KG-Xc)S2Do ziec#H&XJ2T(B$B!eC5P^2~V?qB2xX(~%HNVR=KLn;mRHD)xNjW`D3AXHW zd(rJBxA&(6P004PmOAXajS1v+q6$ky^}(DYhqf&)+t=a_`wgGNZ@5FL&^%3u|3PUo z~6?NtM9nvg0lkyKa>0__29Xo+~UA{FwOrh4y^MAY)#z4JyX1mq0UG6qu< zq9qrZZZO@PxBQPZeP-6Q#G=7W%WtOHjAmv6ZQmjSkDt^0JCI+}8ZkZPa-dA!l?W-* zvalgI!<3Xs7REYT*|$NRu`*Fw?s=K5*k+6S+wCRmN^Q2@zF-?^%{%PN{x)0h8|TSW zI+>6yiO&@^X32`87j#omtdNrC%A8&>FJ5N2GQ-?tI1h|KRY>PGCW8o*MXu$htpHOq zxisgNpQ+|NtoG$;wy_md6x&EqI8Hv}w8a|EVVHQB&*|P03TxENvEr>AB%Z|g@??cG|O{JqfEI$BhMlV>QmSst^ga?o8RXFB> z9cOkFZ3CvEnA&5r6vhy^!nZ-guLD^md$D7+!Rp{{Y-}esw*GLl)QH`v`1ktyYopcC z)!{m=pZo2F-(08U)9PZ1y zU@EFi?pe&bz<5ELU9 zftHLsLM{=ue3=|)ShHCSh~+bl2W$Yv6DURwBcPZ@N4T7LVDdS*Yl}*7ni+W~E)k52 zCys$Np8~Q-_GJ=3Ub|Mk);Ky+k@sTp+Ue@)M(iv^C)&62_VU|}=x{~E@l(}PYt!}Y z=4d1KTE+j5Xy5Ah)+Xw=)~7est%*kT=C*v(iIZ2fU<34k9jCtn1EtoBQr0c6Fzhuu z&AR}gd-B6*m&D(2=PyWQnnka4w6a%T?ZS?AqoZXp`#YK)RbN?btxgFPSz)pyp1pPJ z7F0_=H`!bUZyhL89RaeWM{qMah1CYiBb{jpzXLLHTF+^goH6o6rg3fu-RjAt3r0cD zq;oo8E@aL!O@kV4a*pnF!tCHqhI~+PJ-7C`(yL&`CxAfdJ2qH*tNK>Ge?9)Yp{GMz z;-A8Q4FCDwM{oY+&Bn2*N@!2+SqUwNR=@v=d>Z-ia(GuB+>r;@#_Q+TCpLfdH~IQk z!|K6r7B+bq{8ZYyoqh@_uplk?Z6J09Qp<8d_CN&CLLjw)=p1a;0+Hizb{dC*Y#6#W z{P>G$#x>?ZsLPebgJAFY5vb$JBQ`qFmx?pH6!$RGHu?jY$*%%gB%h8Xc1JGlj9l7$ zqcL)IM?O;-SiQVshCQWr3UQq3 zenVU-G9v?dq!(s@b)H0}0sC6)qPYn(IU)cGboA)T!t%nle98&-|8X|VQT7Th-FC9R z_LDhJv{HZPxtwv#+qP3ZS$?uDpL9;uUNg{Jw#V)P)BcF-R$01Tt6A^C1Pi#W?4@kW z*P`Wv7Y9Cs@cqLran96p)(PjRn%e$Z@D$ZpLa>w3k`eOh`#Fu{p<787`-*+(Sf>~4 zWp2YHJ_KZu{2S^pIAJ$Byb~SXjb7P_UfDX|R&U{YznAb$-kSt6IpN$iQGE5`-D1D` zl)RzN<&f`Dl%LI|bFL?C`&4gNuo1iUe2Yc_3dbk$;Kt1DV9GEZ%-DO44_l zy@K>Z3ipve_UY8!17AQIr%fM7`-08(`J@|kKLi5@g6~j=>_Lc1LkE5;4et9v|G7Y0 z`sX%lXOF#qBFw%CyITX`O8qZLivC@?IT&X}Ih&zTrEOGXp)lodpdscAE99dTGY z7sl*V2p-Om92@Bln%dhvCC=9PNO~b(b>eW1O@fHv;27q8AEmT;j?muENcA1;;1r2^YoBoJTP5jQ+J5)`Rqvx~;^SOIGUxrStsTa)AQg=16~GrT^<>%R!b i55Mew+xxmVbGN4M*38|Wy4w{3xv~&>^wTud9=`z~f0yV0 literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..da83027096aec715f00c0ecaa650ce46609d3f6a GIT binary patch literal 4625 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 RbOL=k;fkK5Kb@kW{vUjw-)8^- literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..bc4ae104240047e06f040ee91da8577b87ff7c40 GIT binary patch literal 364 zcmXw!%}T>S6oqG!6pK=S5O*#jy6K`B)TJV%X`!JSLQ`t(W|$^J8ck+GW-P>=PvPF3 z&(b%TtuLT<*OdwAfqVGQ+1z_qt=2ZEbh6TtD+7SfI{ZufgY|v|Zy*9Q5Q7N5!x=2K zw*GSLq(lhQ^~@q7l9;m0sTtNC6;^v!L0cJiC8gW`t!nfJj@wg>!FcQks_FVc-x>BF zls25IhG+ZZkuto%_EocXTxZRnDm@rYlo3pvuF~&^f!Eg57RC_|F|L86DaIdQU7xz) zXu&gz1Q8iw$S?RK4W&RMKIbwC1-j!b=Dlvm79xpRMwvv`lCmXAlR3&ux!~;5I=?_7 zk+hSO@QK7!poj@nMD=q5xgNqeVTr`p%F9jjI^)rEN^gpzYCo(-98~tDLkPdm06hIM N_I9+_t>Un1e*uQPZ1?~G literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py new file mode 100644 index 0000000..3997f84 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/__init__.py @@ -0,0 +1,33 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..f7ebdb771f4887409de029d46f062039f496cd7c GIT binary patch literal 363 zcmXv}u};G<5Vf6(MukcsBsQcXv2?r3{4I5v<&c?U& z53=$DK(|gL4Ls@Yy?b}k-L1Oa4ytgnB5Kq^=(8^VLH@*gzY=dqpec%x!0&j93)1&q zjwZolFB9DHo-0z>i!=w2(BCV)aqv`<+n^-ZH)Tf{n9CyMAj=&ATW-r-HAb>**fb3v z7E!o$e5Zjo!*1!eqLn+PgFY$Sj0qbtRtv5*V;^W;AG^_XW)n$)L&6Qc&+M~|9MA_F zTBjnQch5X(!CJ*s<;ae*Sja)BErLor+zc*wM`VY z9VOp<^L_Kp%zoeR`)2%QG#Wq%k>^@p>l1&~YFW zpgo)6XwTPsq232G+%(bxzh^RH0KJDa_7>7Ooym6kn>71%*5fl_oy+cR%6)D(8C{cy zH3hKg@n<&s?EWV2bN)W-bwNhb!`VZ=w%KReL7r%ieJ;}h@}vH!*=J$}CK-G|1nDH} zvJ;~py26#wk1u~P>T;RO=~1;g7a?#dZQJD36{I8LP;e%bVYL7_kInD}F3G#xSWcOK zL1w=w5kd^pNx>Cm*;MknEW2S@&Rg1ij__@={L6VI=V>r;DCP*^fjmUpK+$V6HDl%V zl&#o##Y}yOt*ov(cIt{XX*q^!r+#Lc)7HfcC(qlqF>U5`(@FJT)6HwCoH3c2D>yTj zd8Yr=>6C3a`pG#(omHlFJEfU+3TsYb&f{nG!lYvB{c{BzBnKemF#OvwAh%F0+);YB z7VRpH)grOQi@4pN^M;S=fI!>70QK!1UaG^2E2Aj}U?paFP1LwsOh(XyA*6*g{ua{+ zv8aVL0dyffs7173izZw~H<(Oh0wvqr@Wu4R2cw@{fP>#WMQB3JDYl)MvTy=xilZc) z8O2E$b|PoY>Io&`%}-!mwXl}tTz(3IWxL`Wwj4{fa&Ab;OVda{6=Cz zzHqw-Li$^#f*fCimV!m}8R&~lF|Z!+#0+wX1#Lx?ZC%Nivg>tJ`F#<|B>c324J7!^ zR^(dd`U#I5$)A_)@VREcTk>Z2H1$4L<#CL`!kw-QD~VP_2mWhkVz*cm$&EV zbWAXBn1&XPWyTHT^`GO(bO~x#M(v1MI0pSFhZs>d?9$a3T)iF7? zXE#}JbQ_bplN4Rf)^k&C5Ni|wuHYe??n0NQc62fr%XE?~+aeezyjH@*ahQ~!n_W#< z_4{A&HYjWY_U}sQxfopvZiqcA`&N&CB_^NmiLdnCPu)wcy;a@+PIb?zGWV-+t>@r< z>7KNDquTQhjHA!v2k#%gcX%^?dLw>%Gk$g>e)e&w8ovNKsn)fxGO|3fdaT-Yyv%(j z((F?GWP&{r0S^ht_8)-&@anKZC|d^`h#M{2fKWq(AczgHG^JCFfr{)Om|rss9Lyl@ z*wH?RI|MZJ&UsT+*R;JUB_pcgK1aU`LXrm3ubD(LhAh zO7fUgYgd3ooHk51jP;!27}t!Ox*M3iPEBl*S~PMrZf?-AWJkZ@-0WrrP5` zAaYXdjm_ZRLhtXOLwtvP@C z^x>yZ`$rxtPuR!vPuoYIilbhDS~Hrsj@E{6fT@5}vyBjepx)N&{X!7*t>N9N7eU{s z9lkX}4f_!2TX)}KpA9u_e6C>|b_kGY%kC1qV%X2?g-8HkLn>M^SQLw)Vr0Dzp$UY^ zC72Al;c4BeXS5sg`5{kiJJ>L%-6N`Hs*0nV@GoOMuRujGr)5Kfd&-zHbZonVZmO21 zyMhM`H#9joph1b&bg1#HnzQjS*besraaqTj#V5${Z8D5Du^~Iw4j{QKEabt3e5se{ z9`#TYn!;Az!%cS!ZW_`-u%by{LR%t=byuWisoIe!^R=$Mm2=DIe)E&ESnJ+bNiV0X z-F@XyO^R(wXEvlWRp|$t((r~fT$M)3>~pDmQ#!OE9a^!g(y=o8ulCNnXII8nN7sfP zee#5@wqJZIdRMC28g=4rim879^S_dwrs@Bk_`4_jdfk43FEYE{J&pT;0bJ}l1MP+> zA5jb{(1O4ndH=!%K#u?rnO7Z`Baf1uvE#49HLTO*#CZe=REXHV%7NtrD<7|3{UY-q z^QikTy?^d~!u?(Pn^cWmE(RkK98B&AOpfw`aLGU;KZ*0BByAgc z36+|Fz9r#=IR)D~_HyoBq9a$XP0Ekw;Dnb;9W-l*i)GK^=7F6l)uJ3DYY?}W(ATkj zo3W!Cv7@Vl)z}H}Fv%8DpQ}<|GgrpePCXc3yY@(VSa^K=N$ju5Cll57@u%WAbvj%# zSCee|D!k*<=`pW5wP8}|Fv)yO9w=^5mNiR-M>o6x?40G;@Gc?uJw8da5&9^|>+oFG z@Q;Z2A|aQEE?R#wsCq6Z%Si^m1yY+-_@8>u2J-6&6G9cl=DSG)x3{1wOZ z-`kb~{1^jOM2LNTdEzB2@}tZ)3!|+7WxH9vcPk9zmjRZ)?QPtKJ1_dLxXBKBlY%LVMb71K}NvMIOuvl2+=n?+|7Pm8K literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..ab7ad9c024bb53f114be9faafcb5c6c9052051fe GIT binary patch literal 4212 zcmai1O>h&}9e=BpRx8P}Bv5UH!4?>&6)_ez40c1X!MG+A5HzM_tY&7r)~@Z9rCsOk z8Y7RGxEW?5Pd~uZ>4;3)iRok}FkI-V(arOQ_ZSwbPs-!nZ#hg!wlt|Td zM(3&tn=&vT!Z}1lzCGy>s3biF`~I0qIt5yA!jDT}{^(4)3TToN+&9=HBQy!@4Jzpt zSq~EYg69Th@8=Pk1uxjVA}a(0-xiCnfUZ%=rZE%?>deU4Ft3UWhX{?PGQ6rrClwsU z08$P(q$yF=I4w6T z>WnEo3B$3TLuuZdu!2@q5;8 z0Fs7lqx26N?C7#~w0by|&l(3e5V81rpJ%p=1?MrF2WfjP9_H<~_)akFL?< zXdc`tvGBTyvS>z>r?ly)B&QTvl~hfXwdg#6GFr0}NMeGRg~%37^e{m;7F7{n6@}Oi z-KEaKIGyDJN>+@kyqe|Z_$92Q z;Y4cuZDm5yq?8)JpvY6o$nc?4swz#%S-8=7&s9;r8qY`*@!6a8nKgOOgJEo^oUuY|ZO!?Vk*NG*oFj zQffO=X&WfD4Ll70t^M=%FMQ=6UoN-(yx@Gq?y9!zF3QXD%9V0UH_Y;FI9#5=^}vo> zM~laokCy`-D@QAxgQd>FhgxOmVhMgbFMSobwB8b3+m|S}TwDuY+@MkLXVi=T23nsX zhi9-F*jWj5lwfA>SAhe@z&Fb+C)R=|p2EPc4I0M0RLRNze(;UcUFaWOog+T_pI!=R z$__|iCa55;y!jkR5#{N0EpUNoH!H4Rr@_8;c5Q?}33f=l?$qpnn6|Q{y%K~pJ7m?B z;MNTSBIFq=@06%K{VDZ3>beU?H3*R(LuLd^gKS!}G{~8T#q}}zlN<7oGogD@({u8S zDlLdl7U8R2Qqh=4cczrtoKDXwvzVk2-DRXD-OWvl*QCjufp5bt>zI!*xPuI3ASX>p zvhKxVhS#L4u(0l!na5pVH0G(~VoYDaon(evQ#eh$rY$tra9Wcgsv&hA$Reux12^AU zdgs^U1xGd9QXE|#y?y*HT@H5_JnNxwF|r(~g!Y$0`>RdCo9C9!J*6Exyw%QbfCBV~ z`wY3jY%pkNYq5K|`&Rqy_g2E6wclx9iynSJJvdP58!q(?ul1f@?OnaH`qo0(1pgEdu^;|8 zEM|EK;_?(H2@t!aNlC=2&WMm91W{)vB$-!JlB9d)WI$5};1u)ebSk6bD10&|Sxl!j zWd?T>@emRHbu4k3vJH49sK{|mP#M%jmS++xSrc2q8n$fB5KK^0Ko-#ki*~jYdzO33 zJN6gc)o^Puv79J}4-`DrmPj$Xoc+C0@Kr#O(|YhWGTgGNEBhF zMP2|bZI2%;V1k>MuvkxYHN{WieCkOckmo|J#kS?P+gDaDfA;R3cOM-6>)>AoSC3Xs zkCjf3l|z>cOqFfA>0R>PI`)X&ZSh8q`WM^r-3PQo!7MEQ|3X2%5QA%Pm_2NVWo$Qe z2@X=!zzi=)EzVCN??Ltqp-pxFyk?aJxkA zDuuerp&!=0Wb|(DowIkZKH%@=9=^63`aJfE#{ix#$LPf61dzli?t_J6eo`aKd{#7S zL@uica~Z-naol@ze5Ph`bDW^0Kn*pRno%^B*O+(7?v_J0#RwRm>Ji<+9NKEL_< z)Z=Pr)||VaH~UGLnxd%91BePfM*eS6%VQLNjDQbArggYJ-21D& l6>3+B+V$MgPhFr2V>ic_#&2><+$JJ29NZCkYM6|z{s)?HkBk5S literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..5d5689fbfc5541ae74faf94a1348e198b0cef871 GIT binary patch literal 3622 zcmbVOO>7&-6`uVgcPUDwNLiL0+oAuxwk0}F3g@SB{iC=xV6pK}3zTiKSnsZ+rN~`o zW@V8CWvGUMNN`fwKnux83@d1X*zln~rl%Tp4hC`|qZU%PKDfxqH^ub9a4&tc%cW$O z=ph4g_RV`UZ{Ezj@0acq49eV=8%qbCXcfIm&vkTn`?gg zENiei&sQ+q|F+Yy=%+|$uOOX+G0~U%(^jtQe|2{`Uw5}@%8F%1=AY13Mr-Q4PqQCt z3`6r{h}%F}Mn@1QljU@Fgvs(cmlfb2nnuHn&R-F;VO`RND@;~2BnjyeJ#>X>%@ff( z^f1^u3`vjb;u=eI8kLxA=Q$*Iy5SFge(IP?j7k)QQYnQE&BnUR8RI5qArOsU6Jr=; z8fGKTAoK@j4mmC33|~c`3J6u1Dtng>gue;%?T%*b5JLEUr*$apjw08+#$8j?8JMlj@#|~_BL57Iy1gAN{7oT<=`vGBvXF5( z*u*CyxUM8qSXUi2HL8-7W5eg7k*b7J7s`%7WXToCc+tSFs3@joI*Q^-ib@pEis_EI zT;8x;kt!4_ak@oZ$tYXG z(<%y~?G%)$7h~yByI`b=N(!o#{t(+aLvu*_tbM_DOpT<6Y-_|GJl21d5Oc&T7?zVB zxM)}x(|Pkkx>$BbZR^c}Hx8zW=@|V*RU1=B43dTr)Be;X4e4_MvTC4M#&mNl@n)J0 z1Pk5?T0)?DK7}5MoipLO*f+m^Vb|Y9c{#a$?(p@)3)>fyyQcY1#77-n)6Q&ZrgZiF z`N3O5H-^6LkRK)1&GldJUrNaJguI+scTJxwTrVuB_1@jJb$e=i-ur6suS0(s`sybS zch%x&8XTMKT0v}Q*9wQCdmAt<(prfJ4VrEp@NfJYDoE>QdIF-GsSN5?$*qM%Q`fGmSmHQL(I|dNPXP!`(4LU==%({{`I&O^-vtqasF^Vcy9XL}SQzmf*FNfT) zGHR5};j(w4V54d5hHPw(n3fyCMqYKyi_`-B+AHkXier?VN-Q{lrlKFL zWH(f>{jtPsd8WJ+OVwkkg`O|_zUZsPhVGrYzoQ--dbka$nmB}ep|3^>ASf*|I0*}y zLsc%<+O?nyfVL@4hMm@aSHl5pVAtC0r3dAHr$qrd3aD2~j-J*l#e;Gie*jhXW{Ywz zGfrnP&;xfH&enMfeqE?Q?&^HN;DhKl`w^NHtKq7+0bJ-I8Yy8uiT;3C!N+IlCL8R; zWrj!9a9e^3>0&^rlVJ%t$sqb%Pav&+!N>a5Rv-nZ5X?3yu|Tqvs;g44x^rkda{7W1 z&c;9*W_KWbU^4Op)+jlf125G`F!#%=*3o3-Qe=XeKqc;%Xo8U=naa9TR&m@((X>q! z#);D;&yJKf3h>Yz0~1UGeL^$p2~D~eOn+tLph`wlMyUu?X6Px^NfmA2Cm_p|iz+=Q zjZ#X7yG=K(k(5D+^WBsjb0Z^$qYwv!7Sp@K73s8;ayZH`^L5(nTt&Cy*e*|_a~ad+>E+_GMu|`$P`o#b4|{1C+XWx^-2w1h zssk{BBw*jB(29uS$=ORYmlk8MPxBtpUVUetyA{0={kCIAi)8oJ6Z@7EyKi%Uj^2qb zT)OwGTKwoLr#=gz#QJL=&2PW8|Hl5?-Sv%oZ=b1e9Jt3Ub{~9-Sm`V?eRMgNymoYE zVt)J8OOJXtP_AC;P1k$VwS%W0o_Tn@cHqOs-ZQ||D+2Am5<>Bw+3HMn{`f*-F}7ox zUtu{Z!#qm9wv^miPwrgMza0Bw?2m=T*YoDL0 z#dbYK3>_ET7IY!Cfn#64c^^MGbYHIRKfRb3szrzXv%=ASF9^-6zx`Pw#?H z@BFZthrH74LJ=E;cq!pVHPx~$Q&aQ6_O1K8N%bNmh%#YfW6fpT)EtKx3C*!7v@Awv zBQud}Z_T}Z{q28zS3t$EuhNn(Fj>V@=F4O9n z7!tbRwJu~Rrb6A1e|GLE7Z>(6IB2Y}RNBD^n?2i8mK9{r@Qg~{PI%ME5{7#Z0)q$M zKIKMGiFWZHEmBL1H__3;SxQ-+!#l`#PG2Z5#2@>6c^IFCCS4{u z0u=y~VH(>I6aNnN{EPnL5G5;oy!p4Amzcgf)Ax)$z{u0utT|)O+B0?oK@-AfH+)YG H-VFZ*>Z=xg literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/mxbase.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5a31036bb6393d4e1c46e38395b94cbd16f3161 GIT binary patch literal 4415 zcmcIn-ES1v6~FVfJG*$jHpbXM3`>(zmcnlMD2WXzlsJS|vfE;U$SPtq%#7_ZyEE>+ zV}mzK<4TrdOO1es%8sHco4y2#7asFJ<)dPqAz_jEK(w#dd{7h^a>eYzxOAv!U$rtE$5L2$IT48G(%0@(=*|}BdE(U<=adTh z3}h=pMZ7Bp(*miGvBv$ObVa%gh{RSQT4XeoO$4)zv?4di)^$yhMPV=-^A$ERZc$&= zG~2N~P4knQ&a?^J@eUuSC=E+6;piocVmQDSnOVhQKTb{VNIynRZwhpvFc2f-7V=lVd z>oCcV0Qq5>{7p^G#h2CYg|6zp@6_zt&fQ<;KhNJeu(I=Kv+^I)Yu&GXnfg3c9bM_p z&&r>rYOsqKbwk4d9k~VmYaaobN7F;Uxs5gM2p1dOdqakOsA*IC0@6%H{(}5j0z8U? z&xA}?3QXH>jJhtfG-?%f&z`isDQ@?yLQ|l_kGa$yu^m4dt(Prdyn2oAW#}O83)+sd zanIE}>$+EN4eb<;_roH~0huOi9bNP9+09YIJFBj3-9DeconLDI zQFU+iT(xg0om*0K6jPQF2NE^(JsO~P83>q28VfNS31A-u;Egz}H#3)9Q7W-0KiyD8 zFg2R660fLtL(>frs)DLT(NB(8UI<=aMILdRP56>gWV97zXc~wwdG1xZ9fw^wOl??8 z^V|&}GmIu~F(iT262sIj1$Ju-?N*;T2;%H0kZH26lI=U@FWe#R*23B9ukNWU>E0!^H!xp4JV8m_e1CxfB#OH6#=_(P)Aa^; zX^NyMk=#%!!dRno!krZ*N)9~Lpcyx{F+};M#vLF*VhJ4}kSWAzS=@UL@h#Y4ZUasgk{XD8@VKO zg$mB%nl>UmoHz#OWqm*((9@kaKc4$|;aGLs*L%L&vywhME3c_7HCRWwmF)te(3we6M(4efQt^qX3HkBb=3&!8x+`jaVJZ2=M2jOn1RIvT?c_x(3|F z#&yap8bG(6ZyEYEglPcMHV#QNvKH(pcQsqd2%H&NJPr+seg&W*pE!}bRletfp)ZKyi8sh|y#*cRan8s(fSu{1v$xWk2h`bqH zO!g&sk??WEH<9`Bgl8B19nno1ZwD2FMYAIJv9Dry=vRss!$U{Oru_x^a(5F`a7|}m zAL{`!P5zPIikM$b?^#aoSxxsYr+dHIzmk4;DTFnI>V>Q{cwul+8XP>!b7!^{O90H4 zyadont~pUezD3i1H=!59mAIyvt^w2V#IvI7F-_xdE!_bh_P6j?zhuEvZPL9kLw||{ z-xB&JlDB~PsjxaQtYT5q76pp+F~`7Zj$VqPzzN=l4FmaMhWuUbec0Z!P+n>~F#XG? zvZTCOONvS!@@%aA;jf3DNQ%-|lVGqeBG)P@{la<@#!p14z5?8nq@e6skHNxYQ2-gf z=P747X!wT>_-4H!YOvMb0P;djD)DMmoJn9^3KgURFShKre&9s;%rXZqmA`(qO7dXl z%7mA>HVQ9&1}&e&=!TNhalOojUJ$Sx7(0^|^(-^vxtadKp$tYhma8S*JDAZOGtk3T zm^PG59>RO>256Iwk>$oi+4V_uO1D2_%^J= zXEC`^dH6nu0G>){KbVKUhemAb*}X5jc-_Yrac^@Ue{^5%+wd_x zi=R4;;`oMy`Qsc;BKa*Y)Ey}(ec#6fxPvvt5Vc zkA+~)E|n&D;P@(vU{LxA3bpV-);1TxBb{P`GUH;y&@`XUKVf))8Q z^<ifHS?GQ$ z?i2cj*`b>s&V6`Oo6~9p$B^U19qW8){k*VgLrge!>tc;S7m5M5zIBt3BAi&5tP$wA L&|?uee(-+*m%h!* literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..f1b31bdfc6cc16f737950dd3e907b2f6cf1bdd25 GIT binary patch literal 2848 zcmb_e-A@!(6uPI>-sJgjKNM}|Xu4bu1#rnew8l#~Pr4}_&8+2WW`k{8IR0Pble#nxaAx_DoV z!~&_4GRXFSb?Lqk$?C*N!a-)qno!Kifx@RIDGM~ia9qbX4B$*|xMr0y+$*O9GN+Z% zAjRx3%7Myke-@UYHb@&rC^Za@a~;09*X6>vSEU7R@~Y_;t}w4mEuR;zdLy3iSiJD9 z=Z<-o2L~>3?u@xr>iUKJGA<9EO_*bp7i^aoqN91i ziNV69fbTtAg|`gGp%hgwV`#dbgiEJL(4rm+_PL=s;&-g9`0L^ zZ?`nSidfC4zyzb-0{@LGK$bD4Fz)}#dsoEDy;$6o{U#E1If?%;VJR@qNm1~!*0|?! zYA{+ceP`P7X9J1*ET=F$KTtj9j5%(QN%S=uh!Zp6+GezM){eF*-!puA!>=8PLlMtU zfHI849C?!MT<%@!eVom2Wb^l?*K3>E!3FhM`@y?sfBpHP*2C}-TX?_ZLl46II}Z#* zLMq33lo;Sxm+MMhOSNYqn_EcSX$2sFs>Ftu`8AXyd8u>P<%Ac5q26YLO9 zJ8{~!>$Bl|k;@t=Fv0MPyiSiYuNwK?5&PYgoC0h95s*2ut&xMB%SV@vu8gjh*9SJU z7Z=n&!S8b`Ly!mUeGj$1$oVE@unY@o==?=s-Zc&C5`d101fKv;Erfkdj9^EsW*kP> zVw~ksr$PhG@~V@Javnw@Xthk&a~;d9I5oN-Z4F`)Zn2yBv7i74@*W_7x7K>6WwGV4 z*1Mtgu3TO{b#G|>n@8HEH(hPw{246#Z=91XzNs`ZR`y}cj=&B%mG#1~2V8a1WSp`H zUp$RtbVDFxBs)UfFe*l5TkF~j=pMysTz(wL9NEftEgxSxzIu2wd+P0o=EUN)oK(D4 zT$75$q3H3oVy(ma2DL6@7*)@nsvzHL7(Y#!l~|K94BN9n3>A)7Jf9mzSZi1p938ZT zMXySsRN3r(kg$_Ta;R&IOQJ<96~o903>zlLmvb@9T84QM62ysr1?1H{`AhzMD|_T_ zX71}9SycKPnyBUXX3d*3ct-}1Jh_`x>AOjSpHN^i(A{@V%QPA+?Lcwcp>ZiS_FvQ@%bk4kb;;C}t2~=ytB>>%04c z0d-;SPE+RVzNcS?E{OPIpRAgGzizsAv`e^w?LhJO&5=rEkZfpsGp+?az|I$~|LNJ< zP~mK0@3V#U8DcsFjbSs{_XbmksI~$?(F&IL{@5592 zkqjWoBf*?nBasSlS5DOul*_MfYcS0TgQGm_E_lN5W0879-)6>_|Emk>vxPrqd{N_ eIV~R9M#c6Q`;1lL+}$4<1cuP)g$P`DlYam>KzWJ) literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..6718187d4bb0299ee46971971129c6741a799d3f GIT binary patch literal 29901 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 a$W#ceu`6#czuiD|43}h&eC4^kbN&w?J<&P< literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-312.pyc b/.venv/lib/python3.12/site-packages/dns/rdtypes/__pycache__/txtbase.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4ddf711d995f3bf31c90169bacb8a4620a528e5 GIT binary patch literal 5211 zcmcIo-A^3X6~D7Pv){|^!a~5tFtIUVZI%sANG;iNiVaR&wW~IvQL<993_BM*>)jnX zGuVLE5si?-DvmHxBe2>kv{lu#ezE!=w2#dT>vapA_`y{wRe4*CtE7Hu&$%{ zq+Yyt?mhS1k2&Xee&>!q4F-J#%Cnz3lnZmpF)T5Ih-Z#%8_ZCjLTo^!@r^U<+4$KpBV3Kb_Lor;;9r0WA;piYc8ox_DW zlOItshUvLvP)(Z1QBxS3naYrO%FX|`cM8CXPaJUK{{6CV z_;$hn*(N@iF|dFC?DP9&lTC8dqEJU#huk8E7P-_R8kM7Rc#*4*99|-`T&nYR5^FWx zfW-5F!TEDoTSycqRZY}n$&f_jvSf≧6i2A}EU*&8V6z_UV+0uU)(#4nF_FP;9_- z>xQNjrgYP-$qdh?OOuVcX`101jw(PKLt^KbP<_N9XpQ=Ci7$~4L8HhO9k&6}SU6H5 zS)8?}6rBuk+@hzZSt4mW*a$JaHFcO>Ba@m4p0_!xBRj725u*7Ge{E29*2doB=J=Q} z|EEg@#ZaW2GEYTj04r{pc?Ntw(Jm+Kw?5G?N(FIzTqzji<7heKGPP{Uj3TO&O!699 z_sQvKtFNDpqhCAOHa*T34de;b93P*UGpIg3UO!{TerxuwXww$>?I7Ym;#NtC%QlWP zCGZ6!-WrVoWSq&dFa8aKG0ya+B|XhHi8)Me*5q}Xo7B;9;=)?6={Cjm`lc7rl8MEQ_b|$_OvX+JfR^HO=pf4Oy3()ZibF(nySSFErR=C2GkIH zmYO%6W++Lgb#%`-U~54`_SM>;nh)=Nw_cFyNvQs{Kpu4xZ&S%#_O!1(v)+5p6Wcl- z`^`JQcxNlnQfWQ2<8<`;miQZiM=lZwEg8$jQnBLiT<7lkpS|DQxp8Eq(mc8m7_B-< z;HZYiNt!NIQ>GL+w?=c4p^{m zIOUTjdCVPs%!x7Hb^?IM6jv2Vc0^$e5D zZv{d(CvU#F_Jg14AJ5&I+w4A7?ml&Au+lxW(e=_DX`|(IIdFRE!d7d0>0(uYDODE< zw=DUXYv6F|6*IW&mb9T}8m)xjLGSGR1tNNtOp)vEqVu5WuB8O<$gjYHo}wq)=v7z( zOHGT7AHc=6aXkU^8_!`)ug}-J4Ua7%S%m(ck_9jBEiza0^n08Us*S=v2V7KYn!xHK z;Ky&WTLb$S-C5+vJ>}^;bL7UadD3uBcl}H}%QT1niv=+9_LTQH2e2^a#G<=_hlS*P zgw8^GHK;5~Ha}^2t(y;7WF;D^D&~M@8J)%aW^jrcX^rM3V8+6f8JQ`tAfNyri^k2c zqNjf}qr#GDYYj695=91fRwfmyX~$rF(@SSFblOnVg6V&Ku3$*BjMTJt92Bl1%b6}| zdYTqwGx)YmM^9Fw@uktKheX;o z!@cEj@4ax}C!?GFFPHmYzSlqe_qLAJ{7QbaEm3YuY&?IV(l)XY9=RWCzB#mdX5~yJ z)V+S}Zs_>^C)I{4ZRa<_=Ye;ZlcnU^(8p(Po%wU%xoRtH{iqGu_u2<%*9PzUJMTBQ zuEtm5o6YfZb9^HZ|0}Hgt+k8muT(<)8=ig^>zT%Q!`Q<3-1$9JtAL8HVb`+|%fdHc zRSu%f=Hel^c@?>A{T>Ay+NCJeFbyAH?5{Ql(0`66bL1EqBszD^!I8IM6_8$_N7iGA^6KH4Tz~%*6=W$NafK8tA!eY50C5ie$YR ziI*esPvkqpmB>rmgbQ|Ug`=y!72n$6Pl8`Mppzw#F zFx=*S!iRNk{v9YWZWf--%`(T>YzVZT#?7vxaNs?;o+-hfPt5`LKHQC=I^PHleB(%3 zG~zV94HSZml2K<0$QbM^=xB)-4}g;*fG5ECZS4RjB&}>5xnIUq&!WhO>6nDxQWRpSv{euIgLkSBRI4e&hWGK^AzsTvgu_!iqT^My6%&C{hq)r%4z@dbDM*g8a9 z5x9e~#kK}cZHJx_;(HDTl6~hwnTIq*mftSDy&3E&2Yaesl=<-dPn^Y%;sBl{h^;2l zet30uWp?%Y%Jp@l{LINJ?pO`OftEW;9n0ON?uQYO>^SjY4s4xr3SF?UEAr4G*aMHS z#mJvoIo6_m2Mlex03DFFEk84lBRn*uJ2MDZ+ktvkwxGsjNU~vpbA5d7h4Q( zW>dXsL6Xhcg<^mW*Z7$YHlV4#Hbay4CY}ai@mpdY9zR^*%ghRqa~g#%qDV)@s*~fm z?HH@sUlgz09>ga(tKL;!BxjrKBvY SC3S~jAI$0K*k(g)=l=lN{&@ue literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..eb09eb90f535f1cef204a33841d6b972958894d3 GIT binary patch literal 13896 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py b/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py new file mode 100644 index 0000000..fb49f92 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/dnskeybase.py @@ -0,0 +1,83 @@ +# 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): + key = dns.rdata._base64ify(self.key, **kw) # pyright: ignore + return f"{self.flags} {self.protocol} {self.algorithm} {key}" + + @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 new file mode 100644 index 0000000..8e05c2a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/dsbase.py @@ -0,0 +1,83 @@ +# 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) + digest = dns.rdata._hexify( + self.digest, chunksize=chunksize, **kw # pyright: ignore + ) + return f"{self.key_tag} {self.algorithm} {self.digest_type} {digest}" + + @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 new file mode 100644 index 0000000..4eb82eb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/euibase.py @@ -0,0 +1,73 @@ +# 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.exception +import dns.immutable +import dns.rdata + + +@dns.immutable.immutable +class EUIBase(dns.rdata.Rdata): + """EUIxx record""" + + # see: rfc7043.txt + + __slots__ = ["eui"] + # redefine these in subclasses + byte_len = 0 + text_len = 0 + # 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( + f"EUI{self.byte_len * 8} rdata has to have {self.byte_len} bytes" + ) + + 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( + f"Input text must have {cls.text_len} characters" + ) + for i in range(2, cls.byte_len * 3 - 1, 3): + if text[i] != "-": + raise dns.exception.SyntaxError(f"Dash expected at position {i}") + text = text.replace("-", "") + try: + data = binascii.unhexlify(text.encode()) + except (ValueError, TypeError) as ex: + raise dns.exception.SyntaxError(f"Hex decoding error: {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 new file mode 100644 index 0000000..5d33e61 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/mxbase.py @@ -0,0 +1,87 @@ +# 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 f"{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 new file mode 100644 index 0000000..904224f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/nsbase.py @@ -0,0 +1,63 @@ +# 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 new file mode 100644 index 0000000..7338b66 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/svcbbase.py @@ -0,0 +1,587 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import base64 +import enum +import struct +from typing import Any, Dict + +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 + OHTTP = 8 + + @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 += f"\\{c:03d}" + return text + + +def _unescape(value: str) -> bytes: + if value == "": + return b"" + 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) -> Emptiness: + 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) + + +@dns.immutable.immutable +class OHTTPParam(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("ohttp 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 + + +_class_for_key: Dict[ParamKey, Any] = { + ParamKey.MANDATORY: MandatoryParam, + ParamKey.ALPN: ALPNParam, + ParamKey.NO_DEFAULT_ALPN: NoDefaultALPNParam, + ParamKey.PORT: PortParam, + ParamKey.IPV4HINT: IPv4HintParam, + ParamKey.ECH: ECHParam, + ParamKey.IPV6HINT: IPv6HintParam, + ParamKey.OHTTP: OHTTPParam, +} + + +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 f"{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() + pkey = ParamKey.make(key) + pcls = _class_for_key.get(pkey, GenericParam) + with parser.restrict_to(vlen): + value = pcls.from_wire_parser(parser, origin) + params[pkey] = 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 new file mode 100644 index 0000000..ddc196f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/tlsabase.py @@ -0,0 +1,69 @@ +# 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) + cert = dns.rdata._hexify( + self.cert, chunksize=chunksize, **kw # pyright: ignore + ) + return f"{self.usage} {self.selector} {self.mtype} {cert}" + + @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 new file mode 100644 index 0000000..5e5b24f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/txtbase.py @@ -0,0 +1,109 @@ +# 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, Tuple + +import dns.exception +import dns.immutable +import dns.name +import dns.rdata +import dns.rdataclass +import dns.rdatatype +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[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) + ) + if len(self.strings) == 0: + raise ValueError("the list of strings must not be empty") + + def to_text( + self, + origin: dns.name.Name | None = None, + relativize: bool = True, + **kw: Dict[str, Any], + ) -> str: + txt = "" + prefix = "" + for s in self.strings: + txt += f'{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: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = 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 new file mode 100644 index 0000000..c17b154 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rdtypes/util.py @@ -0,0 +1,269 @@ +# 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, Iterable, List, Tuple + +import dns.exception +import dns.ipv4 +import dns.ipv6 +import dns.name +import dns.rdata +import dns.rdatatype +import dns.tokenizer +import dns.wire + + +class Gateway: + """A helper class for the IPSECKEY gateway and AMTRELAY relay fields""" + + name = "" + + def __init__(self, type: Any, gateway: str | dns.name.Name | None = 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 + assert isinstance(self.gateway, str) + dns.ipv4.inet_aton(self.gateway) + elif self.type == 2: + # check that it's OK + assert isinstance(self.gateway, str) + 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: + assert isinstance(self.gateway, dns.name.Name) + 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: + assert isinstance(self.gateway, str) + file.write(dns.ipv4.inet_aton(self.gateway)) + elif self.type == 2: + assert isinstance(self.gateway, str) + file.write(dns.ipv6.inet_aton(self.gateway)) + elif self.type == 3: + assert isinstance(self.gateway, dns.name.Name) + 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: Iterable[Tuple[int, bytes]] | None = None): + last_window = -1 + if windows is None: + windows = [] + 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 = dns.rdatatype.RdataType.make(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): # noqa: B007 + weight = rdata._processing_weight() or _no_weight + if weight > r: + break + r -= weight + total -= weight # pyright: ignore[reportPossiblyUnboundVariable] + # pylint: disable=undefined-loop-variable + ordered.append(rdata) # pyright: ignore[reportPossiblyUnboundVariable] + del rdatas[n] # pyright: ignore[reportPossiblyUnboundVariable] + 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 new file mode 100644 index 0000000..cc912b2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/renderer.py @@ -0,0 +1,355 @@ +# 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.edns +import dns.exception +import dns.rdataclass +import dns.rdatatype +import dns.tsig + +# Note we can't import dns.message for cicularity reasons + +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( # pyright: ignore + 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( # pyright: ignore + 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( # pyright: ignore + 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( # pyright: ignore + 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 new file mode 100644 index 0000000..923bb4b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/resolver.py @@ -0,0 +1,2068 @@ +# 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, Sequence, Tuple, cast +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.rdata +import dns.rdataclass +import dns.rdatatype +import dns.rdtypes.ANY.PTR +import dns.rdtypes.svcbbase +import dns.reversename +import dns.tsig + +if sys.platform == "win32": # pragma: no cover + 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): # pyright: ignore + 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 f"{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: # pragma: no cover + # 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[ + str | None, + bool, + int, + Exception | str, + dns.message.Message | None, +] + + +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(f"Server {err[0]} answered {err[3]}") + return texts + + +class LifetimeTimeout(dns.exception.Timeout): + """The resolution lifetime expired.""" + + msg = "The resolution lifetime expired." + fmt = f"{msg[:-1]} after {{timeout:.3f}} seconds: {{errors}}" + 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 = f"{msg[:-1]} {{query}}: {{errors}}" + 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: str | None = None, + port: int | None = 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 self.rrset is not None: + 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 is not None and len(self.rrset) or 0 + + def __iter__(self) -> Iterator[Any]: + return self.rrset is not None 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 EmptyHostAnswers(dns.exception.DNSException): + """The HostAnswers has no addresses""" + + +class HostAnswers(Answers): + """A dict of DNS stub resolver answers to a host name lookup, indexed by + type. + """ + + @classmethod + def make( + cls, + v6: Answer | None = None, + v4: Answer | None = 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, potentially + # 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: # pragma: no cover + 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)) + if answer is None: + raise EmptyHostAnswers + 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) -> Answer | None: + """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: CacheKey | None = 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) -> Answer | None: + """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: CacheKey | None = 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + rdclass: dns.rdataclass.RdataClass | str, + tcp: bool, + raise_on_no_answer: bool, + search: bool | None, + ) -> 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: dns.nameserver.Nameserver | None = None + self.tcp_attempt = False + self.retry_with_tcp = False + self.request: dns.message.QueryMessage | None = None + self.backoff = 0.0 + + def next_request( + self, + ) -> Tuple[dns.message.QueryMessage | None, Answer | None]: + """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: dns.message.Message | None, ex: Exception | None + ) -> Tuple[Answer | None, 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: Any | None + keyname: dns.name.Name | str | None + keyalgorithm: dns.name.Name | str + edns: int + ednsflags: int + ednsoptions: List[dns.edns.Option] | None + payload: int + cache: Any + flags: int | None + retry_servfail: bool + rotate: bool + ndots: int | None + _nameservers: Sequence[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": # pragma: no cover + 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: # pragma: no cover + 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, encoding="utf-8") + 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: # pragma: no cover + """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: float | None = None, + errors: List[ErrorTuple] | None = 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: bool | None + ) -> 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: dns.name.Name | str | None = None, + algorithm: 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: int | bool | None = 0, + ednsflags: int = 0, + payload: int = dns.message.DEFAULT_EDNS_PAYLOAD, + options: List[dns.edns.Option] | None = 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[str | dns.nameserver.Nameserver], + nameserver_ports: Dict[str, int], + default_port: int, + ) -> List[dns.nameserver.Nameserver]: + enriched_nameservers = [] + if isinstance(nameservers, list | tuple): + 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( + f"nameservers must be a list or tuple (not a {type(nameservers)})" + ) + return enriched_nameservers + + @property + def nameservers( + self, + ) -> Sequence[str | dns.nameserver.Nameserver]: + return self._nameservers + + @nameservers.setter + def nameservers( + self, nameservers: Sequence[str | dns.nameserver.Nameserver] + ) -> None: + """ + *nameservers*, a ``list`` or ``tuple`` 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = None, + search: bool | None = 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = 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: 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: # pragma: no cover + 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: 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 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: # pragma: no cover + pass + + +#: The default resolver. +default_resolver: Resolver | None = 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = None, + search: bool | None = 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = 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: 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: 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: # pragma: no cover + """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: dns.name.Name | str, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + tcp: bool = False, + resolver: Resolver | None = None, + lifetime: float | None = None, +) -> dns.name.Name: # pyright: ignore[reportReturnType] + """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: float | None + if lifetime is not None: + expiration = start + lifetime + else: + expiration = None + while 1: + try: + rlifetime: float | None + 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 (NXDOMAIN, NoAnswer) as e: + if isinstance(e, 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: dns.name.Name | str, + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Resolver | None = 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[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 = Resolver(configure=False) + res.nameservers = nameservers + return res + + +def resolve_at( + where: dns.name.Name | str, + qname: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str = dns.rdatatype.A, + rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + tcp: bool = False, + source: str | None = None, + raise_on_no_answer: bool = True, + source_port: int = 0, + lifetime: float | None = None, + search: bool | None = None, + port: int = 53, + family: int = socket.AF_UNSPEC, + resolver: Resolver | None = 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: Dict[Any, List[Any]] = { + socket.SOCK_DGRAM: [socket.SOL_UDP], + socket.SOCK_STREAM: [socket.SOL_TCP], +} + +_resolver: Resolver | None = 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: + assert _resolver is not None + answers = _resolver.resolve_name(host, family) + addrs = answers.addresses_and_families() + canonical_name = answers.canonical_name().to_text(True) + except 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) # pyright: ignore + 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 sockproto in _protocols_for_socktype[socktype]: + proto = int(sockproto) + 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 OSError("sockaddr resolved to multiple addresses") + addr = tuples[0][4][0] + if flags & socket.NI_DGRAM: + pname = "udp" + else: + pname = "tcp" + assert isinstance(addr, str) + qname = dns.reversename.from_address(addr) + if flags & socket.NI_NUMERICHOST == 0: + try: + assert _resolver is not None + answer = _resolver.resolve(qname, "PTR") + assert answer.rrset is not None + rdata = cast(dns.rdtypes.ANY.PTR.PTR, answer.rrset[0]) + hostname = rdata.target.to_text(True) + except (NXDOMAIN, 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: # pragma: no cover + 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] + assert isinstance(addr, str) + 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: Resolver | None = 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 new file mode 100644 index 0000000..60a4e83 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/reversename.py @@ -0,0 +1,106 @@ +# 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.exception +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 = [str(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 = [str(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 new file mode 100644 index 0000000..271ddbe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/rrset.py @@ -0,0 +1,287 @@ +# 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, cast + +import dns.name +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +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: dns.rdataclass.RdataClass | None = None, + ): + """Create a new RRset.""" + + super().__init__(rdclass, rdtype, covers) + self.name = name + self.deleting = deleting + + def _clone(self): + obj = cast(RRset, 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: dns.rdataclass.RdataClass | None = 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: dns.name.Name | None = 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: dns.name.CompressType | None = None, # type: ignore + origin: dns.name.Name | None = 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: dns.name.Name | str, + ttl: int, + rdclass: dns.rdataclass.RdataClass | str, + rdtype: dns.rdatatype.RdataType | str, + text_rdatas: Collection[str], + idna_codec: dns.name.IDNACodec | None = None, + origin: dns.name.Name | None = None, + relativize: bool = True, + relativize_to: dns.name.Name | None = 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: dns.name.Name | str, + ttl: int, + rdclass: dns.rdataclass.RdataClass | str, + rdtype: 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: dns.name.Name | str, + ttl: int, + rdatas: Collection[dns.rdata.Rdata], + idna_codec: dns.name.IDNACodec | None = 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: 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 new file mode 100644 index 0000000..3417299 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/serial.py @@ -0,0 +1,118 @@ +# 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 new file mode 100644 index 0000000..ae8f0dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/set.py @@ -0,0 +1,308 @@ +# 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 python not having a set class, and + originally the class used lists in its implementation. The ordered and indexable + nature of RRsets and Rdatasets is unfortunately widely used in dnspython + applications, so for backwards compatibility sets continue to be a custom class, now + based on an ordered dictionary. + """ + + __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 f"dns.set.Set({repr(list(self.items.keys()))})" # pragma: no cover + + 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 new file mode 100644 index 0000000..86ae3e2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/tokenizer.py @@ -0,0 +1,706 @@ +# 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, 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: str | None = 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 f'{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: str | None = None, + idna_codec: dns.name.IDNACodec | None = 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: str | None = None + self.ungotten_token: Token | None = 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(f"{value} is not an unsigned 8-bit integer") + 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( + f"{value:o} is not an octal unsigned 16-bit integer" + ) + else: + raise dns.exception.SyntaxError( + f"{value} is not an unsigned 16-bit integer" + ) + 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( + f"{value} is not an unsigned 32-bit integer" + ) + 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( + f"{value} is not an unsigned 48-bit integer" + ) + return value + + def get_string(self, max_length: int | None = 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: int | None = 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: dns.name.Name | None = None, + relativize: bool = False, + relativize_to: dns.name.Name | None = 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: dns.name.Name | None = None, + relativize: bool = False, + relativize_to: dns.name.Name | None = 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( + f'expected EOL or EOF, got {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 new file mode 100644 index 0000000..9ecd737 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/transaction.py @@ -0,0 +1,651 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +import collections +from typing import Any, Callable, Iterator, List, Tuple + +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.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[dns.name.Name | None, bool, dns.name.Name | None]: + """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) -> dns.name.Name | None: + """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: dns.name.Name | str | None, + rdtype: dns.rdatatype.RdataType | str, + covers: 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) -> dns.node.Node | None: + """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: 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): + if replace: + method = "replace()" + else: + method = "add()" + try: + args = collections.deque(args) + 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" + ) + assert rdataset is not None # for type checkers + 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): + if exact: + method = "delete_exact()" + else: + method = "delete()" + try: + args = collections.deque(args) + 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._checked_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() + 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 new file mode 100644 index 0000000..333f9aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/tsig.py @@ -0,0 +1,359 @@ +# 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 +import dns.rdatatype + + +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) + assert ctx is not None # for type checkers + 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(f"unknown TSIG error code {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: dns.name.Name | str, + secret: bytes | str, + algorithm: dns.name.Name | str = 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, Any]: + """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: Dict[dns.name.Name, Any] = {} + 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 new file mode 100644 index 0000000..16289cd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/ttl.py @@ -0,0 +1,90 @@ +# 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.""" + +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(f"unknown unit '{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: int | str) -> int: + if isinstance(value, int): + return value + elif isinstance(value, str): + return 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 new file mode 100644 index 0000000..0e4aee4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/update.py @@ -0,0 +1,389 @@ +# 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 + +import dns.enum +import dns.exception +import dns.message +import dns.name +import dns.opcode +import dns.rdata +import dns.rdataclass +import dns.rdataset +import dns.rdatatype +import dns.rrset +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: dns.name.Name | str | None = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + keyring: Any | None = None, + keyname: dns.name.Name | None = None, + keyalgorithm: dns.name.Name | str = dns.tsig.default_algorithm, + id: int | None = 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: 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: 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: 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: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str | None = 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): # pyright: ignore + 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 new file mode 100644 index 0000000..e11dd29 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/version.py @@ -0,0 +1,42 @@ +# 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 = 8 +#: MICRO +MICRO = 0 +#: RELEASELEVEL +RELEASELEVEL = 0x0F +#: SERIAL +SERIAL = 0 + +if RELEASELEVEL == 0x0F: # pragma: no cover lgtm[py/unreachable-statement] + #: version + version = f"{MAJOR}.{MINOR}.{MICRO}" # lgtm[py/unreachable-statement] +elif RELEASELEVEL == 0x00: # pragma: no cover lgtm[py/unreachable-statement] + version = f"{MAJOR}.{MINOR}.{MICRO}dev{SERIAL}" # lgtm[py/unreachable-statement] +elif RELEASELEVEL == 0x0C: # pragma: no cover lgtm[py/unreachable-statement] + version = f"{MAJOR}.{MINOR}.{MICRO}rc{SERIAL}" # lgtm[py/unreachable-statement] +else: # pragma: no cover lgtm[py/unreachable-statement] + version = f"{MAJOR}.{MINOR}.{MICRO}{RELEASELEVEL:x}{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 new file mode 100644 index 0000000..3644711 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/versioned.py @@ -0,0 +1,320 @@ +# Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license + +"""DNS Versioned Zones.""" + +import collections +import threading +from typing import Callable, Deque, Set, cast + +import dns.exception +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: Callable[[], dns.node.Node] = Node + + def __init__( + self, + origin: dns.name.Name | str | None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + pruning_policy: Callable[["Zone", Version], bool | None] | None = 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: Transaction | None = None + self._write_event: threading.Event | None = 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: int | None = None, serial: int | None = 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 is None: + continue + soa = cast(dns.rdtypes.ANY.SOA.SOA, rds[0]) + if rds and soa.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) # pyright: ignore + 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: int | None) -> 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: + # pylint: disable=unused-argument + def policy(zone, _): # pyright: ignore + return False + + else: + + def policy(zone, _): + return len(zone._versions) > max_versions + + self.set_pruning_policy(policy) + + def set_pruning_policy( + self, policy: Callable[["Zone", Version], bool | None] | None + ) -> 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: dns.name.Name | str, create: bool = False + ) -> dns.node.Node: + if create: + raise UseTransaction + return super().find_node(name) + + def delete_node(self, name: dns.name.Name | str) -> None: + raise UseTransaction + + def find_rdataset( + self, + name: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: dns.rdatatype.RdataType | str = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset | None: + 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: dns.rdatatype.RdataType | str = dns.rdatatype.NONE, + ) -> None: + raise UseTransaction + + def replace_rdataset( + self, name: 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 new file mode 100644 index 0000000..2d77b4c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/win32util.py @@ -0,0 +1,438 @@ +import sys + +import dns._features + +# pylint: disable=W0612,W0613,C0301 + +if sys.platform == "win32": + import ctypes + import ctypes.wintypes as wintypes + import winreg # pylint: disable=import-error + from enum import IntEnum + + import dns.name + + # Keep pylint quiet on non-windows. + try: + _ = WindowsError # pylint: disable=used-before-assignment + except NameError: + WindowsError = Exception + + class ConfigMethod(IntEnum): + Registry = 1 + WMI = 2 + Win32 = 3 + + class DnsInfo: + def __init__(self): + self.domain = None + self.nameservers = [] + self.search = [] + + _config_method = ConfigMethod.Registry + + if dns._features.have("wmi"): + import threading + + import pythoncom # pylint: disable=import-error + import wmi # pylint: disable=import-error + + # Prefer WMI by default if wmi is installed. + _config_method = ConfigMethod.WMI + + class _WMIGetter(threading.Thread): + # pylint: disable=possibly-used-before-assignment + 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 + + 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 _RegistryGetter: + def __init__(self): + self.info = DnsInfo() + + def _split(self, text): + # The windows registry has used both " " and "," as a delimiter, and while + # it is currently using "," in Windows 10 and later, updates can seemingly + # leave a space in too, e.g. "a, b". So we just convert all commas to + # spaces, and use split() in its default configuration, which splits on + # all whitespace and ignores empty strings. + return text.replace(",", " ").split() + + def _config_nameservers(self, nameservers): + for ns in self._split(nameservers): + if ns not in self.info.nameservers: + self.info.nameservers.append(ns) + + def _config_search(self, search): + for s in self._split(search): + 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}" + rf"\{guid}\Connection", + ) + + 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, rf"SYSTEM\CurrentControlSet\Enum\{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 OSError: + break + finally: + interfaces.Close() + finally: + lm.Close() + return self.info + + class _Win32Getter(_RegistryGetter): + + def get(self): + """Get the attributes using the Windows API.""" + # Load the IP Helper library + # # https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses + IPHLPAPI = ctypes.WinDLL("Iphlpapi.dll") + + # Constants + AF_UNSPEC = 0 + ERROR_SUCCESS = 0 + GAA_FLAG_INCLUDE_PREFIX = 0x00000010 + AF_INET = 2 + AF_INET6 = 23 + IF_TYPE_SOFTWARE_LOOPBACK = 24 + + # Define necessary structures + class SOCKADDRV4(ctypes.Structure): + _fields_ = [ + ("sa_family", wintypes.USHORT), + ("sa_data", ctypes.c_ubyte * 14), + ] + + class SOCKADDRV6(ctypes.Structure): + _fields_ = [ + ("sa_family", wintypes.USHORT), + ("sa_data", ctypes.c_ubyte * 26), + ] + + class SOCKET_ADDRESS(ctypes.Structure): + _fields_ = [ + ("lpSockaddr", ctypes.POINTER(SOCKADDRV4)), + ("iSockaddrLength", wintypes.INT), + ] + + class IP_ADAPTER_DNS_SERVER_ADDRESS(ctypes.Structure): + pass # Forward declaration + + IP_ADAPTER_DNS_SERVER_ADDRESS._fields_ = [ + ("Length", wintypes.ULONG), + ("Reserved", wintypes.DWORD), + ("Next", ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)), + ("Address", SOCKET_ADDRESS), + ] + + class IF_LUID(ctypes.Structure): + _fields_ = [("Value", ctypes.c_ulonglong)] + + class NET_IF_NETWORK_GUID(ctypes.Structure): + _fields_ = [("Value", ctypes.c_ubyte * 16)] + + class IP_ADAPTER_PREFIX_XP(ctypes.Structure): + pass # Left undefined here for simplicity + + class IP_ADAPTER_GATEWAY_ADDRESS_LH(ctypes.Structure): + pass # Left undefined here for simplicity + + class IP_ADAPTER_DNS_SUFFIX(ctypes.Structure): + _fields_ = [ + ("String", ctypes.c_wchar * 256), + ("Next", ctypes.POINTER(ctypes.c_void_p)), + ] + + class IP_ADAPTER_UNICAST_ADDRESS_LH(ctypes.Structure): + pass # Left undefined here for simplicity + + class IP_ADAPTER_MULTICAST_ADDRESS_XP(ctypes.Structure): + pass # Left undefined here for simplicity + + class IP_ADAPTER_ANYCAST_ADDRESS_XP(ctypes.Structure): + pass # Left undefined here for simplicity + + class IP_ADAPTER_DNS_SERVER_ADDRESS_XP(ctypes.Structure): + pass # Left undefined here for simplicity + + class IP_ADAPTER_ADDRESSES(ctypes.Structure): + pass # Forward declaration + + IP_ADAPTER_ADDRESSES._fields_ = [ + ("Length", wintypes.ULONG), + ("IfIndex", wintypes.DWORD), + ("Next", ctypes.POINTER(IP_ADAPTER_ADDRESSES)), + ("AdapterName", ctypes.c_char_p), + ("FirstUnicastAddress", ctypes.POINTER(SOCKET_ADDRESS)), + ("FirstAnycastAddress", ctypes.POINTER(SOCKET_ADDRESS)), + ("FirstMulticastAddress", ctypes.POINTER(SOCKET_ADDRESS)), + ( + "FirstDnsServerAddress", + ctypes.POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS), + ), + ("DnsSuffix", wintypes.LPWSTR), + ("Description", wintypes.LPWSTR), + ("FriendlyName", wintypes.LPWSTR), + ("PhysicalAddress", ctypes.c_ubyte * 8), + ("PhysicalAddressLength", wintypes.ULONG), + ("Flags", wintypes.ULONG), + ("Mtu", wintypes.ULONG), + ("IfType", wintypes.ULONG), + ("OperStatus", ctypes.c_uint), + # Remaining fields removed for brevity + ] + + def format_ipv4(sockaddr_in): + return ".".join(map(str, sockaddr_in.sa_data[2:6])) + + def format_ipv6(sockaddr_in6): + # The sa_data is: + # + # USHORT sin6_port; + # ULONG sin6_flowinfo; + # IN6_ADDR sin6_addr; + # ULONG sin6_scope_id; + # + # which is 2 + 4 + 16 + 4 = 26 bytes, and we need the plus 6 below + # to be in the sin6_addr range. + parts = [ + sockaddr_in6.sa_data[i + 6] << 8 | sockaddr_in6.sa_data[i + 6 + 1] + for i in range(0, 16, 2) + ] + return ":".join(f"{part:04x}" for part in parts) + + buffer_size = ctypes.c_ulong(15000) + while True: + buffer = ctypes.create_string_buffer(buffer_size.value) + + ret_val = IPHLPAPI.GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_INCLUDE_PREFIX, + None, + buffer, + ctypes.byref(buffer_size), + ) + + if ret_val == ERROR_SUCCESS: + break + elif ret_val != 0x6F: # ERROR_BUFFER_OVERFLOW + print(f"Error retrieving adapter information: {ret_val}") + return + + adapter_addresses = ctypes.cast( + buffer, ctypes.POINTER(IP_ADAPTER_ADDRESSES) + ) + + current_adapter = adapter_addresses + while current_adapter: + + # Skip non-operational adapters. + oper_status = current_adapter.contents.OperStatus + if oper_status != 1: + current_adapter = current_adapter.contents.Next + continue + + # Exclude loopback adapters. + if current_adapter.contents.IfType == IF_TYPE_SOFTWARE_LOOPBACK: + current_adapter = current_adapter.contents.Next + continue + + # Get the domain from the DnsSuffix attribute. + dns_suffix = current_adapter.contents.DnsSuffix + if dns_suffix: + self.info.domain = dns.name.from_text(dns_suffix) + + current_dns_server = current_adapter.contents.FirstDnsServerAddress + while current_dns_server: + sockaddr = current_dns_server.contents.Address.lpSockaddr + sockaddr_family = sockaddr.contents.sa_family + + ip = None + if sockaddr_family == AF_INET: # IPv4 + ip = format_ipv4(sockaddr.contents) + elif sockaddr_family == AF_INET6: # IPv6 + sockaddr = ctypes.cast(sockaddr, ctypes.POINTER(SOCKADDRV6)) + ip = format_ipv6(sockaddr.contents) + + if ip: + if ip not in self.info.nameservers: + self.info.nameservers.append(ip) + + current_dns_server = current_dns_server.contents.Next + + current_adapter = current_adapter.contents.Next + + # Use the registry getter to get the search info, since it is set at the system level. + registry_getter = _RegistryGetter() + info = registry_getter.get() + self.info.search = info.search + return self.info + + def set_config_method(method: ConfigMethod) -> None: + global _config_method + _config_method = method + + def get_dns_info() -> DnsInfo: + """Extract resolver configuration.""" + if _config_method == ConfigMethod.Win32: + getter = _Win32Getter() + elif _config_method == ConfigMethod.WMI: + getter = _WMIGetter() + else: + getter = _RegistryGetter() + 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 new file mode 100644 index 0000000..cd027fa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/wire.py @@ -0,0 +1,98 @@ +# 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: + """Helper class for parsing DNS wire format.""" + + def __init__(self, wire: bytes, current: int = 0): + """Initialize a Parser + + *wire*, a ``bytes`` contains the data to be parsed, and possibly other data. + Typically it is the whole message or a slice of it. + + *current*, an `int`, the offset within *wire* where parsing should begin. + """ + 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 new file mode 100644 index 0000000..219fdc8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/xfr.py @@ -0,0 +1,356 @@ +# 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, Tuple, cast + +import dns.edns +import dns.exception +import dns.message +import dns.name +import dns.rcode +import dns.rdata +import dns.rdataset +import dns.rdatatype +import dns.rdtypes +import dns.rdtypes.ANY +import dns.rdtypes.ANY.SMIMEA +import dns.rdtypes.ANY.SOA +import dns.rdtypes.svcbbase +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 = f"Zone transfer error: {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: int | None = 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: dns.transaction.Transaction | None = None + self.rdtype = rdtype + if rdtype == dns.rdatatype.IXFR: + if serial is None: + raise ValueError("a starting serial must be supplied for IXFRs") + self.incremental = True + elif rdtype == dns.rdatatype.AXFR: + if is_udp: + raise ValueError("is_udp specified for AXFR") + self.incremental = False + else: + raise ValueError("rdtype is not IXFR or AXFR") + self.serial = serial + self.is_udp = is_udp + (_, _, self.origin) = txn_manager.origin_information() + self.soa_rdataset: dns.rdataset.Rdataset | None = 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: + self.txn = self.txn_manager.writer(not self.incremental) + 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() # pyright: ignore + if self.incremental: + assert self.soa_rdataset is not None + soa = cast(dns.rdtypes.ANY.SOA.SOA, self.soa_rdataset[0]) + if soa.serial == self.serial: + # + # We're already up-to-date. + # + self.done = True + elif dns.serial.Serial(soa.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.incremental: + 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 ( + (not self.incremental) or self.delete_mode + ): + # + # This is the final SOA + # + soa = cast(dns.rdtypes.ANY.SOA.SOA, rdataset[0]) + if self.expecting_SOA: + # We got an empty IXFR sequence! + raise dns.exception.FormError("empty IXFR sequence") + if self.incremental and self.serial != soa.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 + soa = cast(dns.rdtypes.ANY.SOA.SOA, rdataset[0]) + if self.incremental: + if self.delete_mode: + # This is the start of an IXFR deletion set + if soa.serial != self.serial: + raise dns.exception.FormError( + "IXFR base serial mismatch" + ) + else: + # This is the start of an IXFR addition set + self.serial = soa.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.incremental = False + 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: int | None = 0, + use_edns: int | bool | None = None, + ednsflags: int | None = None, + payload: int | None = None, + request_payload: int | None = None, + options: List[dns.edns.Option] | None = None, + keyring: Any = None, + keyname: dns.name.Name | None = None, + keyalgorithm: dns.name.Name | str = dns.tsig.default_algorithm, +) -> Tuple[dns.message.QueryMessage, int | None]: + """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: + soa = cast(dns.rdtypes.ANY.SOA.SOA, rdataset[0]) + serial = soa.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) -> int | None: + """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_rrset = query.find_rrset( + query.authority, question.name, question.rdclass, dns.rdatatype.SOA + ) + soa = cast(dns.rdtypes.ANY.SOA.SOA, soa_rrset[0]) + return soa.serial diff --git a/.venv/lib/python3.12/site-packages/dns/zone.py b/.venv/lib/python3.12/site-packages/dns/zone.py new file mode 100644 index 0000000..f916ffe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/zone.py @@ -0,0 +1,1462 @@ +# 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, + Set, + Tuple, + cast, +) + +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: dns.name.Name | None, + 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 + # We only require the version types as "Version" to allow for flexibility, as + # only the version protocol matters + writable_version_factory: Callable[["Zone", bool], "Version"] | None = None + immutable_version_factory: Callable[["Version"], "Version"] | None = None + + __slots__ = ["rdclass", "origin", "nodes", "relativize"] + + def __init__( + self, + origin: dns.name.Name | str | None, + 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: 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: 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: dns.name.Name | str, create: bool = False + ) -> dns.node.Node | None: + """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: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: dns.rdatatype.RdataType | str = dns.rdatatype.NONE, + create: bool = False, + ) -> dns.rdataset.Rdataset | None: + """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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: 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: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: 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: dns.name.Name | str, + rdtype: dns.rdatatype.RdataType | str, + covers: dns.rdatatype.RdataType | str = dns.rdatatype.NONE, + ) -> dns.rrset.RRset | None: + """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: dns.rdatatype.RdataType | str = dns.rdatatype.ANY, + covers: 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: dns.rdatatype.RdataType | str = dns.rdatatype.ANY, + covers: 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: str | None = 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, # pyright: ignore + relativize=relativize, # pyright: ignore + want_comments=want_comments, # pyright: ignore + ) + 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: str | None = 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: dns.transaction.Transaction | None = 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_rds: dns.rdataset.Rdataset | None + if txn: + soa_rds = txn.get(origin_name, dns.rdatatype.SOA) + else: + soa_rds = self.get_rdataset(origin_name, dns.rdatatype.SOA) + if soa_rds is None: + raise NoSOA + else: + soa = cast(dns.rdtypes.ANY.SOA.SOA, soa_rds[0]) + return soa + + 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: dns.rdtypes.ANY.ZONEMD.ZONEMD | None = None + ) -> None: + digests: 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[dns.name.Name | None, bool, dns.name.Name | None]: + effective: dns.name.Name | None + 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, txn, version, origin): + self.nodes = version.nodes + if self.origin is None: + self.origin = origin + + def _get_next_version_id(self) -> int: + # 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, + ) -> dns.rdataset.Rdataset | None: + 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: MutableMapping[dns.name.Name, dns.node.Node] | None = None, + origin: dns.name.Name | None = 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) -> dns.node.Node | None: + 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, + ) -> dns.rdataset.Rdataset | None: + 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_with_name( + self, name: dns.name.Name + ) -> Tuple[dns.node.Node, dns.name.Name]: + 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, name) + else: + return (node, name) + + def _maybe_cow(self, name: dns.name.Name) -> dns.node.Node: + return self._maybe_cow_with_name(name)[0] + + 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: Version): + if not isinstance(version, WritableVersion): + raise ValueError( + "a dns.zone.ImmutableVersion requires a dns.zone.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 # pyright: ignore + if factory is None: + factory = WritableVersion + self.version = factory(self.zone, self.replacement) # pyright: ignore + + def _get_rdataset(self, name, rdtype, covers): + assert self.version is not None + return self.version.get_rdataset(name, rdtype, covers) + + def _put_rdataset(self, name, rdataset): + assert not self.read_only + assert self.version is not None + self.version.put_rdataset(name, rdataset) + + def _delete_name(self, name): + assert not self.read_only + assert self.version is not None + self.version.delete_node(name) + + def _delete_rdataset(self, name, rdtype, covers): + assert not self.read_only + assert self.version is not None + self.version.delete_rdataset(name, rdtype, covers) + + def _name_exists(self, name): + assert self.version is not None + return self.version.get_node(name) is not None + + def _changed(self): + if self.read_only: + return False + else: + assert self.version is not None + return len(self.version.changed) > 0 + + def _end_transaction(self, commit): + assert self.zone is not None + assert self.version is not None + if self.read_only: + self.zone._end_read(self) # pyright: ignore + elif commit and len(self.version.changed) > 0: + if self.make_immutable: + factory = self.manager.immutable_version_factory # pyright: ignore + if factory is None: + factory = ImmutableVersion + version = factory(self.version) + else: + version = self.version + self.zone._commit_version( # pyright: ignore + self, version, self.version.origin + ) + + else: + # rollback + self.zone._end_write(self) # pyright: ignore + + def _set_origin(self, origin): + assert self.version is not None + if self.version.origin is None: + self.version.origin = origin + + def _iterate_rdatasets(self): + assert self.version is not None + for name, node in self.version.items(): + for rdataset in node: + yield (name, rdataset) + + def _iterate_names(self): + assert self.version is not None + return self.version.keys() + + def _get_node(self, name): + assert self.version is not None + return self.version.get_node(name) + + def _origin_information(self): + assert self.version is not None + (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: dns.name.Name | str | None = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + zone_factory: Any = Zone, + filename: str | None = None, + allow_include: bool = False, + check_origin: bool = True, + idna_codec: dns.name.IDNACodec | None = None, + allow_directives: 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 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: dns.name.Name | str | None = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + zone_factory: Any = Zone, + filename: str | None = None, + allow_include: bool = False, + check_origin: bool = True, + idna_codec: dns.name.IDNACodec | None = None, + allow_directives: 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: dns.name.Name | str | None = None, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + relativize: bool = True, + zone_factory: Any = Zone, + filename: str | None = None, + allow_include: bool = True, + check_origin: bool = True, + idna_codec: dns.name.IDNACodec | None = None, + allow_directives: 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, encoding="utf-8") + 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 new file mode 100644 index 0000000..7a81454 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/zonefile.py @@ -0,0 +1,756 @@ +# 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, Set, Tuple, cast + +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, + dns.name.Name | None, # current_origin + dns.name.Name | None, # last_name + Any | None, # 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: bool | Iterable[str] = True, + force_name: dns.name.Name | None = None, + force_ttl: int | None = None, + force_rdclass: dns.rdataclass.RdataClass | None = None, + force_rdtype: dns.rdatatype.RdataType | None = None, + default_ttl: int | None = 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: Any | None = 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 name is None: + raise dns.exception.SyntaxError("the last used name is undefined") + assert self.zone_origin is not None + 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(f"unknown rdatatype '{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(f"caught exception {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. + soa_rd = cast(dns.rdtypes.ANY.SOA.SOA, rd) + self.default_ttl = soa_rd.minimum + self.default_ttl_known = True + if ttl is None: + # if we didn't have a TTL on the SOA, set it! + ttl = soa_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 + mod = "" + sign = "+" + offset = "0" + width = "0" + base = "d" + g1 = is_generate1.match(side) + if g1: + mod, sign, offset, width, base = g1.groups() + if sign == "": + sign = "+" + else: + g2 = is_generate2.match(side) + if g2: + mod, sign, offset = g2.groups() + if sign == "": + sign = "+" + width = "0" + base = "d" + else: + g3 = is_generate3.match(side) + if g3: + mod, sign, offset, width = g3.groups() + if sign == "": + sign = "+" + base = "d" + + ioffset = int(offset) + iwidth = int(width) + + if sign not in ["+", "-"]: + raise dns.exception.SyntaxError(f"invalid offset sign {sign}") + if base not in ["d", "o", "x", "X", "n", "N"]: + raise dns.exception.SyntaxError(f"invalid type {base}") + + return mod, sign, ioffset, iwidth, 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 + else: + # We don't go to the extra "look at the SOA" level of effort for + # $GENERATE, because the user really ought to have defined a TTL + # somehow! + raise dns.exception.SyntaxError("Missing default TTL value") + + # 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(f"unknown rdatatype '{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(f"${lmod}", lzfindex) + rdata = rhs.replace(f"${rmod}", rzfindex) + + self.last_name = dns.name.from_text( + name, self.current_origin, self.tok.idna_codec + ) + name = self.last_name + assert self.zone_origin is not None + 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( + f"caught exception {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: dns.name.Name | None + 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, encoding="utf-8") + 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(f"{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) # pyright: ignore + + 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.Name | None = dns.name.root, + relativize: bool = False, + rdclass: dns.rdataclass.RdataClass = dns.rdataclass.IN, + ): + self.origin = origin + self.relativize = relativize + self.rdclass = rdclass + self.rrsets: List[dns.rrset.RRset] = [] + + 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: List[dns.rrset.RRset]) -> None: + self.rrsets = rrsets + + +def read_rrsets( + text: Any, + name: dns.name.Name | str | None = None, + ttl: int | None = None, + rdclass: dns.rdataclass.RdataClass | str | None = dns.rdataclass.IN, + default_rdclass: dns.rdataclass.RdataClass | str = dns.rdataclass.IN, + rdtype: dns.rdatatype.RdataType | str | None = None, + default_ttl: int | str | None = None, + idna_codec: dns.name.IDNACodec | None = None, + origin: dns.name.Name | str | None = 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 new file mode 100644 index 0000000..195ee2e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dns/zonetypes.py @@ -0,0 +1,37 @@ +# 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.8.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/METADATA new file mode 100644 index 0000000..eaaf09b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/METADATA @@ -0,0 +1,149 @@ +Metadata-Version: 2.4 +Name: dnspython +Version: 2.8.0 +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: 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.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Topic :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.10 +Provides-Extra: dev +Requires-Dist: black>=25.1.0; extra == 'dev' +Requires-Dist: coverage>=7.0; extra == 'dev' +Requires-Dist: flake8>=7; extra == 'dev' +Requires-Dist: hypercorn>=0.17.0; extra == 'dev' +Requires-Dist: mypy>=1.17; extra == 'dev' +Requires-Dist: pylint>=3; extra == 'dev' +Requires-Dist: pytest-cov>=6.2.0; extra == 'dev' +Requires-Dist: pytest>=8.4; extra == 'dev' +Requires-Dist: quart-trio>=0.12.0; extra == 'dev' +Requires-Dist: sphinx-rtd-theme>=3.0.0; extra == 'dev' +Requires-Dist: sphinx>=8.2.0; extra == 'dev' +Requires-Dist: twine>=6.1.0; extra == 'dev' +Requires-Dist: wheel>=0.45.0; extra == 'dev' +Provides-Extra: dnssec +Requires-Dist: cryptography>=45; extra == 'dnssec' +Provides-Extra: doh +Requires-Dist: h2>=4.2.0; extra == 'doh' +Requires-Dist: httpcore>=1.0.0; extra == 'doh' +Requires-Dist: httpx>=0.28.0; extra == 'doh' +Provides-Extra: doq +Requires-Dist: aioquic>=1.2.0; extra == 'doq' +Provides-Extra: idna +Requires-Dist: idna>=3.10; extra == 'idna' +Provides-Extra: trio +Requires-Dist: trio>=0.30; extra == 'trio' +Provides-Extra: wmi +Requires-Dist: wmi>=1.5.1; (platform_system == 'Windows') and extra == 'wmi' +Description-Content-Type: text/markdown + +# dnspython + +[![Build Status](https://github.com/rthalley/dnspython/actions/workflows/ci.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) +[![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 of `dnspython` 2.8.0. +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 main 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` supports Python 3.10 +and later. Future support is aligned 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.8.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/RECORD new file mode 100644 index 0000000..a1aa736 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/RECORD @@ -0,0 +1,304 @@ +dns/__init__.py,sha256=2TTaN3FRnBIkYhrrkDUs7XYnu4h9zTlfOWdQ4nLuxnA,1693 +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__/_no_ssl.cpython-312.pyc,, +dns/__pycache__/_tls_util.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__/btree.cpython-312.pyc,, +dns/__pycache__/btreezone.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=bv-2iaDTEDH4Esx2tc2GeVCnaqHtsQqb3WWqoYZngzA,2403 +dns/_asyncio_backend.py,sha256=08Ezq3L8G190Sdr8qMgjwnWNhbyMa1MFB3pWYkGQ0a0,9147 +dns/_ddr.py,sha256=rHXKC8kncCTT9N4KBh1flicl79nyDjQ-DDvq30MJ3B8,5247 +dns/_features.py,sha256=VYTUetGL5x8IEtxMUQk9_ftat2cvyYJw8HfIfpMM8D8,2493 +dns/_immutable_ctx.py,sha256=Schj9tuGUAQ_QMh612H7Uq6XcvPo5AkVwoBxZJJ8liA,2478 +dns/_no_ssl.py,sha256=M8mj_xYkpsuhny_vHaTWCjI1pNvekYG6V52kdqFkUYY,1502 +dns/_tls_util.py,sha256=kcvrPdGnSGP1fP9sNKekBZ3j-599HwZkmAk6ybyCebM,528 +dns/_trio_backend.py,sha256=Tqzm46FuRSYkUJDYL8qp6Qk8hbc6ZxiLBc8z-NsTULg,8597 +dns/asyncbackend.py,sha256=82fXTFls_m7F_ekQbgUGOkoBbs4BI-GBLDZAWNGUvJ0,2796 +dns/asyncquery.py,sha256=34B1EIekX3oSg0jF8ZSqEiUbNZTsJa3r2oqC01OIY7U,32329 +dns/asyncresolver.py,sha256=TncJ7UukzA0vF79AwNa2gel0y9UO02tCdQf3zUHbygg,17728 +dns/btree.py,sha256=QPz4IzW_yTtSmz_DC6LKvZdJvTs50CQRKbAa0UAFMTs,30757 +dns/btreezone.py,sha256=H9orKjQaMhnPjtAhHpRZlV5wd91N17iuqOmTUVzv6sU,13082 +dns/dnssec.py,sha256=zXqhmUM4k6M-9YVR49crEI6Jc0zhZSk7NX9BWDafhTQ,41356 +dns/dnssecalgs/__init__.py,sha256=B4hebjElugf8zhCauhH6kvACqI50iYLSKxEqUfL6970,4350 +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=4Oq9EhKBEYupojZ3hENBiuq2Js3Spimy_NeDb9Rl1a8,2497 +dns/dnssecalgs/cryptography.py,sha256=utsBa_s8OOOKUeudvFullBNMRMjHmeoa66RNA6UiJMw,2428 +dns/dnssecalgs/dsa.py,sha256=ONilkD8Hhartj3Mwe7LKBT0vXS4E0KgfvTtV2ysZLhM,3605 +dns/dnssecalgs/ecdsa.py,sha256=TK8PclMAt7xVQTv6FIse9jZwXVCv_B-_AAgfhK0rTWQ,3283 +dns/dnssecalgs/eddsa.py,sha256=Yc0L9O2A_ySOSSalJiq5h7TU1LWtJgW1JIJWsGx96FI,2000 +dns/dnssecalgs/rsa.py,sha256=YOPPtpfOKdgBfBJvOcDofYTiC4mGmwCfqdYUvEbdHf8,3663 +dns/dnssectypes.py,sha256=CyeuGTS_rM3zXr8wD9qMT9jkzvVfTY2JWckUcogG83E,1799 +dns/e164.py,sha256=Sc-Ctv8lXpaDot_Su02wLFxLpxLReVW7_23YiGrnMC4,3937 +dns/edns.py,sha256=E5HRHMJNGGOyNvkR4iKY2jkaoQasa4K61Feuko9uY5s,17436 +dns/entropy.py,sha256=dSbsNoNVoypURvOu-clqMiD-dFQ-fsKOPYSHwoTjaec,4247 +dns/enum.py,sha256=PBphGzrIWOi8l3MgvkEMpsJapKIejkaQUqFuMWUcZXc,3685 +dns/exception.py,sha256=zEdlBUUsjb3dqk0etKxbFXUng0lLB7TPj7JFsNN7HzQ,5936 +dns/flags.py,sha256=cQ3kTFyvcKiWHAxI5AwchNqxVOrsIrgJ6brgrH42Wq8,2750 +dns/grange.py,sha256=ZqjNVDtb7i6E9D3ai6mcWR_nFNHyCXPp7j3dLFidtvY,2154 +dns/immutable.py,sha256=InrtpKvPxl-74oYbzsyneZwAuX78hUqeG22f2aniZbk,2017 +dns/inet.py,sha256=DbkUeb4PNLmxgUVPXX1GeWQH6e7a5WZ2AP_-befdg-o,5753 +dns/ipv4.py,sha256=dRiZRfyZAOlwlj3YlfbvZChRQAKstYh9k0ibNZwHu5U,2487 +dns/ipv6.py,sha256=GccOccOFZGFlwNFgV79GffZJv6u1GW28jM_amdiLqeM,6517 +dns/message.py,sha256=YVNQjYYFDSY6ttuwz_zvJnsCGuY1t11DdchsNlcBHG0,69152 +dns/name.py,sha256=rHvrUjhkCoR0_ANOH3fHJcY1swefx62SfBTDRvoGTsI,42910 +dns/namedict.py,sha256=hJRYpKeQv6Bd2LaUOPV0L_a0eXEIuqgggPXaH4c3Tow,4000 +dns/nameserver.py,sha256=LLOUGTjdAcj4cs-zAXeaH7Pf90IW0P64MQOrAb9PAPE,10007 +dns/node.py,sha256=Z2lzeqvPjqoR-Pbevp0OJqI_bGxwYzJIIevUccTElaM,12627 +dns/opcode.py,sha256=2EgPHQaGBRXN5q4C0KslagWbmWAbyT9Cw_cBj_sMXeA,2774 +dns/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +dns/query.py,sha256=85EWlMD1hDJO5xozZ7tFazMbZldpQ04L0sQFoQfBZiI,61686 +dns/quic/__init__.py,sha256=eqHPKj8SUk5rdeQxJSS-x3XSVqwcUPZlzTUio8mOpSg,2575 +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=YgoU65THKtpHfV8UPAnNr-HkpbkR7XY01E7R3oh5apg,10314 +dns/quic/_common.py,sha256=M7lfxwUfr07fHkefo9BbRogQmwB_lEbittc7ZAQ_ulI,11087 +dns/quic/_sync.py,sha256=Ixj0BR6ngRWaKqTUiTrYbLw0rWVsUE6uJuNJB5oUlI0,10982 +dns/quic/_trio.py,sha256=NdClJJ80TY4kg8wM34JCfzX75fhhDb0vLy-WZkSyW6E,9452 +dns/rcode.py,sha256=A7UyvwbaFDz1PZaoYcAmXcerpZV-bRC2Zv3uJepiXa4,4181 +dns/rdata.py,sha256=7OAmPoSVEysCF84bjvaGXrfB1K69bpswaKtM1X89tXQ,31977 +dns/rdataclass.py,sha256=TK4W4ywB1L_X7EZqk2Gmwnu7vdQpolQF5DtQWyNk5xo,2984 +dns/rdataset.py,sha256=aoOatp7pbWhs2JieS0vcHnNc4dfwA0SBuvXAoqe3vxE,16627 +dns/rdatatype.py,sha256=W7r_B43ja4ZTHIJgqbb2eR99lXOYntf3ngGj396AvKg,7487 +dns/rdtypes/ANY/AFSDB.py,sha256=k75wMwreF1DAfDymu4lHh16BUx7ulVP3PLeQBZnkurY,1661 +dns/rdtypes/ANY/AMTRELAY.py,sha256=zE5xls02_NvbQwXUy-MnpV-uVVSJJuaKtZ86H8_X4ic,3355 +dns/rdtypes/ANY/AVC.py,sha256=SpsXYzlBirRWN0mGnQe0MdN6H8fvlgXPJX5PjOHnEak,1024 +dns/rdtypes/ANY/CAA.py,sha256=Hq1tHBrFW-BdxkjrGCq9u6ezaUHj6nFspBD5ClpkRYc,2456 +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=OAYbtDdcwRhW8w_lbxHbgyWUHxYkTHV2zbiQff00X74,3547 +dns/rdtypes/ANY/CNAME.py,sha256=IHGGq2BDpeKUahTr1pvyBQgm0NGBI_vQ3Vs5mKTXO4w,1206 +dns/rdtypes/ANY/CSYNC.py,sha256=TnO2TjHfc9Cccfsz8dSsuH9Y53o-HllMVeU2DSAglrc,2431 +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/DSYNC.py,sha256=q-26ceC4f2A2A6OmVaiOwDwAe_LAHvRsra1PZ4GyotA,2154 +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=u4qwiDBVoC7bsKfxDKGbPjnOKddpdjy2p1AhziDWcPw,4439 +dns/rdtypes/ANY/HINFO.py,sha256=D2WvjTsvD_XqT8BepBIyjPL2iYGMgYqb1VQa9ApO0qE,2217 +dns/rdtypes/ANY/HIP.py,sha256=WSw31w96y1JM6ufasx7gRHUPTQuI5ejtyLxpD7vcINE,3216 +dns/rdtypes/ANY/ISDN.py,sha256=L4C2Rxrr4JJN17lmJRbZN8RhM_ujjwIskY_4V4Gd3r4,2723 +dns/rdtypes/ANY/L32.py,sha256=I0HcPHmvRUz2_yeDd0c5uueNKwcxmbz6V-7upNOc1GA,1302 +dns/rdtypes/ANY/L64.py,sha256=rbdYukNdezhQGH6vowKu1VbUWwi5cYSg_VbWEDWyYGA,1609 +dns/rdtypes/ANY/LOC.py,sha256=jxbB0bmbnMW8AVrElmoSW0SOmLPoEf5AwQLwUeAyMsY,11962 +dns/rdtypes/ANY/LP.py,sha256=X0xGo9vr1b3AQ8J8LPMyn_ooKRuEmjwdi7TGE2mqK_k,1332 +dns/rdtypes/ANY/MX.py,sha256=qQk83idY0-SbRMDmB15JOpJi7cSyiheF-ALUD0Ev19E,995 +dns/rdtypes/ANY/NID.py,sha256=8D8RDttb0BPObs0dXbFKajAhA05iZlqAq-51b6wusEI,1561 +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=kicEYxcKaLBpV6C_M8cHdDaqBoiYl6EYtPvjyR6kExI,2465 +dns/rdtypes/ANY/NSEC3.py,sha256=NUG3AT626zu3My8QeNMiPVfpn3PRK9AGBkKW3cIZDzM,4250 +dns/rdtypes/ANY/NSEC3PARAM.py,sha256=-r5rBTMezSh7J9Wb7bWng_TXPKIETs2AXY4WFdhz7tM,2625 +dns/rdtypes/ANY/OPENPGPKEY.py,sha256=3LHryx1g0g-WrOI19PhGzGZG0anIJw2CCn93P4aT-Lk,1870 +dns/rdtypes/ANY/OPT.py,sha256=W36RslT_Psp95OPUC70knumOYjKpaRHvGT27I-NV2qc,2561 +dns/rdtypes/ANY/PTR.py,sha256=5HcR1D77Otyk91vVY4tmqrfZfSxSXWyWvwIW-rIH5gc,997 +dns/rdtypes/ANY/RESINFO.py,sha256=Kf2NcKbkeI5gFE1bJfQNqQCaitYyXfV_9nQYl1luUZ0,1008 +dns/rdtypes/ANY/RP.py,sha256=8doJlhjYDYiAT6KNF1mAaemJ20YJFUPvit8LOx4-I-U,2174 +dns/rdtypes/ANY/RRSIG.py,sha256=_ohbap8Dp_3VMU4w7ozVWGyFCtpm8A-l1F1wQiFZogA,4941 +dns/rdtypes/ANY/RT.py,sha256=2t9q3FZQ28iEyceeU25KU2Ur0T5JxELAu8BTwfOUgVw,1013 +dns/rdtypes/ANY/SMIMEA.py,sha256=6yjHuVDfIEodBU9wxbCGCDZ5cWYwyY6FCk-aq2VNU0s,222 +dns/rdtypes/ANY/SOA.py,sha256=tbbpP7RK2kpTTYCgdAWGCxlIMcX9U5MTOhz7vLP4p0I,3034 +dns/rdtypes/ANY/SPF.py,sha256=rA3Srs9ECQx-37lqm7Zf7aYmMpp_asv4tGS8_fSQ-CU,1022 +dns/rdtypes/ANY/SSHFP.py,sha256=F5vrZB-MAmeGJFAgEwRjXxgxerhoAd6kT9AcNNmkcF4,2550 +dns/rdtypes/ANY/TKEY.py,sha256=qvMJd0HGQF1wHGk1eWdITBVnAkj1oTHHbP5zSzV4cTc,4848 +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=JyPYKh2RXzI34oABDiJ2oDh3TE_l-zmut4jBNA-ONt4,2913 +dns/rdtypes/ANY/WALLET.py,sha256=IaP2g7Nq26jWGKa8MVxvJjWXLQ0wrNR1IWJVyyMG8oU,219 +dns/rdtypes/ANY/X25.py,sha256=BzEM7uOY7CMAm7QN-dSLj-_LvgnnohwJDUjMstzwqYo,1942 +dns/rdtypes/ANY/ZONEMD.py,sha256=DjBYvHY13nF70uxTM77zf3R9n0Uy8Frbj1LuBXbC7jU,2389 +dns/rdtypes/ANY/__init__.py,sha256=2UKaYp81SLH6ofE021on9pR7jzmB47D1iXjQ3M7FXrw,1539 +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__/DSYNC.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__/RESINFO.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__/WALLET.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=Iq82L3RLM-OwB5hyvtX1Das9oToiZMzNgs979cAkDz8,2229 +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=4Kz56antsRGu-cfV2MCHN8rmVo90wnZXnLWA6uQpnk4,5081 +dns/rdtypes/IN/DHCID.py,sha256=x9vedfzJ3vvxPC1ihWTTcxXBMYL0Q24Wmj6O67aY5og,1875 +dns/rdtypes/IN/HTTPS.py,sha256=P-IjwcvDQMmtoBgsDHglXF7KgLX73G6jEDqCKsnaGpQ,220 +dns/rdtypes/IN/IPSECKEY.py,sha256=jMO-aGl1eglWDqMxAkM2BvKDjfe9O1X0avBoWCtWi30,3261 +dns/rdtypes/IN/KX.py,sha256=K1JwItL0n5G-YGFCjWeh0C9DyDD8G8VzicsBeQiNAv0,1013 +dns/rdtypes/IN/NAPTR.py,sha256=JhGpvtCn_qlNWWlW9ilrWh9PNElBgNq1SWJPqD3LRzA,3741 +dns/rdtypes/IN/NSAP.py,sha256=6YfWCVSIPTTBmRAzG8nVBj3LnohncXUhSFJHgp-TRdc,2163 +dns/rdtypes/IN/NSAP_PTR.py,sha256=iTxlV6fr_Y9lqivLLncSHxEhmFqz5UEElDW3HMBtuCU,1015 +dns/rdtypes/IN/PX.py,sha256=zRg_5eGQdpzCRUsXIccxJOs7xoTAn7i4PIrj0Zwv-1A,2748 +dns/rdtypes/IN/SRV.py,sha256=TVai6Rtfx0_73wH999uPGuz-p2m6BTVIleXy1Tlm5Dc,2759 +dns/rdtypes/IN/SVCB.py,sha256=HeFmi2v01F00Hott8FlvQ4R7aPxFmT7RF-gt45R5K_M,218 +dns/rdtypes/IN/WKS.py,sha256=4_dLY3Bh6ePkfgku11QzLJv74iSyoSpt8EflIp_AMNc,3644 +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=GXSOvGtiRjY3fhqlI_T-4ukF4JQvvh3sk7UF0vipmPc,2824 +dns/rdtypes/dsbase.py,sha256=elOLkRb45vYzyh36_1FSJWWO9AI2wnK3GpddmQNdj3Y,3423 +dns/rdtypes/euibase.py,sha256=2DluC_kTi2io2ICgzFEdSxKGPFx3ib3ZXnA6YaAhAp0,2675 +dns/rdtypes/mxbase.py,sha256=N_3EX_2BgY0wMdGADL6_5nxBRUdx4ZcdNIYfGg5rMP8,3190 +dns/rdtypes/nsbase.py,sha256=tueXVV6E8lelebOmrmoOPq47eeRvOpsxHVXH4cOFxcs,2323 +dns/rdtypes/svcbbase.py,sha256=0VnPpt7fSCNt_MtGnWOiYtkY-6jQRWIli8JTRROakys,17717 +dns/rdtypes/tlsabase.py,sha256=hHuRO_MQ5g_tWBIDyTNArAWwbUc-MdZlXcjQxy5defA,2588 +dns/rdtypes/txtbase.py,sha256=lEzlKS6dx6UnhgoBPGIzqC3G0e8iWBetrkDtkwM16Ic,3723 +dns/rdtypes/util.py,sha256=WjiRlxsu_sq40XpSdR6wN54WWavKe7PLh-V9UaNhk7A,9680 +dns/renderer.py,sha256=sj_m9NRJoY8gdQ9zOhSVu0pTAUyBtM5AGpfea83jGpQ,11500 +dns/resolver.py,sha256=FRa-pJApeV_DFgLEwiwZP-2g7RHAg0kVCbg9EdNYLnc,73967 +dns/reversename.py,sha256=pPDGRfg7iq09cjEhKLKEcahdoyViS0y0ORip--r5vk8,3845 +dns/rrset.py,sha256=f8avzbtBb-y93jdyhhTJ8EJx1zOTcNTK3DtiK84eGNY,9129 +dns/serial.py,sha256=-t5rPW-TcJwzBMfIJo7Tl-uDtaYtpqOfCVYx9dMaDCY,3606 +dns/set.py,sha256=hublMKCIhd9zp5Hz_fvQTwF-Ze28jn7mjqei6vTGWfs,9213 +dns/tokenizer.py,sha256=dqQvBF3oUjP7URC7ZzBuQVLMVXhvf1gJusIpkV-IQ6U,23490 +dns/transaction.py,sha256=HnHa4nKL_ddtuWH4FaiKPEt81ImELL1fumZb3ll4KbI,22579 +dns/tsig.py,sha256=mWjZGZL75atl-jf3va1FhP9LfLGWT5g9Y9DgsSan4Mo,11576 +dns/tsigkeyring.py,sha256=1xSBgaV1KLR_9FQGsGWbkBD3XJjK8IFQx-H_olH1qyQ,2650 +dns/ttl.py,sha256=Rl8UOKV0_QyZzOdQ-JoB7nSHvBFehZXe_M0cxIBVc3Y,2937 +dns/update.py,sha256=iqZEO-_U0ooAqLlIRo1OhAKI8d-jpwPhBy-vC8v1dtY,12236 +dns/version.py,sha256=d7ViavUC8gYfrWbeyH8WMAldyGk_WVF5_zkCmCJv0ZQ,1763 +dns/versioned.py,sha256=yJ76QfKdIEKBtKX_DLA_IZGUZoFB1id1mMKzIj2eRm8,11841 +dns/win32util.py,sha256=iz5Gw0CTHAIqumdE25xdYUbhhSFiaZTRM-HXskglB2o,16799 +dns/wire.py,sha256=hylnQ30yjA3UcJSElhSAqYKMt5HICYqQ_N5b71K2smA,3155 +dns/xfr.py,sha256=UE4xAyfRDNH14x4os8yC-4Tl8brc_kCpBLxT0h6x-AM,13637 +dns/zone.py,sha256=ZferSA6wMN46uuBNkrgbRcSM8FSCCxMrNiLT3WoISbw,53098 +dns/zonefile.py,sha256=Xz24A8wH97NoA_iTbastSzUZ-S-DmLFG0SgIfVzQinY,28517 +dns/zonetypes.py,sha256=HrQNZxZ_gWLWI9dskix71msi9wkYK5pgrBBbPb1T74Y,690 +dnspython-2.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +dnspython-2.8.0.dist-info/METADATA,sha256=dPdZU5uJ4pkVGy1pfGEjBzRbdm27fpQ1z4Y6Bpgf04U,5680 +dnspython-2.8.0.dist-info/RECORD,, +dnspython-2.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +dnspython-2.8.0.dist-info/licenses/LICENSE,sha256=w-o_9WVLMpwZ07xfdIGvYjw93tSmFFWFSZ-EOtPXQc0,1526 diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/WHEEL new file mode 100644 index 0000000..12228d4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/licenses/LICENSE b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000..390a726 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/dnspython-2.8.0.dist-info/licenses/LICENSE @@ -0,0 +1,35 @@ +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/gridfs/__init__.py b/.venv/lib/python3.12/site-packages/gridfs/__init__.py new file mode 100644 index 0000000..8173561 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/__init__.py @@ -0,0 +1,54 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""GridFS is a specification for storing large objects in Mongo. + +The :mod:`gridfs` package is an implementation of GridFS on top of +:mod:`pymongo`, exposing a file-like interface. + +.. seealso:: The MongoDB documentation on `gridfs `_. +""" +from __future__ import annotations + +from gridfs.asynchronous.grid_file import ( + AsyncGridFS, + AsyncGridFSBucket, + AsyncGridIn, + AsyncGridOut, + AsyncGridOutCursor, +) +from gridfs.errors import NoFile +from gridfs.grid_file_shared import DEFAULT_CHUNK_SIZE +from gridfs.synchronous.grid_file import ( + GridFS, + GridFSBucket, + GridIn, + GridOut, + GridOutCursor, +) + +__all__ = [ + "AsyncGridFS", + "GridFS", + "AsyncGridFSBucket", + "GridFSBucket", + "NoFile", + "DEFAULT_CHUNK_SIZE", + "AsyncGridIn", + "GridIn", + "AsyncGridOut", + "GridOut", + "AsyncGridOutCursor", + "GridOutCursor", +] diff --git a/.venv/lib/python3.12/site-packages/gridfs/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/gridfs/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20889d2882b7c0fa0d583b263c54573c82df004d GIT binary patch literal 1071 zcmZuw%Wl*#6m{m^%%hzF34{Q-VpWMIqDU-8Ri!?m0t%vKlZ_{nI2qH}j_jmjmV5|!AQCH9?0~AO)D^aqRyqcxsdQztiuR)t$leWATZF_Cn@j9m0NS3JW*(Po#UE1?{CT=BtI`9T` z*;}S7-U=OhLpt(C7KlOE{$_c1=AE@Ae`o>lQjqZA7!nD6C{rAehy;E{ID-)vP-a{Z z7Q@6BF@}8l2?v>kgu%C*#oV#&lNp9%%ER#_Rz#7Uz|;>;ef6Q^+H`T%OLUrkp&5_;bpo7WH9761<*}Q^m$IEFwR^O4D(m#Mno*|f)<07DY!YyvQ&;;SA8+dr;d>drw$jf8*qW$()`v0IrcAoAZu39^%>)(ZMoJg zy4!Njg3^33Tu65E;1p*I{XH0T)=Ee(AQZ4Cnr z9SzG0j@IrLZG_ZMLa4A2it;QM7$H$t&EipPPT2AFkC+M0crG1XL;9FXT_b5U483M9 z`qert1Q*hHDehIZ+vP~ke1St_vUpf^|1V_rX0~PaVYZ;%YwLcJAoLwvs1#hs?u=9H zN?%f+x$gxx)9ZfVQ=XAPy4BdMZ%avHrU%D$&M-T36Ebzvc{bzhx$|tpl_bOK)mY1F zR9u7zBN;+YI=}90svD3ecuPD`y6RE^xuM{9-LkAdk6TvbasaF+SB;icy963ky7~jW TxT@V${;u@9wJ&XPUoHCwi84JF literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/gridfs/__pycache__/errors.cpython-312.pyc b/.venv/lib/python3.12/site-packages/gridfs/__pycache__/errors.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..415e449143b818b52a9a95f42a6d61aaf9daba13 GIT binary patch literal 1221 zcmb7^OK%e~5XbGjn@2$igb)I`NC-u0cX>)EpjI?ZdLpXIEgUxPu9Ga?T?gAK-5fae zz_GV}hKi5UZxD$S;=m2ma_I?=-Bv-OhwfoK^Y>2t^UQ3HYPB-b&%#kAY~~R94#D}8 zw+=qu)y@G5P!|O_$V52KbTb(IY?O_2-5f?4w2p$@9t!d-_cd$tx_P4(v|42O3$ zl2+$t)S^+#TCL2erLBj!P)HxOaampw9~{x%)3 zpKyE~Pb)M@xS}Rcht-YrEl&o#B?K3~MYI;5EyvfjbATA?Vtv<4fV-vE9 zJCithxJo4>enh1teJ%)%B4YOmyTEihhq-CV^AZ{}&zqJ#PlJvl@T;EpaZIDLoucOj z-1odgbPN!u%pLNWxsuA5ChmsdADFMCyUDk>3VrFC8?3D^y^%5;Br!{r>+G;($Bn`* zca*9jPo6nXp1LwrZ0W2X(zOM08Qfetqf`|1S;pMbj)Xq%p=r()wR8PS^|{vMLX1bs zlp&1Re~1!HjL!Gq2i zb4NQDz%Ba*Y+ebt(BW<1d_K#mL>eXoqBs$Z2Ba@|tl1NuEV12CDqU)GkxKw?03euq zvw+JM5cDenmI1E?$I8je|3mtkQnB;usv%W+6oEX|a|nr-fZha9FblJws}>aWPJK>P z^a@kkAFHv@Q!BC{%#YF-z97dAlF`->+?FDE68+jxH)f;NU|<;!~rR={uK{kwm3(8LD&FVsaP AE&u=k literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/gridfs/__pycache__/grid_file.cpython-312.pyc b/.venv/lib/python3.12/site-packages/gridfs/__pycache__/grid_file.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..218062d124d360ad7ac7b7533d8a829125e9cc47 GIT binary patch literal 372 zcmYj~%}T>S6oqG!irP}}0i;BCDI|jmu2iVFaiNqhyBYE`Nk%hs!_1@*U&Lo{;gj?Y z0)ike+$r64<)lUM!2LM~?){d-VINr?Egk;uAoS6hKUv=iuST{%qZG|iic_b=!kIg` z+uhQAwOiBkcb?8?Ea0L7Wjx3{-N>X+AfeV?u6UYhZ!*30GEiOuqKXWUdC5%^esu^R zw}VJZFg6FNt;OSezt`?knnFTp+ov?EO|2NE%C!gW;r40xj}vwg&3MUP(5m~oE=U1_ z5glm}NitRNz!Ia$3}P@m(c}(f4ma1qL~EW)!K5MKg2@Fbc}%Lt6d=#SlT)I(VL=ro vk5SGv*^)P7Lkp{By>}rXtxI;L1{REM=>yxoU5xS12;s{&bo`0NYTt&x)KhS| literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/gridfs/__pycache__/grid_file_shared.cpython-312.pyc b/.venv/lib/python3.12/site-packages/gridfs/__pycache__/grid_file_shared.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ab2a158f63b6ce6e5e91e9433d0c46ad5a4c687 GIT binary patch literal 6604 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/gridfs/asynchronous/__init__.py b/.venv/lib/python3.12/site-packages/gridfs/asynchronous/__init__.py new file mode 100644 index 0000000..0826145 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/asynchronous/__init__.py @@ -0,0 +1,42 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""GridFS is a specification for storing large objects in Mongo. + +The :mod:`gridfs` package is an implementation of GridFS on top of +:mod:`pymongo`, exposing a file-like interface. + +.. seealso:: The MongoDB documentation on `gridfs `_. +""" +from __future__ import annotations + +from gridfs.asynchronous.grid_file import ( + AsyncGridFS, + AsyncGridFSBucket, + AsyncGridIn, + AsyncGridOut, + AsyncGridOutCursor, +) +from gridfs.errors import NoFile +from gridfs.grid_file_shared import DEFAULT_CHUNK_SIZE + +__all__ = [ + "AsyncGridFS", + "AsyncGridFSBucket", + "NoFile", + "DEFAULT_CHUNK_SIZE", + "AsyncGridIn", + "AsyncGridOut", + "AsyncGridOutCursor", +] diff --git a/.venv/lib/python3.12/site-packages/gridfs/asynchronous/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/gridfs/asynchronous/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5148ab3461d824141b9cdfe9b8de6843c10432a GIT binary patch literal 910 zcmY*XO>fjN5OuP<*=@F4)C!3c$Q2h>qOl4RhgDUnpHKm%qU|LYuQzcL)5MN!r(%!% z3hvzaE&T_PIC0_zR25QB*qfzVd>GAmJny~n%++Av19R4|Tz0Pmz;_$`J8t7R*C)pL z3}O(1*oocTDcsO?tl!F8MLTR4ov>4Qp;vUnZqW;Svs^o0D*Vu&`JKF9EQiYuNI~p< zal*m0JM58F2Y{E7#fPVmDM+9yX~Ys15zPdLiI7lfAsJ6$PGm}gI zH3?=^@gxv3MUjvcHR89&IPibiPaisUpKvZ3*=pFS`#WmNqegtax)AJE(FN5D`6FI0 z-{s#`x?Wq1_A04_H2B`JI5h1UZ*BkJaOeEZ8Qyz!e*7Ar9)$Vs&Rya8aqi-eLQz3-JGg)`NK+ zl~PDGBd;IK@9xU6%7~=#OqJC@@*Lx@;Mx#_Ta7YNP^5?|2uCMU%(e!-7Zagbq|kg^ z8#_v|l-q%!;F9u7l(Pvcr#ch-d9e8mDW>Vhe4W&ML};-cgfZhxV;q#z+uoL0!z!oS x^1cyaHXEoX#{6tKj`Qo0=d^EDfb;lI+jHC-V2ugazkwIO+r7T~sVmpaz+(D6`M6ISI+Ym`@mML3{7upU8@`4g1fKV?$No1(j zkux1RacimRL~@)wE0au3*KVV&5* z^M3zx?uLb0T%}3-$nbe@j2q%fcNI|%8 zq%iCpafXXVio(Ss#bMWoi@mppO2VZhr7WBmDhrp7l(VoSR1vNmsSH<*RE4WYs##oq zs3u%HQX8%tsSDSS)Q1~J8p10^R)iZz8pBN^O<{3F46htn8EzhF4!4Z7gj+{i!)+sN z;r5YsmcJm>5$+u647*3%;jWRc@Tw7fJK9?q>JIme^sulq${m6P2E(vW2Zyedk!lj{2;Ri+@2=|Zlhc}OG4sRLR65cwpHT>Yn zgY11-=%Mh#BM-B1dB_{yHnJ@|FfxGO6(fUEt5o@U>&TGVw8JD-ecmKhzlr{*Yh*i% zt3h0?7Po`N)gi84i`y9(l$`|wEuh;7nJ zdX&Y9h+C<}4YRmr#I*$0Xu0=JTik6ko&%9cC@M}wWKj+rlLOJfWGpy&NSp|U0#Pv< zi^u^<3{HwWDzvu6%YPXOo^iNJkV*Xe#G8rAlZ;O9y+-*-;ypz)@$F5jF_K$@E zsk}%1$Bv=2lw&xuFA$rOCsU5S$0(COl(LTdqcLQ)?V5~jSciaP@7R&Rcx;#CwxnDG z5m}x(7Gu?-DXD_(xDTBSMq|;GeKcl*>1;^Wg9!zP(Qj?b|uz z9mK2c%By_e{-L2qd;w-4>(CGAg@XGHJc zH8PYc8ywp1J-Fw9QXPx44L$nUfu~cp;h`t@3?rN0cL=@Z3r_lu$&q6LIX0cDP$MH# zF+H~4Hy#T3WgqoJTxV4$GM;(SqCk!O)aU4O!tEKO7i8>N`Q- z>l=@R!~RJL9TYu=Sr>4dQ^orNe(5neFcFXg=#5mNZ+}2O9*`f6NP+F+f?*lalEI&F zV-o`BO|vGsX4Z7XkU$N}g)wD$nE+tkRLVFlOq-@nZu9WWTD}lODT3vJB_Kwpjvb50 zF)=(9iUoV5$oNzk10cp^|76t9x;#oP#bhu<$=#^$4Iz~m84&vKMFVg z95Lrj0n>;8lw_6!prB&OC7C~O0}?8cEC8N1sZ_G!X_v~Ra>vGoXhTu1bW(#h8EmqDdb zF*qTP0^Tt|h+lkx`_qP3D@D!zi^rzJv@AVpMvpqo#yb0t`DK4tRMYfpAxsi+DjLv> z0#bj!xX!a$dymiF7YIj=1ChrL2k?bh z?7+^xu(W=4uN- z(wel!OifM_iQ(6lii!n@(C`~UCm`$z(H}bDpN@($A2$D_v8(`LA&*+ny#VDqHhQD6 z9EOso~-`k56ho{CoZ2d@Mo``&?cN_$#g0UVP^?9@= z&S>ikm=>Qi#=gb~chzOr&Cv2px4PDNdiME-Z|9v0qJn+Su)8Q#5CoX!U|enk)Jf%Q zkRfFy(o9}Ku|;Y9ka8M3RCZH}LX@hKTPYhOq*Hc=Y(cu>gC~zhQ+Xh5C;(E|8V!Ud zQVzAbsRBKB6adbIr6{kbaveU9l);$KH&ek#HV)Z5YV0ngiT)C9&znB(H~==|oR zS6zMA%If2dd;ao3eBa1ld?vo<*?7Zq3uT{)J3sSbxo7U=LiwgMj_a=Kq^m9AYD>EM zu27EFc-#JkvIBAFf$Q}x7k4M>d(T*}I%*cH>XKFMiK_NwRd=GQ`;7Ia!o}+PWOYZP zx+7WbNmP5zSbxPSZ$ul?h%dreg@oZe2&7>+(-95B;gxbt2tnY!ZwkLE><6pi|0^KG zG$>C!YG6t9naEiTtSW{KGyu(KorfNrn zc*g>h9ud5acr-A5A|gvzxQ7^>7ZpzgLm?#73OEL=!$5@`nL2csR&Ree5Ic;ewOl(4 z>w4D&2hpR=zM#}RI!fdNEq?yVQ4wU}6bQ9xF?uX89-IgUq;4@tv}ZJ?HGqgxe+Xz% z0$3r2$$joo61kO5!G&{74V9X)}bi1z2`7vd;cv&RWT z1376mf*jTfs9lpXk(h3?yd95}ofjCTX`#(~pmsKJ>GNKVm60@Bha2X7ovEb$!n$Pr zss#SKHq0HzE$-OBr=)SJ5#jVjnv(wq0*{!4yPcM1nwINKF9?$GSuD;ko6lQ**=94H zvdmhJXoMMxR4$z?djaGc%VFU;1QT))>6dYhgFdEzD#7;xej95NO#4k6O)vH1uLXbk zEv7Xlmd1+Ku(CIrY^JDi0s^0>@m+#xS~zKW+H^wrEz1ePZT4SBvmcv^@hPq`_M%^1 zHZ*32aUS}GsUTQ2#?nLp(t)`9`}HL!V*bYtixW|gIvkx{U3?7PROUp#Htc|7xF$Fr z=_GLH*2`hoYE)fv0hAe#30-MR6bnI}sbkZzKom;@3D^wl2?dYp^EtwV2qHM|<3Wj& z8Oj2rB{3?}VvI;05lao_kAiQEP_j{V3Ty3EmpIXZd{K9E>X0{+5FW!qTD!m`>oBET zWROHFO#u@?^r#Q;0exerdn|zE5k{cm)G;Rd;ouk;en4lnJ;QFR{0IOG z1Fb0=qgYZFpcKS2Fl(i+llLG=Dqq8kQGgH=6~W03%dx503>O~}!i4Y-5uZbnXb`u* zGu;HdU1zFjx)4h?^(OGYy#LY$+0ZxATlX!owpn{O4o9nSf-JPLQ`A$~ZG3n$?H z0wDxKCwGSshzEX4Q01x1p@2709$i2H=5#DEaxx;}a@gq7w1?UA=IN^s-gA0fK1SSo&E0dm9 z0%lgf-oiAZ1rzpAKqpR=Op4g6U-WA&p^2wLc#R7gXVOfclH9`umqrO^IvUms*J8AW->{^d8kLPc<4eg3aqi?B$|A~BK3!gi3@Pg&h;}oV z5I#M92_fP&q>d7e%$E>ImWB(1$%gJkL-*XF%O|e7cAT*;*-ftUmxC_`zj!q1sEs>n z7hUCN3L$CBue)`FgdnH8HwwQkthatEZ=*0y>sGypq+o=N732<|$FtT`wprnG_?xv! z<}FBIkTF@_#I&Oe1W8V6JSSOWo3*@UQAw0j_F41uX32WWJZqM0M^UdlV315{QX|Pe z1F6rcyjlAZO)ACfrn_WSTXLjOk5xmndD`0|HBNs@d9h;km)a`9G`a3m)ugreNbZ_6 z8JAvDzA09&)<0`kTbfDoopQt~wN$1fxg=Z;^s7UKC=5C@w3uQwYOYq3Tqc-e_3Cda zf7U!JOqgdd>T0c$aN2yrWFYwp{GR~0-4?RYM4t4A1B~O4iA3@ff&efGge0SQM}s)3 z#3^m9>(u0l!x4xZ)MCN5#2`3|7SI}}YgBzR3K~oTr9`PSYUP<2_d_CfEP82K5-r-z zuySs4ad|Lk0A^2c0h%~D6&?$au7jvPKcsGxkgr3d0x0MwaW0Wo92<(>$95@Hra_Vd zNUexJ7=;-osxAhp9K;_A7pL?EE9n=e0`j!jsgS#4(>XgRgsj~n3eM=DZlZt@O|1-% z1t%3T%Y;@4lm>_0-Bi(}He92lJ!=5=RBDtyCmaEbCg=45mSH3vh@%EtNHmPbFKnKX%nCVp@f3aLaipEPXSE57#lb)2{0~Y z1Nx?=&+xapEHfn!9Er0DH#&^zII4}4Qi@V_zo8AROQ}GjS%LP%PAM?qhmuSm9s&h; z*|UaCQsYF@%2CJ7omu03q#&pnbSdAWQt=Z(Om6ysD6O(_h*&_7scXzo-lu^ zfOqjAv?y{4r%G-aWpir{NU4Z=hI6Tdi~?*YJC6cZGD3#;hpux1I?y)Ey93keb-t~;U)p`?}`G2o9hR* zTHf7iLCm-Xnbb?8L3bjC)1YReF4gyjr(_{6N)o zkpkYd@<-KWDt2ox$5`wD&k^}s@P%|aYZYo0Cj;WwZKkR$l1NH6qP3<_lMTqQI* zQ2lCPa;713u^Zq11ImrA!i}%^PE%F;*#pVSwnSxHymJ5C$+xQ$mHXe@j$6{TKknEc zckRCwWgJ*-v+$}QTEA**PM`h6jlDpUgg-?fOOl{*7x%(%Nzm8_N!nmmc@C@-ONKPT zluMkPo71{;Y10-6m%H(o$JUcl#UC~kk-ls*w zRx4XVnt(%>7>_b!6qJlq5s)NQ4x~SZwvjY4kX?+!hz52pLBFp%9-5o_H zKL)_SrA(YQi;`s4W+0;qcyT_E+VT(*rwWw_=>MkO7ETmBO{I|NDP2o!=SgR{IiMoB z<#ATv&nUrFE_qsQ%5UOyOmW%EM_)XeEN)E{w_Xg)JsEFXfB8hbxOJg;N7At)?%1)2 z6rmSG$>R1zar<1sr5*8(EpH!+7q>4I4<{YN3CA$WrgjQ97aha5qAO9wX^(fMa7pm4 zuztH8q4{dT+hm=uu~WE7zFnVN0Wy~51p;pL#|W%IcCkZIif9Hx4<6K=z;q_mE1J77Y==QD)!;@Wz*&ZZFHAi$Qy( z%}Z6r$w$Tr+Wyrnh7fw_E|&ts@=V~0X-)QqoR4t8MhUQZJf==|E@EXp4n$oiFjk*LgU_K&EEglW-TeW zUQz*3YuwfJk63hF6~gI zENP-JLX;C^-|P{$BWd8IKYT0{FsR0XfJoKbGZu=BAMF>pd9JSUCQ_Gm<5tq1tKZuB zS)Etu7n?Dc{hrv#So0}^u3YCGz$`Wz#|s5bU#x+Rk? zk5F2V!GqZd4X0m{f_zJW2&J_sOtKozay@8rmc+tg658#97~&?V6~iT09#&KYeccVpY6)_gw3h>fM*~5PEw6S*Q^GI90zf7JK&w1RWQo2Eqku!IJtA=`%&Lv+3=DF9#)NSw z5QaqzBqB237GzkT;3#5pB$N*E3?L5>4X}U#RVYIV6eyr=S+J&SqSaZM{hW;Wc$fHM z!EgWtZ#^JS1>8EH$g?m8bE{Unhd8ZH1K2#lm@}nA(YxGShFDWYP{U|B9pq+je$u)d zTU%)9Vtk^37~>hU+DK58nh7=5G=bA#n0ATyX=jLTY>53=KU}@i68LSVe;B3U1<3c9LbKTX2ym4ZCVc0p#O~6b&VH zx;-Sh?QGVC#Le9h*tR*}J2mvXnvzYVwyj+9O&Y)`IlUnOsO?GGanC+Or zkrVhLY^4YXLw?y{onYus)v`%7$v4dV0euM&9MZ*rs7|8_Zkx|Ys>7PepCj8Htrdu9 zPz^-&X<(#~h}H>#!zN2BAYiR&3Z0cru%IWn(Xx)J8Icu@M#h5x9Z;`w2_>7YY+IP2 zV>4(8uY%H51%NuqY4aE?&X{d1%|E>^T6>u4pw&ZD5;Mysl%~iMusSq=1F#baAB#^Ue zAf@4bP3ui>x&D1h2eIkS>kj)*2E$WfLmPOJB-Kf)nis4^DJ5k=ihk1JZ3j^t5Id;_ ziX0i5TTl~PZt59rRw!!=lW9Bz)j32?)2#7Pw);)=PXKF(YgUVZB2$Ds3>rZy2fAVq zjK{I%kSVD#O4NY_tvHCttcMB2CIPB+5}k@6Xv|=gf@^r~%ge&Jii%CCw9n#SLk5kI zq-S-HsH_B3Lh~Ac55y~=2mkWyIRbD+G%@1>F+Vy%gLQ*K27F`NAF=$fw<4{6oo42p zh!UV$hv1B1Gseg>+Bs{7jLO>R=1NQt;_IJ%?3jI%sYY+-eesrlAx3Avt*J~%nn00=sZkrN;J)O(YW)7Ffum3 zs@pX&+5CrK+Z7}84THKIQ;VAxfS&RBA-Ft-9a+#yh5*eb$EST^OlY==7~~VpL%Q^q zfLviap!z)e1|Gb<+bt@D9PYe9g;J$$Pl4)G7r23sn)$8vyE-bP!5ynL3D5)>*4$nZmA(dngsA?sr=%v=M9Z zSD*+bR^mekXn!!lPemueq~N3k=krxXjkKE7CBR8=BQigLb;gJ)OmnusjjelS_lLI8 zLWQxM|3!g8gC)c0C`7mrc`;xE5*YB9&^#d(v_)l1UmXWROwNQlu+A9o}u#iqg_}c{4H^c>-fVOn24)HJGq zgjbzeIaN$@!>!F)S_9GQ#cI!L!R_jfM4J1>&o;Lnis8AY@6@s%JrRj)HKa4txlb{p z9~~u>i&?|wugcT$smKR_%&iz@U{qV9xoigyAzB(s%vyzjSVXpFw=G*^YPx7iv}0IIB5X6BrNx=c%PoNUfi|yeCV* z!)O#mbe3V<%;j%t!?ACRVfg6|M$|CB8-@$)bRfL(qbxB)_+?Rcb$ z)i&{A(Cuqv+D#ZGe8QF%7w*{kEKAof@wl%L(mIahT;Ju8!io9+W7X4cvaUz)t+R@o-^JhhpD9Wg7wU)xT9goW~#0?q<--7(B+Nq@ze*B zB?r!Uue(arwE3peM}?c$OAaV{r`6sr;dcaYyY+Xw5Sm{hcss50jrkPr6mW-M3G+l0 zrUX0C&|4g7!}y8q`F_SbIsKeWRx2qBcJm++ZIO_v2U2!U6|;R6kcmC6zR;7%z`8u~ z8^Z7#c9|*YC@<^LTMeq`lV_N=BF$09b389uM7b@)vJcN;cL6hWxK*U(LzAA9kK&yn z4p#cl@e%t~s!40Fp+NKSu-n0nCZLAkt&x{Yoz-rhl0GeECEGuA3W>t>=$U+?wV$EJ zp~w_K}(@2UOH^Xw7n24Bf~GMV9Gy zf^MIs+w*h_(Cy#S?HA}q*8QBidw@c8qm>qIzykTa=>zMQkF6cH-W&UbBHP9rjSaSy zA8#qORV;OxS~?b2wk)n*x0L^YZA7?U*0f}!0K}LG=CPo|ROaEYq0B0;S}J6*PE$eE zjUpDD5vUlHkyl|mz{{WjmEqxWSZm0b&teNGV<81U_SD(-3LkH(uoWz=Hmz)9MYP*0 zu9vJ>vf-%|Kq0Kv@}+zV@g`6REf8-MA#~$0q2D$nEX@igcMmIMwQa@qnzkhyo=PDU z;*FpX+EDy)Aws<4sJq7Tr(KFlOt`TdI^zsSBIW>SPRTN1O?LyL{(|!lE8KS2_;0CP zjn|)2g=9kt`|>I14P1^erz8Q#kut4aTHp?OtH@o+n@Z}Df=^QhYXfs_qLl-8CQ=Qr znZ2!L?oOP`m2OPS($|qw6s@$P-1*={rJkIlXz7Y`r^{$d101R7d0f&8I8U*Tlps~3 z-eT5@CaD;`U%FhW**RD)QF0l|FH5hTrC2GIpj?c1)^|5crFc^T4i8Qg})Nw1+^6t<8a;5ycDnjXNQyww!7_s2_%6*dXh$->;;TCnTJen*cj z+U}my;M~%DwcvLxJ_&K!UL0_3Ip+3}3bhOL=aFhXtcGT-v$nUaY7ZEFwJcLEZQ0Ja z?y6jEr_ZbfwLmw+FZ7bAhV|!>W<9KijT>xyAs?jM9Ott#K*FFmkk<9h>$p6{55c4U>p+-3Np!I^-M z?f>RQu~_H?W|-uez0k|jK8UDSl-sv{{UdOq!f09Cl;)*%0xMNZK`B+Sf!3#K={-*x zT(ON(C7}J8P<2!>9Y`xIO};C_<4Ia!G$Dv}$ZBGS+>C!ZMM4^?3eyaAr&X!8BVD#k zb!U{S*RJKy#f1DzaxBCbCWM8`o&qk5F}Puav^m4QkoN5udeT@J-3uwj{j50|QU#uH zvy>*8y<3SV*DT#x=E>8gNj+!Qs$gI_r#uY5s_Yf_%riXG%%(lFiiHP5J*xrWzySOq zKKDUdmhP_8{HJGY`ukKTIJvZlLQ8kcUZP-%ssg{fY}mUwMf8{^?nh?Ae;=*_!MbO!N#Udmc~pJib`hak&YW zpPqff4U^eZ0FOXU(Dg{#y(Q7TCE2|#(Y-C%y*JUlS54aU7$xnYq=iV@+()H1F4ok& zmj6nAvc{dLaVKlmBx=^cl~55EZWF(#UKD0z7_o&$GK@^*RpJ9?q9AX`pcBMX&P~md z`KxAFyIHU)+Ja5d7Pcu`s|R_Eq@Pmv1l;4!T46nBQ(P0-{Qm}7_F_jk#9ZS37^W)O zn?uh^5=EmVjbzDu&eI#RqJXZQnH%7@269MJ2{3NJwFi(I7y%nq$mNxyr{eNTMXJd( zCroNQemFwAXn}$N5CFS$d7r_6BRENBUwo%16Ow0cyH)WrYl|uuf;LQRB(n|S+D0Au z>KJRg!4%FQMis^Iv`-onxANVwT=oIu0w^bq#`F_Wq~HR24s1JFHeIf)yPE+}pY(C7 z1oBRbuJ6>^$k&r7HdFD;ybneTnJaPYx@_AMbw3aUx5^+9@diOJN;(QWWF>x3@8pNv@yty5?)HEA||sH_R4 zlk|C}WXv4YQL$5j(r%?J%}{_5hh+H3X9Az-!E{Y9)uz*rw3gUOnywX{i^BX=V(!lXNGqGh;6P6s zu#`jX6DA#e!zyDF8>G2}y}5++bqbNRgJnp;;YTc*W#Cwr4Si->vvGEI zDi;J;(Wg;5$KLrh7YZ)!yz1zISgdyC_11Oq)`t@%;@eVk`?HDd&;BsaRsuu`(VDY@ zHlQ|joEbb@mvA0%=-jVM>12NYe(SY1feELulMj4{3_fCy~zyn)k$rE`va|8A`So|6~NJ zm4>zdvx`7|j4%#L*@#qi%--qF#+gPWt9_>m2lbW6x%QtUUC*a=+@N;peMP!V>q=B6 zRs|Pc=r&Ir87eOs0ZIsmTxz5Zv+8P+yV)RPG{SJI=_xBjOaF6pAM^hl-KTr1{`BbH z!uKx_>X(0$ZW`K_-@;FYDd%cfmM9&!%fE$0SqylpE*opBfN`e#Zg9Ms8s~wSqR*lZ z;QD8w3OC^Tiu%PGF&o|I+Znnq{wJY(k|?}DiVAGxQg@q~jv$NB{%viM&C1$2P!Vpk`*R`)i_FbqB=%lOUkmT248-KHzJ=-l!*B> z6}c}{ovrwZbJtWgS=_J<019_aU%3p6eH+}r4C|XoKiru>Pzm@u@Z{M{GjT+^^^neH z(>EFiS~(4X>1tGt`d;#LSp0`by zqWv~-jJ%g_a1v>fopdAPH5H8$22a`f9G25DdCCHpBH5U{EE|C3;?lEIeU32q3#gB8 zP<%o#Rn#QQyAtJHi(tg7+UFdXJ0SJ0SOrOTMZu3umi*Qwx?L|SSuCqO?>^_guzjJd zCFyL5J6nE~hc_(E#|7Ao`0G0_jK6m1l|u`z_PC>csSq!3g8MJRof5+Rt(yFY6#8+L ztdCCDc^?wKDtKG%U$Y=|vC`XR`H!%1v;V#op+77{+`Nq$y4jm3+zvxG3z@qi+-3J} z$(vsdPq@zcHF*@?l!y3nq7&3hqZzIsM%kgh9L)fegjw6H9gc4md3g@1A#XPCh(#@2 zf6i*=2=`7kOq+rGJ@~7}pX&AeHZ;JrIgJLW0I6~Cu4n+h4TbivF^6x2Ex;Wa5ysCc zgX|@fDKl7JV(_y`GyF>-0{(?D>^~>dso@GiU4gIhN0dSF{=`Uzw0&a=TWugBe&U>b3-h_F@V~4@;PB`R zbZG+jKXW^F--xTlWjK1HGMj0?$!h_VA230h6ge?T9;HpSImamn zFN2K*v({YP3`v{m(#lXIpr>rQgmc#RrXqQb3X^5C<|9U~i)T0KX#~?t8}1}MWqi;< z@Ib@(+laryJIg~ec%dmkzCmSK<`!|@36PPjQ-gfWuqmS)z?xAB#y5&rdgChmXMlX< zsw@ZO(^`E00bh$qb*hVAG4(QfgJ7?=k}2wBXGeINes*+soETfxVT6R(d8@6ISR?~v zcC+%+l9MD8W}ZhoR~w@Z@RaJwa)gtvLA;$w5Nq{60~|C=74qFc)uu@qH31{-V^&0r ztdp|^cR%|e6&vU-W9g^z2^%x{cgg~-04F{bQxqaT$?2!Ig81gP5sJ34JlQrW*)u1X zv@1K64d5X95=!QzX1}SfFi!#^%>1 zUzuEJ^dxIMXKmN2>n^PS^_j(r+QpLkWXZ}z2^^wdE$Lj!Hq|lF}5*l>6dYe;tMez1ozv4jX-wWO~_HS4a`i)BOM#~>Tq-FnZ zGeYx?g@~MY3*Pls$h#=KMxgszf!?lX;f?lf`FZnOg>5G1{DXN2zhlconl!4E^k6R# zReB47+qzE3X;peek%%F)cHP-^GY*c*X>a;zT{q)gL6V?mT+!l~cASlbqkxP@#^7{w za`(I)e=C`D1-t+2K#)6va83nK&(&(vCe|Ir1(dDfM>hXt*+pB8jhT}`VD?Y{)8KYeq%9K(E{ zS*`l39#)w_n%Jv47756v6NL-S_lq4@!LXcj5_fPi}%Xe3oV1C6IO0^8ugRx<~n}PHJt;)4f$WYL&dqu)T}$*6aevD%fC}WNvHWso8qM zbjk)lG`YcZ4&ocN+h<{)sI)+53vA51fUyO1pehU@Ou0-HauM`W-rbdnzOvsDR6Ayc zSIuA3`WK+u#z1twe^Lk03_^2jMn20Rrod*hy9CjlQJs4H$v|ng7}&X3x3&DwS~J<^ z|Nawx*i-<1W{b``?^Rl%mBsrE7K&_>qOlyK*Ot3h5$IGiNdgWrEJ|@~-y`>wVUPsf zwM`s}67C43@0V0n-Bh;Dv!+i&O)F8zm0x-!UcTvOqI}aQSLm>H{LoAy81AQfB2TMub_n%5So=P~LN(24%`{MhbiSPSNywC~U?%Vc| z@Ox%&z7?wDJo_ICUR%K*SrGc;Jj9U_InO?yCs5eYjimGI2tmxRcO(2xqp)qI^_?aW z;rs3**9zdg9bSZ(85MW6!ep&9yg9c(ki-GDCk&U1qoYU%r-$&9q|H`MWT?3!Wwt6m zlMZ$DRxeBOPQaRm$yeDf5^O;M5F}4=|J<&1sroFD!q0u{a>W4rZ2jD~uECRlzEMio z^68Fvb)URO0pYHHrM`9d2mCTlY2>a%H7KQ^|1n}o*dDXYeQW9_Ou#!*;E*>|+Qp_7 zK>QlZtt)nvG8Lb-2S9{m`>f4TY*qnTzE~AILhfZ2XDo!tuN?COi_NU$@LNhhVo8C# zFuP?QY1uYL)%7fWL=Zx1Yl!rS&QB>=AoSew4df%iMxyO_xzGk>$x$J}KsJU=QLM4ZY=o@5p0 zz-ikG)4qzFYUwSNPXkNKpC>8?B!~&M@qOV$7R^*;lP!b-#)w!EkpPzF&=lXvStv=l z)JtRQ3cyWdO1#qdRC7tzDQk?{#V*~Ri?Myia+MUh*dy4FS@eT>unQXu;8E0$L!)(9 zG#bMrS=ph{VA75hDuo&zlBQqDby%~06!Q_yO^lvdvd2o)U#dQc9UA>rZh8aJsC~xPe<8EzL=hX2-3;uDfPjncW20R_IVp$*YcizxX6`EwBE@S;3L%vpidl*aohlIsT`y4p`JC| zQI6rH0zhy+Lvem5C8tYi|4%~M)IUt;`-%IMN)=9SP@`Cy*A_rj-jpbz!0cnk=glm+9{Pb=03)7PHDM^%LzH_Fbv%Nifw@}GF8ToSc zWLiDwa^C2iDYc~cb*nfNU9dxt?1R46s$$)*f$m;M}4 z3_nCgT)Z)C*q*jnT7TiGcuD&i+qF9%b6kGb|Aj4dxbev^ZN0cAQQrEg4>#^yaIQ)^ zS0|jSFExC3`l@pm9d7)LaILZB;`Ud=$(mJXb{P*h&X4aOxmx~A-1!WI8@1i)>Bh*w zPdDEA_Tl*Ey@}er3uTYRpBRZdN9atTXSCCe*AH5RcQzFdw3*(u3j;0Ick}8g+$H;>B8LFX&_TlMh`gR5z_mu_+P8i2X^$PeCAp{JT4mj19VfHiw zkY%=+En@?sg4dsu6}nvU=VS%ZhU(1F`{llo70g@`3gSNl(3by@Zhu6#y|@{-u*u)0 z*!Sqhlo({vBmXDdvXvHf*|s_=xX5KpzI^H*5Vrpc>fjbWPYKM#r$15KkNs?C56|TR zht_R|9eEu#v#}j*d1UK@+x4Q-d$sk+L$)7LURJ;ol?Q7d{JsfyZ?(NQm+~phQn1?( z>78QlQ?so|_=@0NZGF{&&^H8cpZ%K_gkG=oZnAt=@UFA}r)Gran+g#*-z9iASm#&S zDcmQ}9m)#4-N3?|?Ar?R<{uQc3C{V4@(_NBq{Yenb-36 zKBUy9lUAWtw!kT_P5^LKwn6~VfhoB>%|%OeNeEQQIP-PFoMEqH(`Aeb z6vwx$tvK+BD{&dboY|r%jY-Kj1vh9=b1R)9w&5V1Ty{IuF*KRlg6XHGPaT7DDnLgD zr-~FCAEWUOKkkBU>`6I|9n4*Q;edzeR4A5p-UV+>E{h)i=EnqGPvYA+FXhox#W=&_ zv*$jWtnNuv_be8bo!t?4He5K4)6@6FJG}A6ZSU;>+^z8d?$!|8Eh{)<$4=mqs-&ys zimL^T%#v9sZCmVIcj-{P<3{|r_ye}$!^7`)Ui53y#NmWNn06~#1wRj2^0Fr3WEe@zuIQIP_tn9HI##WF%M zflwxFDo&pdxy)M9Q>*|t9;IVTIIQZ-f>oYGwP&%Y`a(_I*?jSdxg(dm<2^g#tvloU zo+e<`yF9R3EbE?oHr~A@zU7(tvr_z-L-CTsH!NlXSLU4Hs*b=_jrI3iymf-xDgOuh z(iiFW8+7{u-CoBnWx)|Q%x$vakQgRW7>G=c!+-R1GPzTpsocxX1oVfV@$F{{%13ed zx^k}0uOY!3mSLQO!_U;wOP9}5JtSSZRj(EBD0coq*GEU%bc=L?jq!w?FH(j_a6_Bf z9#N7FafGI|Sua&yq_=DU*@-*G+-!hZp5LKFtQ`8{JLD$M81CYnb>CAt)G0FA{mZ{X zB{~cxG9HDsC!^&o&pAqNH+*ugM88gn%*shd%IQPwTN&%~H&ks#wkIfuaeVYvESrbg zlh$&!URXUDr7J1*KEHj+O8Lvw#Pf8!K(|-uRztU4bYtF1E>UPDZYdk>6`huUk0OYd zU^_hIKc>(Lx_zH+0lG2v@GmHIg>LWD?HA}q+u^vX_f-nf?XQ3aqdP%furq^jOiqKi zDE8qOTS^A}7oV0Vp zMR9{>)2d!73I_!huCr~&K|z&EHVUAy`p%_17Q`7nIBbXo3+VVDmXeP8sk~9dVqpWU z6;sqt#gtYo*(iWwaL7;|3!+ij3BrOnFbKu4pp!~unW$ZrH(V_Cq+qfa{@Z6h_Y6Kj zFS^&(!5WAs3Pv4y6yoJlh!;yCUMhu9XxT?Dgl!u za!@__6e^(h7gESc%`c)*F}0p@E|s8DKPp8?tEY8I5==wFE@8>iXscMPuH(-(FMD3k zue@PH{L;f}W3E2|@$^cYo28_uyPN;sxSu_*=NElsL%dP}LQC7#yf@6XwkHMU4F#7r zC>?lxXxEaZ&gK^uYwDFi`!*h61=1@e9}8~kp<|my1=EP|8MJ(z?HQhv1v)C|rv$@%D;fW5Vxk({@;uD4VWTFt}Qt?Mc2q`V0I0)095}rj9 znry|aOX%6v!+&o&z@GSm8#cr*4GN~Gh3AmC9*O8f>AA|opAQ^lPbB`>hIqcnXlae( zPrC@x;Sz3?fWXwxmpW*Ygb@o)5bDo3vuHCrQ!4AkDQOGNh_a5@q^&sb$vR@k(}8DR zz#%<|vze@K@@h;Y`DRm~Kq`<5CFfg3D%CtvC|LqdsbC9IvG>K2OEpUvDUwR?uGsLd zRC`y<-j!+Z%B2eJor}Gz)ZSG|)u^*ZtFuI^MY&bl`#PNVWNcF@d*7hFUm-Q7>*~;JFshMro(C z4$mfOm$V*FQQ9qSz;h+uZ^W}%dPLfUXN$B)dH~N>oCWkK!}c?c10if*B1)Dk0GJsj ze(iuG`0e{W!wi3CZJ8*fY4u+K!i|oXXYa$2Q8@0%q0A@4Bj4ytdNoc36QmQ#`JX)SD2-y9*_o^j2Z$bLp$ zK=5XHr_=?rn*8rBQWruZ*ql)%e%?cUPRK1W-{`uF`if$0Jgv!eq+Jc^Ppwz)zC;3B zcb~fCo>G@eW%p2a`8}nsICL+)Txo3gbd|f>{}S}Vj4ctBv#2%LpbHtjw>@3=P+$@V z6_G_HIa;P;bdB0iegH&_9oNeYAIBLy;wRf9PY*jH0$YmdsB@-lC0C3QNjIU?&I*8y zqUL-@(I#>zX0*Xl1){8)tW(V7NF)Z?S|}74kCE|~W+ybMNmO&5SaEDR%v}v?S-Isx zZc||JUTI>45S>V@9{z3+X~PJ95Ee%f04;U(IX7>h2yy)C8(0w^28y7@K%n4eVGN?d!-cO7##f-q|a-zADIS z2xY2*qh@OQ;VB43xV)P-KG5jW`>afguLE{W3r^BTQk0}b`eJl=3zc@9^<+304)jo5 zT9a>IJe?%5AIHm~7`1Tia0ym4J9`d(y#jvNcH@bbrWMckIpehFNgN6f&4b?gbRL~} zbj+ZGP&y(oIUbQ9e_-XSKgjBtyH;moK%K#7pgL?2R_KQJG!|TE9%bYT&Kn}iMeRm) zW|9@-@qkPRg5a%M5^JXs6sXpE>1)1UBp`qyR3MvRvT4*OPjT?YvdS^$% zm^KEGr__G*gkL@sg=N9yG(W9gOot?$1Ty_&k>dfwXynp+{A`_mx6%0VUP(O$W>gAB z{bNv(gr)VP0A(@+kJ$BO5QFFXw0rTxuGO2npw-%YOvBMERKd;?pdrKx_Z!Z9Gv?~| z$8Z$uXs%r&Ohm8ai}W)T^(Am?OGd@|Idkc!DP@$$52>43n=?0SwV}zJ7XTt;lE{?P z2zgS4$}a*=%t3uBpFsnHCvJgBEt!W~oFk_glBR47r?Q=o2-6}@<>44RGHp%e`;JAT zLBh7oa4}V+991{TjyOU(c6gMuJyq<(iDXl9fX=6q0w?jCmw*T$muRXy{WsfnGOQk~ z*8<|taIhX7!Y4mWH#glj;Fhu+iyY%8?)B2oyvO$Kdepn`X?{dDNhg_{EM?~?Bvt6c z=f!9|ajNzp;PZ#{I?wHYVmEtU=cWh{PRd||H?9}dJzCJX{F=6<}|oNzXM=<1q#=r4zpd&Uxb#;&@?ai$Lr{Aqfv;FW@l>)+~p zvumMd{Tch2sTYeD8(UvH^2(99`l}muE;Q~!*k)k>R4}# zsh(@S>gYAb)Xoil>ybAex$4+tjIEw)zv@_HjH!KV+qZVVvHQ(ER~-)+Wh+B6BL2Hcl1EJ5tYg{bgSC)%HHY&j3ZzZm2VnU~7L(Q(N~6kgYu<}JI*Q?c*E z>iIHL+>+Y^7>WA0@^zRWwlRvFZgZ*tyu=Ys`tyZKy8M&{v?^ZOIo-)#Ku_>IUdh+# zy)zWVd3+2g1)_2fiyFu70O;&Fq_V+IG)zu8wo{&+lpvqI@u6h-|Dni9iY(xfM6|16 zB#364Pyt37%AcuCgBQkEUnZdOAXmVIo( z?RsM;6lj%A7p4{}+HOJsTC!@X)a0y2r0WTRB6|FIQ2Lwj(b-t`$wzcQTflzY{Mcct zX#L0}6p4!~ntrVS#>_?Ht?RCuiv?F)U5iy>vZ^Oh)w5W!;{4uodr`yO#AWGE!haA( z@zrEuP+bNKgVM4e((M-W_H?>sz#{wyIPA3kh8>~bYQp54cM4v&b-u_>;c_z^FU?n4 zy*>8%TEV-jV7|^m;T3rl-)coBa=C<*s|1Sg5h&i{pqLE;z1>*2&1QMWY)1GUt3Y8} z9)*i~>ApqS_K@|Rtu}!nb8>G4xQSnqGfW59zrD z6&yVVP}Ibhw{=+l-;sR#0e7MNe2 z@VZQ`m;V8`Hw3nVqtw-W|6U!`Rbkplz-}?2fSDkBMo|r6aO_a4>Jn;I+*GH0V>l1D3fA8eml?!D%l1`Z1 z?8r*7A|J8N*1q56t9{axm@ulb$?Lz_E6%1-SO^6;s>8j9vn*?9E%sPS|}b*I>zIU zadHdA@--%k89JDxSr zV;4ayzVBEA=TP|)Ifr`oR+J>dr@hu~z2xF~+bXzt_U7ToJH<5h-*K&=a1W+_`gEtY z4)7%0ppB>JO_&;ZW?uVEZegZk7mWz^6VTxBzLy>XLqy%3)qOa98^;<;GiAp9<8i-7 z9Y`?vc@vOuNyW>vFV4mt^_;SN4Dq*`50F%Fv;)S19T4C^3}2ZQRB&^XVfFl;GO(mD8b<1&|~|C2D}G` z2D>#Oi_B)FL?WQarkQP-xZblGN9?HbsEj7b{~1N4>?r0V45K2v`c0nAiE5{`BvVU+=i zDu@;dv}+bARlZ_B!kFPyQpN?{nJf33S%LV0{iYZc1^fa4&G5tMwhS|9Gt)w%>|K+} z31EO*QOH(BVxk#77CYLa9oTHZz|Q}N(qsZksba61fStqqhM5!6G(V%%1Bx>NV#yWk z?(@x58&{U$#b49%A{zX8(_*3P?8YxVbgjIWAA>Wu?o!1<`G&Z2!(vhC*#qZC&W$9B znik8e&-b6}zaS;b>EN4d#SQTl+ulBqSg~uNcz4{fn=Q<7W6#=AI)fQ|06l0Vazl;d zrf3a&XBXB~`jKBF z2-R4$F9%6?MAL!l=@@d)fxaa+YsgI=wU|t8)j3G5);$R5npqgl!cn2Zul*Hjn0e7K z!f?z9gh+Dw=+v=eBaCj0MQgE(a<&q+Ri~0lcctE}&2V6b`#7sU}y3OqN7s zhWg;2IM(R~?2E>rn{pRI*yW>T>hnRM1(6kW>L4z4^hxuWEomg&wcoe@>EQtbt@*#w z+j2~nH!S+pPT3e>kJ8M7z>ynK5gD2}wKXx+-_X=1i!O#aoF!-1o!@$HYobuRI0(tf z@EgO)?tujUw+F{~YaBBqKCu@hkJv=4_7jB5y+;ZEff|bL~UPC1YUo zRL`b`mY=8DERiA&u`{v_5RY)ROfwb2KyCapN4*ZCo;JiO3&wNCl`+@;j)wL*l*Op3 z#d`6K_2q&W3(l@fIBEdKUf%MBEno=Fcb)4>mNh5Jnv-QciL#!#sY}v#j(q#b+m(q8 zJK~<5?`?=5cq%^Pi|-$e@Afa0jU}C93FjENX=lx%bL>`>n4Z(#8gGj*Unh81TIcKY z5Y7T_Y`}gK6H^(mocn}T0S!7zFbb=nzNab8E}$NKKV7+p?Pv@k2k&mawf z){HCHa3W8$Y5 z2Jle3AbxC6OM&?DIC)_`q8YcSZ!kBQSd+G{4Vs=?XM(>~>{wr^zgI)x)RY-3a{5XEH zT>+^=W);TBt|)0Nq=4HJyxC$G4ZfNGdwUa|0}1EA`_5J%^`xu$imQ3i zS@uJuD`OEI30KF}RS#Z)1d5uFEN@Sgw=YoK!&h7nFIkID_Cf3@X;^r}1IX!+IRB`@4`w0*K9no6G$KDk!D@*`7WQPuUP=GQ*^%4g@c zCYl~N>s+kw_y{uI0paWbEN9Q3ICtX0@h|;Syz8MW)ekM!G$dj&l4i+vj|$=~igyFFg-0a0mV{=8=YC^%}U%YfR(Qa@yb) z>gm;xAx93Ro{O+~OShcO-4bR^i&p01FEy2cT0i*EoN9qw--t2^cVt|;HEw2~veVgr z8zh7sBn0h{VHZdU_{Tw6nuV_VhLNxusiDWJJr8?+)3hZGB|B!C^fkexhFAo&J_wJD z`QZ=pfZU5AAC?PnheXUAn_=#p7;c6mr#?i>G%gICabbyrgjXciN?9f&CsGay&d}ar z%T#P!=RQcm$j~QVajEYrDyM@=WQe0!wFK{x%$JF&bf+jHNMff(6UKkHcuv;;UkWKc0B_ z@#Mn?6AvGZcWhbcctS}%n{c*XuU+w4-79s;T6dz>ovdA#s9ksIz;{NzJ@Q_~U$6MH z74h123$;%s%b$!ppIj<56|EsHWW`fJEiMtNMzoFaX-mS{a&hqWonPPiH%@H9K=hBJ zkKhwd3j-U3&sPpOO|J+84(nI8xer16Zy`uew5QQ(p8QXydqHrUXVx+^RnaeQhd6fVB!qxboYgiSV0!@-xvFs^ zD?O@#LIrVop2 z;rV^oX8Jhm_PJ%FU zRiWw?AyNXjg{8cLp(5+dxr}9}DNDp4ysH){w4LZxghh%(-#6%lJBUGUdS+W=Sxx>fEX?^?-_uJDZHMpQkTb-*>JDwkl~@bT%pY zO7xoPP$HA{8;uC1&0-oNHjBFvFwWv!)Cttr%49e#xXr^UD|t+W@RksFs^TDwxtOFb zAi*D}>?aE_MY5Z+l5jt5Zazp~Tw z6V^SCIq`;hcst*vn9;#0xU%z0WNesji>1b{L%la>azlpfslI)6vsrzoKkvOf=6n~D0C{ab zcBMf`u1S8_DJ!{J=ho{}L)Zd?fuRco7@<#g>9Moi0WglCLJND;_xe-)y6iq;ZOZwr zYKXy{rV@rgWs4btOpQkMu24!C~GA7u3rAb|M zCB}qVl~zTa`t~qbw;t>xVh6h`|lLkI;n5TEf$QdxL zB9j^iiUfWQ1d$oki{g`ybgF!l0olegWNh@JSD>Qn&Rpjy638BfvS%0D!@w~)sU7|L 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 literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/gridfs/asynchronous/grid_file.py b/.venv/lib/python3.12/site-packages/gridfs/asynchronous/grid_file.py new file mode 100644 index 0000000..e512f79 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/asynchronous/grid_file.py @@ -0,0 +1,2008 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tools for representing files stored in GridFS.""" +from __future__ import annotations + +import datetime +import inspect +import io +import math +from collections import abc +from typing import Any, Iterable, Mapping, NoReturn, Optional, cast + +from bson.int64 import Int64 +from bson.objectid import ObjectId +from gridfs.errors import CorruptGridFile, FileExists, NoFile +from gridfs.grid_file_shared import ( + _C_INDEX, + _CHUNK_OVERHEAD, + _F_INDEX, + _SEEK_CUR, + _SEEK_END, + _SEEK_SET, + _UPLOAD_BUFFER_CHUNKS, + _UPLOAD_BUFFER_SIZE, + DEFAULT_CHUNK_SIZE, + EMPTY, + NEWLN, + _a_grid_in_property, + _a_grid_out_property, + _clear_entity_type_registry, +) +from pymongo import ASCENDING, DESCENDING, WriteConcern, _csot +from pymongo.asynchronous.client_session import AsyncClientSession +from pymongo.asynchronous.collection import AsyncCollection +from pymongo.asynchronous.cursor import AsyncCursor +from pymongo.asynchronous.database import AsyncDatabase +from pymongo.asynchronous.helpers import anext +from pymongo.common import validate_string +from pymongo.errors import ( + BulkWriteError, + ConfigurationError, + CursorNotFound, + DuplicateKeyError, + InvalidOperation, + OperationFailure, +) +from pymongo.helpers_shared import _check_write_command_response +from pymongo.read_preferences import ReadPreference, _ServerMode + +_IS_SYNC = False + + +def _disallow_transactions(session: Optional[AsyncClientSession]) -> None: + if session and session.in_transaction: + raise InvalidOperation("GridFS does not support multi-document transactions") + + +class AsyncGridFS: + """An instance of GridFS on top of a single Database.""" + + def __init__(self, database: AsyncDatabase[Any], collection: str = "fs"): + """Create a new instance of :class:`GridFS`. + + Raises :class:`TypeError` if `database` is not an instance of + :class:`~pymongo.database.Database`. + + :param database: database to use + :param collection: root collection to use + + .. versionchanged:: 4.0 + Removed the `disable_md5` parameter. See + :ref:`removed-gridfs-checksum` for details. + + .. versionchanged:: 3.11 + Running a GridFS operation in a transaction now always raises an + error. GridFS does not support multi-document transactions. + + .. versionchanged:: 3.7 + Added the `disable_md5` parameter. + + .. versionchanged:: 3.1 + Indexes are only ensured on the first write to the DB. + + .. versionchanged:: 3.0 + `database` must use an acknowledged + :attr:`~pymongo.database.Database.write_concern` + + .. seealso:: The MongoDB documentation on `gridfs `_. + """ + if not isinstance(database, AsyncDatabase): + raise TypeError(f"database must be an instance of Database, not {type(database)}") + + database = _clear_entity_type_registry(database) + + if not database.write_concern.acknowledged: + raise ConfigurationError("database must use acknowledged write_concern") + + self._collection = database[collection] + self._files = self._collection.files + self._chunks = self._collection.chunks + + def new_file(self, **kwargs: Any) -> AsyncGridIn: + """Create a new file in GridFS. + + Returns a new :class:`~gridfs.grid_file.GridIn` instance to + which data can be written. Any keyword arguments will be + passed through to :meth:`~gridfs.grid_file.GridIn`. + + If the ``"_id"`` of the file is manually specified, it must + not already exist in GridFS. Otherwise + :class:`~gridfs.errors.FileExists` is raised. + + :param kwargs: keyword arguments for file creation + """ + return AsyncGridIn(self._collection, **kwargs) + + async def put(self, data: Any, **kwargs: Any) -> Any: + """Put data in GridFS as a new file. + + Equivalent to doing:: + + with fs.new_file(**kwargs) as f: + f.write(data) + + `data` can be either an instance of :class:`bytes` or a file-like + object providing a :meth:`read` method. If an `encoding` keyword + argument is passed, `data` can also be a :class:`str` instance, which + will be encoded as `encoding` before being written. Any keyword + arguments will be passed through to the created file - see + :meth:`~gridfs.grid_file.GridIn` for possible arguments. Returns the + ``"_id"`` of the created file. + + If the ``"_id"`` of the file is manually specified, it must + not already exist in GridFS. Otherwise + :class:`~gridfs.errors.FileExists` is raised. + + :param data: data to be written as a file. + :param kwargs: keyword arguments for file creation + + .. versionchanged:: 3.0 + w=0 writes to GridFS are now prohibited. + """ + async with AsyncGridIn(self._collection, **kwargs) as grid_file: + await grid_file.write(data) + return grid_file._id + + async def get(self, file_id: Any, session: Optional[AsyncClientSession] = None) -> AsyncGridOut: + """Get a file from GridFS by ``"_id"``. + + Returns an instance of :class:`~gridfs.grid_file.GridOut`, + which provides a file-like interface for reading. + + :param file_id: ``"_id"`` of the file to get + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + gout = AsyncGridOut(self._collection, file_id, session=session) + + # Raise NoFile now, instead of on first attribute access. + await gout.open() + return gout + + async def get_version( + self, + filename: Optional[str] = None, + version: Optional[int] = -1, + session: Optional[AsyncClientSession] = None, + **kwargs: Any, + ) -> AsyncGridOut: + """Get a file from GridFS by ``"filename"`` or metadata fields. + + Returns a version of the file in GridFS whose filename matches + `filename` and whose metadata fields match the supplied keyword + arguments, as an instance of :class:`~gridfs.grid_file.GridOut`. + + Version numbering is a convenience atop the GridFS API provided + by MongoDB. If more than one file matches the query (either by + `filename` alone, by metadata fields, or by a combination of + both), then version ``-1`` will be the most recently uploaded + matching file, ``-2`` the second most recently + uploaded, etc. Version ``0`` will be the first version + uploaded, ``1`` the second version, etc. So if three versions + have been uploaded, then version ``0`` is the same as version + ``-3``, version ``1`` is the same as version ``-2``, and + version ``2`` is the same as version ``-1``. + + Raises :class:`~gridfs.errors.NoFile` if no such version of + that file exists. + + :param filename: ``"filename"`` of the file to get, or `None` + :param version: version of the file to get (defaults + to -1, the most recent version uploaded) + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + :param kwargs: find files by custom metadata. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``get_version`` no longer ensures indexes. + """ + query = kwargs + if filename is not None: + query["filename"] = filename + + _disallow_transactions(session) + cursor = self._files.find(query, session=session) + if version is None: + version = -1 + if version < 0: + skip = abs(version) - 1 + cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING) + else: + cursor.limit(-1).skip(version).sort("uploadDate", ASCENDING) + try: + doc = await anext(cursor) + return AsyncGridOut(self._collection, file_document=doc, session=session) + except StopAsyncIteration: + raise NoFile("no version %d for filename %r" % (version, filename)) from None + + async def get_last_version( + self, + filename: Optional[str] = None, + session: Optional[AsyncClientSession] = None, + **kwargs: Any, + ) -> AsyncGridOut: + """Get the most recent version of a file in GridFS by ``"filename"`` + or metadata fields. + + Equivalent to calling :meth:`get_version` with the default + `version` (``-1``). + + :param filename: ``"filename"`` of the file to get, or `None` + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + :param kwargs: find files by custom metadata. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + return await self.get_version(filename=filename, session=session, **kwargs) + + # TODO add optional safe mode for chunk removal? + async def delete(self, file_id: Any, session: Optional[AsyncClientSession] = None) -> None: + """Delete a file from GridFS by ``"_id"``. + + Deletes all data belonging to the file with ``"_id"``: + `file_id`. + + .. warning:: Any processes/threads reading from the file while + this method is executing will likely see an invalid/corrupt + file. Care should be taken to avoid concurrent reads to a file + while it is being deleted. + + .. note:: Deletes of non-existent files are considered successful + since the end result is the same: no file with that _id remains. + + :param file_id: ``"_id"`` of the file to delete + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``delete`` no longer ensures indexes. + """ + _disallow_transactions(session) + await self._files.delete_one({"_id": file_id}, session=session) + await self._chunks.delete_many({"files_id": file_id}, session=session) + + async def list(self, session: Optional[AsyncClientSession] = None) -> list[str]: + """List the names of all files stored in this instance of + :class:`GridFS`. + + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``list`` no longer ensures indexes. + """ + _disallow_transactions(session) + # With an index, distinct includes documents with no filename + # as None. + return [ + name + for name in await self._files.distinct("filename", session=session) + if name is not None + ] + + async def find_one( + self, + filter: Optional[Any] = None, + session: Optional[AsyncClientSession] = None, + *args: Any, + **kwargs: Any, + ) -> Optional[AsyncGridOut]: + """Get a single file from gridfs. + + All arguments to :meth:`find` are also valid arguments for + :meth:`find_one`, although any `limit` argument will be + ignored. Returns a single :class:`~gridfs.grid_file.GridOut`, + or ``None`` if no matching file is found. For example: + + .. code-block: python + + file = fs.find_one({"filename": "lisa.txt"}) + + :param filter: a dictionary specifying + the query to be performing OR any other type to be used as + the value for a query for ``"_id"`` in the file collection. + :param args: any additional positional arguments are + the same as the arguments to :meth:`find`. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + :param kwargs: any additional keyword arguments + are the same as the arguments to :meth:`find`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + if filter is not None and not isinstance(filter, abc.Mapping): + filter = {"_id": filter} + + _disallow_transactions(session) + async for f in self.find(filter, *args, session=session, **kwargs): + return f + + return None + + def find(self, *args: Any, **kwargs: Any) -> AsyncGridOutCursor: + """Query GridFS for files. + + Returns a cursor that iterates across files matching + arbitrary queries on the files collection. Can be combined + with other modifiers for additional control. For example:: + + for grid_out in fs.find({"filename": "lisa.txt"}, + no_cursor_timeout=True): + data = grid_out.read() + + would iterate through all versions of "lisa.txt" stored in GridFS. + Note that setting no_cursor_timeout to True may be important to + prevent the cursor from timing out during long multi-file processing + work. + + As another example, the call:: + + most_recent_three = fs.find().sort("uploadDate", -1).limit(3) + + would return a cursor to the three most recently uploaded files + in GridFS. + + Follows a similar interface to + :meth:`~pymongo.collection.Collection.find` + in :class:`~pymongo.collection.Collection`. + + If a :class:`~pymongo.client_session.AsyncClientSession` is passed to + :meth:`find`, all returned :class:`~gridfs.grid_file.GridOut` instances + are associated with that session. + + :param filter: A query document that selects which files + to include in the result set. Can be an empty document to include + all files. + :param skip: the number of files to omit (from + the start of the result set) when returning the results + :param limit: the maximum number of results to + return + :param no_cursor_timeout: if False (the default), any + returned cursor is closed by the server after 10 minutes of + inactivity. If set to True, the returned cursor will never + time out on the server. Care should be taken to ensure that + cursors with no_cursor_timeout turned on are properly closed. + :param sort: a list of (key, direction) pairs + specifying the sort order for this query. See + :meth:`~pymongo.cursor.Cursor.sort` for details. + + Raises :class:`TypeError` if any of the arguments are of + improper type. Returns an instance of + :class:`~gridfs.grid_file.GridOutCursor` + corresponding to this query. + + .. versionchanged:: 3.0 + Removed the read_preference, tag_sets, and + secondary_acceptable_latency_ms options. + .. versionadded:: 2.7 + .. seealso:: The MongoDB documentation on `find `_. + """ + return AsyncGridOutCursor(self._collection, *args, **kwargs) + + async def exists( + self, + document_or_id: Optional[Any] = None, + session: Optional[AsyncClientSession] = None, + **kwargs: Any, + ) -> bool: + """Check if a file exists in this instance of :class:`GridFS`. + + The file to check for can be specified by the value of its + ``_id`` key, or by passing in a query document. A query + document can be passed in as dictionary, or by using keyword + arguments. Thus, the following three calls are equivalent: + + >>> fs.exists(file_id) + >>> fs.exists({"_id": file_id}) + >>> fs.exists(_id=file_id) + + As are the following two calls: + + >>> fs.exists({"filename": "mike.txt"}) + >>> fs.exists(filename="mike.txt") + + And the following two: + + >>> fs.exists({"foo": {"$gt": 12}}) + >>> fs.exists(foo={"$gt": 12}) + + Returns ``True`` if a matching file exists, ``False`` + otherwise. Calls to :meth:`exists` will not automatically + create appropriate indexes; application developers should be + sure to create indexes if needed and as appropriate. + + :param document_or_id: query document, or _id of the + document to check for + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + :param kwargs: keyword arguments are used as a + query document, if they're present. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + _disallow_transactions(session) + if kwargs: + f = await self._files.find_one(kwargs, ["_id"], session=session) + else: + f = await self._files.find_one(document_or_id, ["_id"], session=session) + + return f is not None + + +class AsyncGridFSBucket: + """An instance of GridFS on top of a single Database.""" + + def __init__( + self, + db: AsyncDatabase[Any], + bucket_name: str = "fs", + chunk_size_bytes: int = DEFAULT_CHUNK_SIZE, + write_concern: Optional[WriteConcern] = None, + read_preference: Optional[_ServerMode] = None, + ) -> None: + """Create a new instance of :class:`GridFSBucket`. + + Raises :exc:`TypeError` if `database` is not an instance of + :class:`~pymongo.database.Database`. + + Raises :exc:`~pymongo.errors.ConfigurationError` if `write_concern` + is not acknowledged. + + :param database: database to use. + :param bucket_name: The name of the bucket. Defaults to 'fs'. + :param chunk_size_bytes: The chunk size in bytes. Defaults + to 255KB. + :param write_concern: The + :class:`~pymongo.write_concern.WriteConcern` to use. If ``None`` + (the default) db.write_concern is used. + :param read_preference: The read preference to use. If + ``None`` (the default) db.read_preference is used. + + .. versionchanged:: 4.0 + Removed the `disable_md5` parameter. See + :ref:`removed-gridfs-checksum` for details. + + .. versionchanged:: 3.11 + Running a GridFSBucket operation in a transaction now always raises + an error. GridFSBucket does not support multi-document transactions. + + .. versionchanged:: 3.7 + Added the `disable_md5` parameter. + + .. versionadded:: 3.1 + + .. seealso:: The MongoDB documentation on `gridfs `_. + """ + if not isinstance(db, AsyncDatabase): + raise TypeError(f"database must be an instance of AsyncDatabase, not {type(db)}") + + db = _clear_entity_type_registry(db) + + wtc = write_concern if write_concern is not None else db.write_concern + if not wtc.acknowledged: + raise ConfigurationError("write concern must be acknowledged") + + self._bucket_name = bucket_name + self._collection = db[bucket_name] + self._chunks: AsyncCollection[Any] = self._collection.chunks.with_options( + write_concern=write_concern, read_preference=read_preference + ) + + self._files: AsyncCollection[Any] = self._collection.files.with_options( + write_concern=write_concern, read_preference=read_preference + ) + + self._chunk_size_bytes = chunk_size_bytes + self._timeout = db.client.options.timeout + + def open_upload_stream( + self, + filename: str, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[AsyncClientSession] = None, + ) -> AsyncGridIn: + """Opens a Stream that the application can write the contents of the + file to. + + The user must specify the filename, and can choose to add any + additional information in the metadata field of the file document or + modify the chunk size. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + with fs.open_upload_stream( + "test_file", chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) as grid_in: + grid_in.write("data I want to store!") + # uploaded on close + + Returns an instance of :class:`~gridfs.grid_file.GridIn`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param filename: The name of the file to upload. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + opts = { + "filename": filename, + "chunk_size": ( + chunk_size_bytes if chunk_size_bytes is not None else self._chunk_size_bytes + ), + } + if metadata is not None: + opts["metadata"] = metadata + + return AsyncGridIn(self._collection, session=session, **opts) + + def open_upload_stream_with_id( + self, + file_id: Any, + filename: str, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[AsyncClientSession] = None, + ) -> AsyncGridIn: + """Opens a Stream that the application can write the contents of the + file to. + + The user must specify the file id and filename, and can choose to add + any additional information in the metadata field of the file document + or modify the chunk size. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + with fs.open_upload_stream_with_id( + ObjectId(), + "test_file", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) as grid_in: + grid_in.write("data I want to store!") + # uploaded on close + + Returns an instance of :class:`~gridfs.grid_file.GridIn`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param file_id: The id to use for this file. The id must not have + already been used for another file. + :param filename: The name of the file to upload. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + opts = { + "_id": file_id, + "filename": filename, + "chunk_size": ( + chunk_size_bytes if chunk_size_bytes is not None else self._chunk_size_bytes + ), + } + if metadata is not None: + opts["metadata"] = metadata + + return AsyncGridIn(self._collection, session=session, **opts) + + @_csot.apply + async def upload_from_stream( + self, + filename: str, + source: Any, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[AsyncClientSession] = None, + ) -> ObjectId: + """Uploads a user file to a GridFS bucket. + + Reads the contents of the user file from `source` and uploads + it to the file `filename`. Source can be a string or file-like object. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + file_id = fs.upload_from_stream( + "test_file", + "data I want to store!", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + + Returns the _id of the uploaded file. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param filename: The name of the file to upload. + :param source: The source stream of the content to be uploaded. Must be + a file-like object that implements :meth:`read` or a string. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + async with self.open_upload_stream( + filename, chunk_size_bytes, metadata, session=session + ) as gin: + await gin.write(source) + + return cast(ObjectId, gin._id) + + @_csot.apply + async def upload_from_stream_with_id( + self, + file_id: Any, + filename: str, + source: Any, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[AsyncClientSession] = None, + ) -> None: + """Uploads a user file to a GridFS bucket with a custom file id. + + Reads the contents of the user file from `source` and uploads + it to the file `filename`. Source can be a string or file-like object. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + file_id = fs.upload_from_stream( + ObjectId(), + "test_file", + "data I want to store!", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param file_id: The id to use for this file. The id must not have + already been used for another file. + :param filename: The name of the file to upload. + :param source: The source stream of the content to be uploaded. Must be + a file-like object that implements :meth:`read` or a string. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + async with self.open_upload_stream_with_id( + file_id, filename, chunk_size_bytes, metadata, session=session + ) as gin: + await gin.write(source) + + async def open_download_stream( + self, file_id: Any, session: Optional[AsyncClientSession] = None + ) -> AsyncGridOut: + """Opens a Stream from which the application can read the contents of + the stored file specified by file_id. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # get _id of file to read. + file_id = fs.upload_from_stream("test_file", "data I want to store!") + grid_out = fs.open_download_stream(file_id) + contents = grid_out.read() + + Returns an instance of :class:`~gridfs.grid_file.GridOut`. + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be downloaded. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + gout = AsyncGridOut(self._collection, file_id, session=session) + + # Raise NoFile now, instead of on first attribute access. + await gout.open() + return gout + + @_csot.apply + async def download_to_stream( + self, file_id: Any, destination: Any, session: Optional[AsyncClientSession] = None + ) -> None: + """Downloads the contents of the stored file specified by file_id and + writes the contents to `destination`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to read + file_id = fs.upload_from_stream("test_file", "data I want to store!") + # Get file to write to + file = open('myfile','wb+') + fs.download_to_stream(file_id, file) + file.seek(0) + contents = file.read() + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be downloaded. + :param destination: a file-like object implementing :meth:`write`. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + async with await self.open_download_stream(file_id, session=session) as gout: + while True: + chunk = await gout.readchunk() + if not len(chunk): + break + destination.write(chunk) + + @_csot.apply + async def delete(self, file_id: Any, session: Optional[AsyncClientSession] = None) -> None: + """Given an file_id, delete this stored file's files collection document + and associated chunks from a GridFS bucket. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to delete + file_id = fs.upload_from_stream("test_file", "data I want to store!") + fs.delete(file_id) + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be deleted. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + _disallow_transactions(session) + res = await self._files.delete_one({"_id": file_id}, session=session) + await self._chunks.delete_many({"files_id": file_id}, session=session) + if not res.deleted_count: + raise NoFile("no file could be deleted because none matched %s" % file_id) + + @_csot.apply + async def delete_by_name( + self, filename: str, session: Optional[AsyncClientSession] = None + ) -> None: + """Given a filename, delete this stored file's files collection document(s) + and associated chunks from a GridFS bucket. + + For example:: + + my_db = AsyncMongoClient().test + fs = AsyncGridFSBucket(my_db) + await fs.upload_from_stream("test_file", "data I want to store!") + await fs.delete_by_name("test_file") + + Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists. + + :param filename: The name of the file to be deleted. + :param session: a :class:`~pymongo.client_session.AsyncClientSession` + + .. versionadded:: 4.12 + """ + _disallow_transactions(session) + files = self._files.find({"filename": filename}, {"_id": 1}, session=session) + file_ids = [file["_id"] async for file in files] + res = await self._files.delete_many({"_id": {"$in": file_ids}}, session=session) + await self._chunks.delete_many({"files_id": {"$in": file_ids}}, session=session) + if not res.deleted_count: + raise NoFile(f"no file could be deleted because none matched filename {filename!r}") + + def find(self, *args: Any, **kwargs: Any) -> AsyncGridOutCursor: + """Find and return the files collection documents that match ``filter`` + + Returns a cursor that iterates across files matching + arbitrary queries on the files collection. Can be combined + with other modifiers for additional control. + + For example:: + + for grid_data in fs.find({"filename": "lisa.txt"}, + no_cursor_timeout=True): + data = grid_data.read() + + would iterate through all versions of "lisa.txt" stored in GridFS. + Note that setting no_cursor_timeout to True may be important to + prevent the cursor from timing out during long multi-file processing + work. + + As another example, the call:: + + most_recent_three = fs.find().sort("uploadDate", -1).limit(3) + + would return a cursor to the three most recently uploaded files + in GridFS. + + Follows a similar interface to + :meth:`~pymongo.collection.Collection.find` + in :class:`~pymongo.collection.Collection`. + + If a :class:`~pymongo.client_session.AsyncClientSession` is passed to + :meth:`find`, all returned :class:`~gridfs.grid_file.GridOut` instances + are associated with that session. + + :param filter: Search query. + :param batch_size: The number of documents to return per + batch. + :param limit: The maximum number of documents to return. + :param no_cursor_timeout: The server normally times out idle + cursors after an inactivity period (10 minutes) to prevent excess + memory use. Set this option to True prevent that. + :param skip: The number of documents to skip before + returning. + :param sort: The order by which to sort results. Defaults to + None. + """ + return AsyncGridOutCursor(self._collection, *args, **kwargs) + + async def open_download_stream_by_name( + self, filename: str, revision: int = -1, session: Optional[AsyncClientSession] = None + ) -> AsyncGridOut: + """Opens a Stream from which the application can read the contents of + `filename` and optional `revision`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + grid_out = fs.open_download_stream_by_name("test_file") + contents = grid_out.read() + + Returns an instance of :class:`~gridfs.grid_file.GridOut`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + + Raises :exc:`~ValueError` filename is not a string. + + :param filename: The name of the file to read from. + :param revision: Which revision (documents with the same + filename and different uploadDate) of the file to retrieve. + Defaults to -1 (the most recent revision). + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + :Note: Revision numbers are defined as follows: + + - 0 = the original stored file + - 1 = the first revision + - 2 = the second revision + - etc... + - -2 = the second most recent revision + - -1 = the most recent revision + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + query = {"filename": filename} + _disallow_transactions(session) + cursor = self._files.find(query, session=session) + if revision < 0: + skip = abs(revision) - 1 + cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING) + else: + cursor.limit(-1).skip(revision).sort("uploadDate", ASCENDING) + try: + grid_file = await anext(cursor) + return AsyncGridOut(self._collection, file_document=grid_file, session=session) + except StopAsyncIteration: + raise NoFile("no version %d for filename %r" % (revision, filename)) from None + + @_csot.apply + async def download_to_stream_by_name( + self, + filename: str, + destination: Any, + revision: int = -1, + session: Optional[AsyncClientSession] = None, + ) -> None: + """Write the contents of `filename` (with optional `revision`) to + `destination`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get file to write to + file = open('myfile','wb') + fs.download_to_stream_by_name("test_file", file) + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + + Raises :exc:`~ValueError` if `filename` is not a string. + + :param filename: The name of the file to read from. + :param destination: A file-like object that implements :meth:`write`. + :param revision: Which revision (documents with the same + filename and different uploadDate) of the file to retrieve. + Defaults to -1 (the most recent revision). + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + :Note: Revision numbers are defined as follows: + + - 0 = the original stored file + - 1 = the first revision + - 2 = the second revision + - etc... + - -2 = the second most recent revision + - -1 = the most recent revision + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + async with await self.open_download_stream_by_name( + filename, revision, session=session + ) as gout: + while True: + chunk = await gout.readchunk() + if not len(chunk): + break + destination.write(chunk) + + async def rename( + self, file_id: Any, new_filename: str, session: Optional[AsyncClientSession] = None + ) -> None: + """Renames the stored file with the specified file_id. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to rename + file_id = fs.upload_from_stream("test_file", "data I want to store!") + fs.rename(file_id, "new_test_name") + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be renamed. + :param new_filename: The new name of the file. + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + _disallow_transactions(session) + result = await self._files.update_one( + {"_id": file_id}, {"$set": {"filename": new_filename}}, session=session + ) + if not result.matched_count: + raise NoFile( + "no files could be renamed %r because none " + "matched file_id %i" % (new_filename, file_id) + ) + + async def rename_by_name( + self, filename: str, new_filename: str, session: Optional[AsyncClientSession] = None + ) -> None: + """Renames the stored file with the specified filename. + + For example:: + + my_db = AsyncMongoClient().test + fs = AsyncGridFSBucket(my_db) + await fs.upload_from_stream("test_file", "data I want to store!") + await fs.rename_by_name("test_file", "new_test_name") + + Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists. + + :param filename: The filename of the file to be renamed. + :param new_filename: The new name of the file. + :param session: a :class:`~pymongo.client_session.AsyncClientSession` + + .. versionadded:: 4.12 + """ + _disallow_transactions(session) + result = await self._files.update_many( + {"filename": filename}, {"$set": {"filename": new_filename}}, session=session + ) + if not result.matched_count: + raise NoFile( + f"no files could be renamed {new_filename!r} because none matched filename {filename!r}" + ) + + +class AsyncGridIn: + """Class to write data to GridFS.""" + + def __init__( + self, + root_collection: AsyncCollection[Any], + session: Optional[AsyncClientSession] = None, + **kwargs: Any, + ) -> None: + """Write a file to GridFS + + Application developers should generally not need to + instantiate this class directly - instead see the methods + provided by :class:`~gridfs.GridFS`. + + Raises :class:`TypeError` if `root_collection` is not an + instance of :class:`~pymongo.collection.AsyncCollection`. + + Any of the file level options specified in the `GridFS Spec + `_ may be passed as + keyword arguments. Any additional keyword arguments will be + set as additional fields on the file document. Valid keyword + arguments include: + + - ``"_id"``: unique ID for this file (default: + :class:`~bson.objectid.ObjectId`) - this ``"_id"`` must + not have already been used for another file + + - ``"filename"``: human name for the file + + - ``"contentType"`` or ``"content_type"``: valid mime-type + for the file + + - ``"chunkSize"`` or ``"chunk_size"``: size of each of the + chunks, in bytes (default: 255 kb) + + - ``"encoding"``: encoding used for this file. Any :class:`str` + that is written to the file will be converted to :class:`bytes`. + + :param root_collection: root collection to write to + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` to use for all + commands + :param kwargs: Any: file level options (see above) + + .. versionchanged:: 4.0 + Removed the `disable_md5` parameter. See + :ref:`removed-gridfs-checksum` for details. + + .. versionchanged:: 3.7 + Added the `disable_md5` parameter. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + `root_collection` must use an acknowledged + :attr:`~pymongo.collection.AsyncCollection.write_concern` + """ + if not isinstance(root_collection, AsyncCollection): + raise TypeError( + f"root_collection must be an instance of AsyncCollection, not {type(root_collection)}" + ) + + if not root_collection.write_concern.acknowledged: + raise ConfigurationError("root_collection must use acknowledged write_concern") + _disallow_transactions(session) + + # Handle alternative naming + if "content_type" in kwargs: + kwargs["contentType"] = kwargs.pop("content_type") + if "chunk_size" in kwargs: + kwargs["chunkSize"] = kwargs.pop("chunk_size") + + coll = _clear_entity_type_registry(root_collection, read_preference=ReadPreference.PRIMARY) + + # Defaults + kwargs["_id"] = kwargs.get("_id", ObjectId()) + kwargs["chunkSize"] = kwargs.get("chunkSize", DEFAULT_CHUNK_SIZE) + object.__setattr__(self, "_session", session) + object.__setattr__(self, "_coll", coll) + object.__setattr__(self, "_chunks", coll.chunks) + object.__setattr__(self, "_file", kwargs) + object.__setattr__(self, "_buffer", io.BytesIO()) + object.__setattr__(self, "_position", 0) + object.__setattr__(self, "_chunk_number", 0) + object.__setattr__(self, "_closed", False) + object.__setattr__(self, "_ensured_index", False) + object.__setattr__(self, "_buffered_docs", []) + object.__setattr__(self, "_buffered_docs_size", 0) + + async def _create_index( + self, collection: AsyncCollection[Any], index_key: Any, unique: bool + ) -> None: + doc = await collection.find_one(projection={"_id": 1}, session=self._session) + if doc is None: + try: + index_keys = [ + index_spec["key"] + async for index_spec in await collection.list_indexes(session=self._session) + ] + except OperationFailure: + index_keys = [] + if index_key not in index_keys: + await collection.create_index( + index_key.items(), unique=unique, session=self._session + ) + + async def _ensure_indexes(self) -> None: + if not object.__getattribute__(self, "_ensured_index"): + _disallow_transactions(self._session) + await self._create_index(self._coll.files, _F_INDEX, False) + await self._create_index(self._coll.chunks, _C_INDEX, True) + object.__setattr__(self, "_ensured_index", True) + + async def abort(self) -> None: + """Remove all chunks/files that may have been uploaded and close.""" + await self._coll.chunks.delete_many({"files_id": self._file["_id"]}, session=self._session) + await self._coll.files.delete_one({"_id": self._file["_id"]}, session=self._session) + object.__setattr__(self, "_closed", True) + + @property + def closed(self) -> bool: + """Is this file closed?""" + return self._closed + + _id: Any = _a_grid_in_property("_id", "The ``'_id'`` value for this file.", read_only=True) + filename: Optional[str] = _a_grid_in_property("filename", "Name of this file.") + name: Optional[str] = _a_grid_in_property("filename", "Alias for `filename`.") + content_type: Optional[str] = _a_grid_in_property( + "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." + ) + length: int = _a_grid_in_property("length", "Length (in bytes) of this file.", closed_only=True) + chunk_size: int = _a_grid_in_property("chunkSize", "Chunk size for this file.", read_only=True) + upload_date: datetime.datetime = _a_grid_in_property( + "uploadDate", "Date that this file was uploaded.", closed_only=True + ) + md5: Optional[str] = _a_grid_in_property( + "md5", + "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", + closed_only=True, + ) + + _buffer: io.BytesIO + _closed: bool + _buffered_docs: list[dict[str, Any]] + _buffered_docs_size: int + + def __getattr__(self, name: str) -> Any: + if name == "_coll": + return object.__getattribute__(self, name) + elif name in self._file: + return self._file[name] + raise AttributeError("GridIn object has no attribute '%s'" % name) + + def __setattr__(self, name: str, value: Any) -> None: + # For properties of this instance like _buffer, or descriptors set on + # the class like filename, use regular __setattr__ + if name in self.__dict__ or name in self.__class__.__dict__: + object.__setattr__(self, name, value) + else: + # All other attributes are part of the document in db.fs.files. + # Store them to be sent to server on close() or if closed, send + # them now. + self._file[name] = value + if self._closed: + if _IS_SYNC: + self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {name: value}}) + else: + raise AttributeError( + "AsyncGridIn does not support __setattr__ after being closed(). Set the attribute before closing the file or use AsyncGridIn.set() instead" + ) + + async def set(self, name: str, value: Any) -> None: + self._file[name] = value + if self._closed: + await self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {name: value}}) + + async def _flush_data(self, data: Any, force: bool = False) -> None: + """Flush `data` to a chunk.""" + await self._ensure_indexes() + assert len(data) <= self.chunk_size + if data: + self._buffered_docs.append( + {"files_id": self._file["_id"], "n": self._chunk_number, "data": data} + ) + self._buffered_docs_size += len(data) + _CHUNK_OVERHEAD + if not self._buffered_docs: + return + # Limit to 100,000 chunks or 32MB (+1 chunk) of data. + if ( + force + or self._buffered_docs_size >= _UPLOAD_BUFFER_SIZE + or len(self._buffered_docs) >= _UPLOAD_BUFFER_CHUNKS + ): + try: + await self._chunks.insert_many(self._buffered_docs, session=self._session) + except BulkWriteError as exc: + # For backwards compatibility, raise an insert_one style exception. + write_errors = exc.details["writeErrors"] + for err in write_errors: + if err.get("code") in (11000, 11001, 12582): # Duplicate key errors + self._raise_file_exists(self._file["_id"]) + result = {"writeErrors": write_errors} + wces = exc.details["writeConcernErrors"] + if wces: + result["writeConcernError"] = wces[-1] + _check_write_command_response(result) + raise + self._buffered_docs = [] + self._buffered_docs_size = 0 + self._chunk_number += 1 + self._position += len(data) + + async def _flush_buffer(self, force: bool = False) -> None: + """Flush the buffer contents out to a chunk.""" + await self._flush_data(self._buffer.getvalue(), force=force) + self._buffer.close() + self._buffer = io.BytesIO() + + async def _flush(self) -> Any: + """Flush the file to the database.""" + try: + await self._flush_buffer(force=True) + # The GridFS spec says length SHOULD be an Int64. + self._file["length"] = Int64(self._position) + self._file["uploadDate"] = datetime.datetime.now(tz=datetime.timezone.utc) + + return await self._coll.files.insert_one(self._file, session=self._session) + except DuplicateKeyError: + self._raise_file_exists(self._id) + + def _raise_file_exists(self, file_id: Any) -> NoReturn: + """Raise a FileExists exception for the given file_id.""" + raise FileExists("file with _id %r already exists" % file_id) + + async def close(self) -> None: + """Flush the file and close it. + + A closed file cannot be written any more. Calling + :meth:`close` more than once is allowed. + """ + if not self._closed: + await self._flush() + object.__setattr__(self, "_closed", True) + + def read(self, size: int = -1) -> NoReturn: + raise io.UnsupportedOperation("read") + + def readable(self) -> bool: + return False + + def seekable(self) -> bool: + return False + + async def write(self, data: Any) -> None: + """Write data to the file. There is no return value. + + `data` can be either a string of bytes or a file-like object + (implementing :meth:`read`). If the file has an + :attr:`encoding` attribute, `data` can also be a + :class:`str` instance, which will be encoded as + :attr:`encoding` before being written. + + Due to buffering, the data may not actually be written to the + database until the :meth:`close` method is called. Raises + :class:`ValueError` if this file is already closed. Raises + :class:`TypeError` if `data` is not an instance of + :class:`bytes`, a file-like object, or an instance of :class:`str`. + Unicode data is only allowed if the file has an :attr:`encoding` + attribute. + + :param data: string of bytes or file-like object to be written + to the file + """ + if self._closed: + raise ValueError("cannot write to a closed file") + + try: + # file-like + read = data.read + except AttributeError: + # string + if not isinstance(data, (str, bytes)): + raise TypeError("can only write strings or file-like objects") from None + if isinstance(data, str): + try: + data = data.encode(self.encoding) + except AttributeError: + raise TypeError( + "must specify an encoding for file in order to write str" + ) from None + read = io.BytesIO(data).read + + if inspect.iscoroutinefunction(read): + await self._write_async(read) + else: + if self._buffer.tell() > 0: + # Make sure to flush only when _buffer is complete + space = self.chunk_size - self._buffer.tell() + if space: + try: + to_write = read(space) + except BaseException: + await self.abort() + raise + self._buffer.write(to_write) + if len(to_write) < space: + return # EOF or incomplete + await self._flush_buffer() + to_write = read(self.chunk_size) + while to_write and len(to_write) == self.chunk_size: + await self._flush_data(to_write) + to_write = read(self.chunk_size) + self._buffer.write(to_write) + + async def _write_async(self, read: Any) -> None: + if self._buffer.tell() > 0: + # Make sure to flush only when _buffer is complete + space = self.chunk_size - self._buffer.tell() + if space: + try: + to_write = await read(space) + except BaseException: + await self.abort() + raise + self._buffer.write(to_write) + if len(to_write) < space: + return # EOF or incomplete + await self._flush_buffer() + to_write = await read(self.chunk_size) + while to_write and len(to_write) == self.chunk_size: + await self._flush_data(to_write) + to_write = await read(self.chunk_size) + self._buffer.write(to_write) + + async def writelines(self, sequence: Iterable[Any]) -> None: + """Write a sequence of strings to the file. + + Does not add separators. + """ + for line in sequence: + await self.write(line) + + def writeable(self) -> bool: + return True + + async def __aenter__(self) -> AsyncGridIn: + """Support for the context manager protocol.""" + return self + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Any: + """Support for the context manager protocol. + + Close the file if no exceptions occur and allow exceptions to propagate. + """ + if exc_type is None: + # No exceptions happened. + await self.close() + else: + # Something happened, at minimum mark as closed. + object.__setattr__(self, "_closed", True) + + # propagate exceptions + return False + + +GRIDOUT_BASE_CLASS = io.IOBase if _IS_SYNC else object # type: Any + + +class AsyncGridOut(GRIDOUT_BASE_CLASS): # type: ignore + + """Class to read data out of GridFS.""" + + def __init__( + self, + root_collection: AsyncCollection[Any], + file_id: Optional[int] = None, + file_document: Optional[Any] = None, + session: Optional[AsyncClientSession] = None, + ) -> None: + """Read a file from GridFS + + Application developers should generally not need to + instantiate this class directly - instead see the methods + provided by :class:`~gridfs.GridFS`. + + Either `file_id` or `file_document` must be specified, + `file_document` will be given priority if present. Raises + :class:`TypeError` if `root_collection` is not an instance of + :class:`~pymongo.collection.AsyncCollection`. + + :param root_collection: root collection to read from + :param file_id: value of ``"_id"`` for the file to read + :param file_document: file document from + `root_collection.files` + :param session: a + :class:`~pymongo.client_session.AsyncClientSession` to use for all + commands + + .. versionchanged:: 3.8 + For better performance and to better follow the GridFS spec, + :class:`GridOut` now uses a single cursor to read all the chunks in + the file. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + Creating a GridOut does not immediately retrieve the file metadata + from the server. Metadata is fetched when first needed. + """ + if not isinstance(root_collection, AsyncCollection): + raise TypeError( + f"root_collection must be an instance of AsyncCollection, not {type(root_collection)}" + ) + _disallow_transactions(session) + + root_collection = _clear_entity_type_registry(root_collection) + + super().__init__() + + self._chunks = root_collection.chunks + self._files = root_collection.files + self._file_id = file_id + self._buffer = EMPTY + # Start position within the current buffered chunk. + self._buffer_pos = 0 + self._chunk_iter = None + # Position within the total file. + self._position = 0 + self._file = file_document + self._session = session + if not _IS_SYNC: + self.closed = False + + _id: Any = _a_grid_out_property("_id", "The ``'_id'`` value for this file.") + filename: str = _a_grid_out_property("filename", "Name of this file.") + name: str = _a_grid_out_property("filename", "Alias for `filename`.") + content_type: Optional[str] = _a_grid_out_property( + "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." + ) + length: int = _a_grid_out_property("length", "Length (in bytes) of this file.") + chunk_size: int = _a_grid_out_property("chunkSize", "Chunk size for this file.") + upload_date: datetime.datetime = _a_grid_out_property( + "uploadDate", "Date that this file was first uploaded." + ) + aliases: Optional[list[str]] = _a_grid_out_property( + "aliases", "DEPRECATED, will be removed in PyMongo 5.0. List of aliases for this file." + ) + metadata: Optional[Mapping[str, Any]] = _a_grid_out_property( + "metadata", "Metadata attached to this file." + ) + md5: Optional[str] = _a_grid_out_property( + "md5", + "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", + ) + + _file: Any + _chunk_iter: Any + + if not _IS_SYNC: + closed: bool + + async def __anext__(self) -> bytes: + line = await self.readline() + if line: + return line + raise StopAsyncIteration() + + async def to_list(self) -> list[bytes]: + return [x async for x in self] # noqa: C416, RUF100 + + async def readline(self, size: int = -1) -> bytes: + """Read one line or up to `size` bytes from the file. + + :param size: the maximum number of bytes to read + """ + return await self._read_size_or_line(size=size, line=True) + + async def readlines(self, size: int = -1) -> list[bytes]: + """Read one line or up to `size` bytes from the file. + + :param size: the maximum number of bytes to read + """ + await self.open() + lines = [] + remainder = int(self.length) - self._position + bytes_read = 0 + while remainder > 0: + line = await self._read_size_or_line(line=True) + bytes_read += len(line) + lines.append(line) + remainder = int(self.length) - self._position + if 0 < size < bytes_read: + break + + return lines + + async def open(self) -> None: + if not self._file: + _disallow_transactions(self._session) + self._file = await self._files.find_one({"_id": self._file_id}, session=self._session) + if not self._file: + raise NoFile( + f"no file in gridfs collection {self._files!r} with _id {self._file_id!r}" + ) + + def __getattr__(self, name: str) -> Any: + if _IS_SYNC: + self.open() # type: ignore[unused-coroutine] + elif not self._file: + raise InvalidOperation( + "You must call AsyncGridOut.open() before accessing the %s property" % name + ) + if name in self._file: + return self._file[name] + raise AttributeError("GridOut object has no attribute '%s'" % name) + + def readable(self) -> bool: + return True + + async def readchunk(self) -> bytes: + """Reads a chunk at a time. If the current position is within a + chunk the remainder of the chunk is returned. + """ + await self.open() + received = len(self._buffer) - self._buffer_pos + chunk_data = EMPTY + chunk_size = int(self.chunk_size) + + if received > 0: + chunk_data = self._buffer[self._buffer_pos :] + elif self._position < int(self.length): + chunk_number = int((received + self._position) / chunk_size) + if self._chunk_iter is None: + self._chunk_iter = _AsyncGridOutChunkIterator( + self, self._chunks, self._session, chunk_number + ) + + chunk = await self._chunk_iter.next() + chunk_data = chunk["data"][self._position % chunk_size :] + + if not chunk_data: + raise CorruptGridFile("truncated chunk") + + self._position += len(chunk_data) + self._buffer = EMPTY + self._buffer_pos = 0 + return chunk_data + + async def _read_size_or_line(self, size: int = -1, line: bool = False) -> bytes: + """Internal read() and readline() helper.""" + await self.open() + remainder = int(self.length) - self._position + if size < 0 or size > remainder: + size = remainder + + if size == 0: + return EMPTY + + received = 0 + data = [] + while received < size: + needed = size - received + if self._buffer: + # Optimization: Read the buffer with zero byte copies. + buf = self._buffer + chunk_start = self._buffer_pos + chunk_data = memoryview(buf)[self._buffer_pos :] + self._buffer = EMPTY + self._buffer_pos = 0 + self._position += len(chunk_data) + else: + buf = await self.readchunk() + chunk_start = 0 + chunk_data = memoryview(buf) + if line: + pos = buf.find(NEWLN, chunk_start, chunk_start + needed) - chunk_start + if pos >= 0: + # Decrease size to exit the loop. + size = received + pos + 1 + needed = pos + 1 + if len(chunk_data) > needed: + data.append(chunk_data[:needed]) + # Optimization: Save the buffer with zero byte copies. + self._buffer = buf + self._buffer_pos = chunk_start + needed + self._position -= len(self._buffer) - self._buffer_pos + else: + data.append(chunk_data) + received += len(chunk_data) + + # Detect extra chunks after reading the entire file. + if size == remainder and self._chunk_iter: + try: + await self._chunk_iter.next() + except StopAsyncIteration: + pass + + return b"".join(data) + + async def read(self, size: int = -1) -> bytes: + """Read at most `size` bytes from the file (less if there + isn't enough data). + + The bytes are returned as an instance of :class:`bytes` + If `size` is negative or omitted all data is read. + + :param size: the number of bytes to read + + .. versionchanged:: 3.8 + This method now only checks for extra chunks after reading the + entire file. Previously, this method would check for extra chunks + on every call. + """ + return await self._read_size_or_line(size=size) + + def tell(self) -> int: + """Return the current position of this file.""" + return self._position + + async def seek(self, pos: int, whence: int = _SEEK_SET) -> int: + """Set the current position of this file. + + :param pos: the position (or offset if using relative + positioning) to seek to + :param whence: where to seek + from. :attr:`os.SEEK_SET` (``0``) for absolute file + positioning, :attr:`os.SEEK_CUR` (``1``) to seek relative + to the current position, :attr:`os.SEEK_END` (``2``) to + seek relative to the file's end. + + .. versionchanged:: 4.1 + The method now returns the new position in the file, to + conform to the behavior of :meth:`io.IOBase.seek`. + """ + if whence == _SEEK_SET: + new_pos = pos + elif whence == _SEEK_CUR: + new_pos = self._position + pos + elif whence == _SEEK_END: + new_pos = int(self.length) + pos + else: + raise OSError(22, "Invalid value for `whence`") + + if new_pos < 0: + raise OSError(22, "Invalid value for `pos` - must be positive") + + # Optimization, continue using the same buffer and chunk iterator. + if new_pos == self._position: + return new_pos + + self._position = new_pos + self._buffer = EMPTY + self._buffer_pos = 0 + if self._chunk_iter: + await self._chunk_iter.close() + self._chunk_iter = None + return new_pos + + def seekable(self) -> bool: + return True + + def __aiter__(self) -> AsyncGridOut: + """Return an iterator over all of this file's data. + + The iterator will return lines (delimited by ``b'\\n'``) of + :class:`bytes`. This can be useful when serving files + using a webserver that handles such an iterator efficiently. + + .. versionchanged:: 3.8 + The iterator now raises :class:`CorruptGridFile` when encountering + any truncated, missing, or extra chunk in a file. The previous + behavior was to only raise :class:`CorruptGridFile` on a missing + chunk. + + .. versionchanged:: 4.0 + The iterator now iterates over *lines* in the file, instead + of chunks, to conform to the base class :py:class:`io.IOBase`. + Use :meth:`GridOut.readchunk` to read chunk by chunk instead + of line by line. + """ + return self + + async def close(self) -> None: + """Make GridOut more generically file-like.""" + if self._chunk_iter: + await self._chunk_iter.close() + self._chunk_iter = None + if _IS_SYNC: + super().close() + else: + self.closed = True + + def write(self, value: Any) -> NoReturn: + raise io.UnsupportedOperation("write") + + def writelines(self, lines: Any) -> NoReturn: + raise io.UnsupportedOperation("writelines") + + def writable(self) -> bool: + return False + + async def __aenter__(self) -> AsyncGridOut: + """Makes it possible to use :class:`AsyncGridOut` files + with the async context manager protocol. + """ + return self + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Any: + """Makes it possible to use :class:`AsyncGridOut` files + with the async context manager protocol. + """ + await self.close() + return False + + def fileno(self) -> NoReturn: + raise io.UnsupportedOperation("fileno") + + def flush(self) -> None: + # GridOut is read-only, so flush does nothing. + pass + + def isatty(self) -> bool: + return False + + def truncate(self, size: Optional[int] = None) -> NoReturn: + # See https://docs.python.org/3/library/io.html#io.IOBase.writable + # for why truncate has to raise. + raise io.UnsupportedOperation("truncate") + + # Override IOBase.__del__ otherwise it will lead to __getattr__ on + # __IOBase_closed which calls _ensure_file and potentially performs I/O. + # We cannot do I/O in __del__ since it can lead to a deadlock. + def __del__(self) -> None: + pass + + +class _AsyncGridOutChunkIterator: + """Iterates over a file's chunks using a single cursor. + + Raises CorruptGridFile when encountering any truncated, missing, or extra + chunk in a file. + """ + + def __init__( + self, + grid_out: AsyncGridOut, + chunks: AsyncCollection[Any], + session: Optional[AsyncClientSession], + next_chunk: Any, + ) -> None: + self._id = grid_out._id + self._chunk_size = int(grid_out.chunk_size) + self._length = int(grid_out.length) + self._chunks = chunks + self._session = session + self._next_chunk = next_chunk + self._num_chunks = math.ceil(float(self._length) / self._chunk_size) + self._cursor = None + + _cursor: Optional[AsyncCursor[Any]] + + def expected_chunk_length(self, chunk_n: int) -> int: + if chunk_n < self._num_chunks - 1: + return self._chunk_size + return self._length - (self._chunk_size * (self._num_chunks - 1)) + + def __aiter__(self) -> _AsyncGridOutChunkIterator: + return self + + def _create_cursor(self) -> None: + filter = {"files_id": self._id} + if self._next_chunk > 0: + filter["n"] = {"$gte": self._next_chunk} + _disallow_transactions(self._session) + self._cursor = self._chunks.find(filter, sort=[("n", 1)], session=self._session) + + async def _next_with_retry(self) -> Mapping[str, Any]: + """Return the next chunk and retry once on CursorNotFound. + + We retry on CursorNotFound to maintain backwards compatibility in + cases where two calls to read occur more than 10 minutes apart (the + server's default cursor timeout). + """ + if self._cursor is None: + self._create_cursor() + assert self._cursor is not None + try: + return await self._cursor.next() + except CursorNotFound: + await self._cursor.close() + self._create_cursor() + return await self._cursor.next() + + async def next(self) -> Mapping[str, Any]: + try: + chunk = await self._next_with_retry() + except StopAsyncIteration: + if self._next_chunk >= self._num_chunks: + raise + raise CorruptGridFile("no chunk #%d" % self._next_chunk) from None + + if chunk["n"] != self._next_chunk: + await self.close() + raise CorruptGridFile( + "Missing chunk: expected chunk #%d but found " + "chunk with n=%d" % (self._next_chunk, chunk["n"]) + ) + + if chunk["n"] >= self._num_chunks: + # According to spec, ignore extra chunks if they are empty. + if len(chunk["data"]): + await self.close() + raise CorruptGridFile( + "Extra chunk found: expected %d chunks but found " + "chunk with n=%d" % (self._num_chunks, chunk["n"]) + ) + + expected_length = self.expected_chunk_length(chunk["n"]) + if len(chunk["data"]) != expected_length: + await self.close() + raise CorruptGridFile( + "truncated chunk #%d: expected chunk length to be %d but " + "found chunk with length %d" % (chunk["n"], expected_length, len(chunk["data"])) + ) + + self._next_chunk += 1 + return chunk + + __anext__ = next + + async def close(self) -> None: + if self._cursor: + await self._cursor.close() + self._cursor = None + + +class AsyncGridOutIterator: + def __init__( + self, grid_out: AsyncGridOut, chunks: AsyncCollection[Any], session: AsyncClientSession + ): + self._chunk_iter = _AsyncGridOutChunkIterator(grid_out, chunks, session, 0) + + def __aiter__(self) -> AsyncGridOutIterator: + return self + + async def next(self) -> bytes: + chunk = await self._chunk_iter.next() + return bytes(chunk["data"]) + + __anext__ = next + + +class AsyncGridOutCursor(AsyncCursor): # type: ignore[type-arg] + """A cursor / iterator for returning GridOut objects as the result + of an arbitrary query against the GridFS files collection. + """ + + def __init__( + self, + collection: AsyncCollection[Any], + filter: Optional[Mapping[str, Any]] = None, + skip: int = 0, + limit: int = 0, + no_cursor_timeout: bool = False, + sort: Optional[Any] = None, + batch_size: int = 0, + session: Optional[AsyncClientSession] = None, + ) -> None: + """Create a new cursor, similar to the normal + :class:`~pymongo.cursor.Cursor`. + + Should not be called directly by application developers - see + the :class:`~gridfs.GridFS` method :meth:`~gridfs.GridFS.find` instead. + + .. versionadded 2.7 + + .. seealso:: The MongoDB documentation on `cursors `_. + """ + _disallow_transactions(session) + collection = _clear_entity_type_registry(collection) + + # Hold on to the base "fs" collection to create GridOut objects later. + self._root_collection = collection + + super().__init__( + collection.files, + filter, + skip=skip, + limit=limit, + no_cursor_timeout=no_cursor_timeout, + sort=sort, + batch_size=batch_size, + session=session, + ) + + async def next(self) -> AsyncGridOut: + """Get next GridOut object from cursor.""" + _disallow_transactions(self.session) + next_file = await super().next() + return AsyncGridOut(self._root_collection, file_document=next_file, session=self.session) + + async def to_list(self, length: Optional[int] = None) -> list[AsyncGridOut]: + """Convert the cursor to a list.""" + if length is None: + return [x async for x in self] # noqa: C416,RUF100 + if length < 1: + raise ValueError("to_list() length must be greater than 0") + ret = [] + for _ in range(length): + ret.append(await self.next()) + return ret + + __anext__ = next + + def add_option(self, *args: Any, **kwargs: Any) -> NoReturn: + raise NotImplementedError("Method does not exist for GridOutCursor") + + def remove_option(self, *args: Any, **kwargs: Any) -> NoReturn: + raise NotImplementedError("Method does not exist for GridOutCursor") + + def _clone_base(self, session: Optional[AsyncClientSession]) -> AsyncGridOutCursor: + """Creates an empty GridOutCursor for information to be copied into.""" + return AsyncGridOutCursor(self._root_collection, session=session) diff --git a/.venv/lib/python3.12/site-packages/gridfs/errors.py b/.venv/lib/python3.12/site-packages/gridfs/errors.py new file mode 100644 index 0000000..e8c02ce --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/errors.py @@ -0,0 +1,34 @@ +# Copyright 2009-2015 MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Exceptions raised by the :mod:`gridfs` package""" +from __future__ import annotations + +from pymongo.errors import PyMongoError + + +class GridFSError(PyMongoError): + """Base class for all GridFS exceptions.""" + + +class CorruptGridFile(GridFSError): + """Raised when a file in :class:`~gridfs.GridFS` is malformed.""" + + +class NoFile(GridFSError): + """Raised when trying to read from a non-existent file.""" + + +class FileExists(GridFSError): + """Raised when trying to create a file that already exists.""" diff --git a/.venv/lib/python3.12/site-packages/gridfs/grid_file.py b/.venv/lib/python3.12/site-packages/gridfs/grid_file.py new file mode 100644 index 0000000..b2cab71 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/grid_file.py @@ -0,0 +1,18 @@ +# Copyright 2024-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Re-import of synchronous gridfs API for compatibility.""" +from __future__ import annotations + +from gridfs.synchronous.grid_file import * # noqa: F403 diff --git a/.venv/lib/python3.12/site-packages/gridfs/grid_file_shared.py b/.venv/lib/python3.12/site-packages/gridfs/grid_file_shared.py new file mode 100644 index 0000000..79a0ad7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/grid_file_shared.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import os +import warnings +from typing import Any, Optional + +from pymongo import ASCENDING +from pymongo.common import MAX_MESSAGE_SIZE +from pymongo.errors import InvalidOperation + +_SEEK_SET = os.SEEK_SET +_SEEK_CUR = os.SEEK_CUR +_SEEK_END = os.SEEK_END + +EMPTY = b"" +NEWLN = b"\n" + +"""Default chunk size, in bytes.""" +# Slightly under a power of 2, to work well with server's record allocations. +DEFAULT_CHUNK_SIZE = 255 * 1024 +# The number of chunked bytes to buffer before calling insert_many. +_UPLOAD_BUFFER_SIZE = MAX_MESSAGE_SIZE +# The number of chunk documents to buffer before calling insert_many. +_UPLOAD_BUFFER_CHUNKS = 100000 +# Rough BSON overhead of a chunk document not including the chunk data itself. +# Essentially len(encode({"_id": ObjectId(), "files_id": ObjectId(), "n": 1, "data": ""})) +_CHUNK_OVERHEAD = 60 + +_C_INDEX: dict[str, Any] = {"files_id": ASCENDING, "n": ASCENDING} +_F_INDEX: dict[str, Any] = {"filename": ASCENDING, "uploadDate": ASCENDING} + + +def _a_grid_in_property( + field_name: str, + docstring: str, + read_only: Optional[bool] = False, + closed_only: Optional[bool] = False, +) -> Any: + """Create a GridIn property.""" + + warn_str = "" + if docstring.startswith("DEPRECATED,"): + warn_str = ( + f"GridIn property '{field_name}' is deprecated and will be removed in PyMongo 5.0" + ) + + def getter(self: Any) -> Any: + if warn_str: + warnings.warn(warn_str, stacklevel=2, category=DeprecationWarning) + if closed_only and not self._closed: + raise AttributeError("can only get %r on a closed file" % field_name) + # Protect against PHP-237 + if field_name == "length": + return self._file.get(field_name, 0) + return self._file.get(field_name, None) + + def setter(self: Any, value: Any) -> Any: + if warn_str: + warnings.warn(warn_str, stacklevel=2, category=DeprecationWarning) + if self._closed: + raise InvalidOperation( + "AsyncGridIn does not support __setattr__ after being closed(). Set the attribute before closing the file or use AsyncGridIn.set() instead" + ) + self._file[field_name] = value + + if read_only: + docstring += "\n\nThis attribute is read-only." + elif closed_only: + docstring = "{}\n\n{}".format( + docstring, + "This attribute is read-only and " + "can only be read after :meth:`close` " + "has been called.", + ) + + if not read_only and not closed_only: + return property(getter, setter, doc=docstring) + return property(getter, doc=docstring) + + +def _a_grid_out_property(field_name: str, docstring: str) -> Any: + """Create a GridOut property.""" + + def a_getter(self: Any) -> Any: + if not self._file: + raise InvalidOperation( + "You must call GridOut.open() before accessing " "the %s property" % field_name + ) + # Protect against PHP-237 + if field_name == "length": + return self._file.get(field_name, 0) + return self._file.get(field_name, None) + + docstring += "\n\nThis attribute is read-only." + return property(a_getter, doc=docstring) + + +def _grid_in_property( + field_name: str, + docstring: str, + read_only: Optional[bool] = False, + closed_only: Optional[bool] = False, +) -> Any: + """Create a GridIn property.""" + warn_str = "" + if docstring.startswith("DEPRECATED,"): + warn_str = ( + f"GridIn property '{field_name}' is deprecated and will be removed in PyMongo 5.0" + ) + + def getter(self: Any) -> Any: + if warn_str: + warnings.warn(warn_str, stacklevel=2, category=DeprecationWarning) + if closed_only and not self._closed: + raise AttributeError("can only get %r on a closed file" % field_name) + # Protect against PHP-237 + if field_name == "length": + return self._file.get(field_name, 0) + return self._file.get(field_name, None) + + def setter(self: Any, value: Any) -> Any: + if warn_str: + warnings.warn(warn_str, stacklevel=2, category=DeprecationWarning) + if self._closed: + self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {field_name: value}}) + self._file[field_name] = value + + if read_only: + docstring += "\n\nThis attribute is read-only." + elif closed_only: + docstring = "{}\n\n{}".format( + docstring, + "This attribute is read-only and " + "can only be read after :meth:`close` " + "has been called.", + ) + + if not read_only and not closed_only: + return property(getter, setter, doc=docstring) + return property(getter, doc=docstring) + + +def _grid_out_property(field_name: str, docstring: str) -> Any: + """Create a GridOut property.""" + warn_str = "" + if docstring.startswith("DEPRECATED,"): + warn_str = ( + f"GridOut property '{field_name}' is deprecated and will be removed in PyMongo 5.0" + ) + + def getter(self: Any) -> Any: + if warn_str: + warnings.warn(warn_str, stacklevel=2, category=DeprecationWarning) + self.open() + + # Protect against PHP-237 + if field_name == "length": + return self._file.get(field_name, 0) + return self._file.get(field_name, None) + + docstring += "\n\nThis attribute is read-only." + return property(getter, doc=docstring) + + +def _clear_entity_type_registry(entity: Any, **kwargs: Any) -> Any: + """Clear the given database/collection object's type registry.""" + codecopts = entity.codec_options.with_options(type_registry=None) + return entity.with_options(codec_options=codecopts, **kwargs) diff --git a/.venv/lib/python3.12/site-packages/gridfs/py.typed b/.venv/lib/python3.12/site-packages/gridfs/py.typed new file mode 100644 index 0000000..0f40570 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/py.typed @@ -0,0 +1,2 @@ +# PEP-561 Support File. +# "Package maintainers who wish to support type checking of their code MUST add a marker file named py.typed to their package supporting typing". diff --git a/.venv/lib/python3.12/site-packages/gridfs/synchronous/__init__.py b/.venv/lib/python3.12/site-packages/gridfs/synchronous/__init__.py new file mode 100644 index 0000000..bc27043 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gridfs/synchronous/__init__.py @@ -0,0 +1,42 @@ +# Copyright 2009-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""GridFS is a specification for storing large objects in Mongo. + +The :mod:`gridfs` package is an implementation of GridFS on top of +:mod:`pymongo`, exposing a file-like interface. + +.. seealso:: The MongoDB documentation on `gridfs `_. +""" +from __future__ import annotations + +from gridfs.errors import NoFile +from gridfs.grid_file_shared import DEFAULT_CHUNK_SIZE +from gridfs.synchronous.grid_file import ( + GridFS, + GridFSBucket, + GridIn, + GridOut, + GridOutCursor, +) + +__all__ = [ + "GridFS", + "GridFSBucket", + "NoFile", + "DEFAULT_CHUNK_SIZE", + "GridIn", + "GridOut", + "GridOutCursor", +] diff --git a/.venv/lib/python3.12/site-packages/gridfs/synchronous/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/gridfs/synchronous/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88f3af9c70fe56a823f8bd12dcb693c5a4576bd4 GIT binary patch literal 881 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 Jmt None: + if session and session.in_transaction: + raise InvalidOperation("GridFS does not support multi-document transactions") + + +class GridFS: + """An instance of GridFS on top of a single Database.""" + + def __init__(self, database: Database[Any], collection: str = "fs"): + """Create a new instance of :class:`GridFS`. + + Raises :class:`TypeError` if `database` is not an instance of + :class:`~pymongo.database.Database`. + + :param database: database to use + :param collection: root collection to use + + .. versionchanged:: 4.0 + Removed the `disable_md5` parameter. See + :ref:`removed-gridfs-checksum` for details. + + .. versionchanged:: 3.11 + Running a GridFS operation in a transaction now always raises an + error. GridFS does not support multi-document transactions. + + .. versionchanged:: 3.7 + Added the `disable_md5` parameter. + + .. versionchanged:: 3.1 + Indexes are only ensured on the first write to the DB. + + .. versionchanged:: 3.0 + `database` must use an acknowledged + :attr:`~pymongo.database.Database.write_concern` + + .. seealso:: The MongoDB documentation on `gridfs `_. + """ + if not isinstance(database, Database): + raise TypeError(f"database must be an instance of Database, not {type(database)}") + + database = _clear_entity_type_registry(database) + + if not database.write_concern.acknowledged: + raise ConfigurationError("database must use acknowledged write_concern") + + self._collection = database[collection] + self._files = self._collection.files + self._chunks = self._collection.chunks + + def new_file(self, **kwargs: Any) -> GridIn: + """Create a new file in GridFS. + + Returns a new :class:`~gridfs.grid_file.GridIn` instance to + which data can be written. Any keyword arguments will be + passed through to :meth:`~gridfs.grid_file.GridIn`. + + If the ``"_id"`` of the file is manually specified, it must + not already exist in GridFS. Otherwise + :class:`~gridfs.errors.FileExists` is raised. + + :param kwargs: keyword arguments for file creation + """ + return GridIn(self._collection, **kwargs) + + def put(self, data: Any, **kwargs: Any) -> Any: + """Put data in GridFS as a new file. + + Equivalent to doing:: + + with fs.new_file(**kwargs) as f: + f.write(data) + + `data` can be either an instance of :class:`bytes` or a file-like + object providing a :meth:`read` method. If an `encoding` keyword + argument is passed, `data` can also be a :class:`str` instance, which + will be encoded as `encoding` before being written. Any keyword + arguments will be passed through to the created file - see + :meth:`~gridfs.grid_file.GridIn` for possible arguments. Returns the + ``"_id"`` of the created file. + + If the ``"_id"`` of the file is manually specified, it must + not already exist in GridFS. Otherwise + :class:`~gridfs.errors.FileExists` is raised. + + :param data: data to be written as a file. + :param kwargs: keyword arguments for file creation + + .. versionchanged:: 3.0 + w=0 writes to GridFS are now prohibited. + """ + with GridIn(self._collection, **kwargs) as grid_file: + grid_file.write(data) + return grid_file._id + + def get(self, file_id: Any, session: Optional[ClientSession] = None) -> GridOut: + """Get a file from GridFS by ``"_id"``. + + Returns an instance of :class:`~gridfs.grid_file.GridOut`, + which provides a file-like interface for reading. + + :param file_id: ``"_id"`` of the file to get + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + gout = GridOut(self._collection, file_id, session=session) + + # Raise NoFile now, instead of on first attribute access. + gout.open() + return gout + + def get_version( + self, + filename: Optional[str] = None, + version: Optional[int] = -1, + session: Optional[ClientSession] = None, + **kwargs: Any, + ) -> GridOut: + """Get a file from GridFS by ``"filename"`` or metadata fields. + + Returns a version of the file in GridFS whose filename matches + `filename` and whose metadata fields match the supplied keyword + arguments, as an instance of :class:`~gridfs.grid_file.GridOut`. + + Version numbering is a convenience atop the GridFS API provided + by MongoDB. If more than one file matches the query (either by + `filename` alone, by metadata fields, or by a combination of + both), then version ``-1`` will be the most recently uploaded + matching file, ``-2`` the second most recently + uploaded, etc. Version ``0`` will be the first version + uploaded, ``1`` the second version, etc. So if three versions + have been uploaded, then version ``0`` is the same as version + ``-3``, version ``1`` is the same as version ``-2``, and + version ``2`` is the same as version ``-1``. + + Raises :class:`~gridfs.errors.NoFile` if no such version of + that file exists. + + :param filename: ``"filename"`` of the file to get, or `None` + :param version: version of the file to get (defaults + to -1, the most recent version uploaded) + :param session: a + :class:`~pymongo.client_session.ClientSession` + :param kwargs: find files by custom metadata. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``get_version`` no longer ensures indexes. + """ + query = kwargs + if filename is not None: + query["filename"] = filename + + _disallow_transactions(session) + cursor = self._files.find(query, session=session) + if version is None: + version = -1 + if version < 0: + skip = abs(version) - 1 + cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING) + else: + cursor.limit(-1).skip(version).sort("uploadDate", ASCENDING) + try: + doc = next(cursor) + return GridOut(self._collection, file_document=doc, session=session) + except StopIteration: + raise NoFile("no version %d for filename %r" % (version, filename)) from None + + def get_last_version( + self, + filename: Optional[str] = None, + session: Optional[ClientSession] = None, + **kwargs: Any, + ) -> GridOut: + """Get the most recent version of a file in GridFS by ``"filename"`` + or metadata fields. + + Equivalent to calling :meth:`get_version` with the default + `version` (``-1``). + + :param filename: ``"filename"`` of the file to get, or `None` + :param session: a + :class:`~pymongo.client_session.ClientSession` + :param kwargs: find files by custom metadata. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + return self.get_version(filename=filename, session=session, **kwargs) + + # TODO add optional safe mode for chunk removal? + def delete(self, file_id: Any, session: Optional[ClientSession] = None) -> None: + """Delete a file from GridFS by ``"_id"``. + + Deletes all data belonging to the file with ``"_id"``: + `file_id`. + + .. warning:: Any processes/threads reading from the file while + this method is executing will likely see an invalid/corrupt + file. Care should be taken to avoid concurrent reads to a file + while it is being deleted. + + .. note:: Deletes of non-existent files are considered successful + since the end result is the same: no file with that _id remains. + + :param file_id: ``"_id"`` of the file to delete + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``delete`` no longer ensures indexes. + """ + _disallow_transactions(session) + self._files.delete_one({"_id": file_id}, session=session) + self._chunks.delete_many({"files_id": file_id}, session=session) + + def list(self, session: Optional[ClientSession] = None) -> list[str]: + """List the names of all files stored in this instance of + :class:`GridFS`. + + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.1 + ``list`` no longer ensures indexes. + """ + _disallow_transactions(session) + # With an index, distinct includes documents with no filename + # as None. + return [ + name for name in self._files.distinct("filename", session=session) if name is not None + ] + + def find_one( + self, + filter: Optional[Any] = None, + session: Optional[ClientSession] = None, + *args: Any, + **kwargs: Any, + ) -> Optional[GridOut]: + """Get a single file from gridfs. + + All arguments to :meth:`find` are also valid arguments for + :meth:`find_one`, although any `limit` argument will be + ignored. Returns a single :class:`~gridfs.grid_file.GridOut`, + or ``None`` if no matching file is found. For example: + + .. code-block: python + + file = fs.find_one({"filename": "lisa.txt"}) + + :param filter: a dictionary specifying + the query to be performing OR any other type to be used as + the value for a query for ``"_id"`` in the file collection. + :param args: any additional positional arguments are + the same as the arguments to :meth:`find`. + :param session: a + :class:`~pymongo.client_session.ClientSession` + :param kwargs: any additional keyword arguments + are the same as the arguments to :meth:`find`. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + if filter is not None and not isinstance(filter, abc.Mapping): + filter = {"_id": filter} + + _disallow_transactions(session) + for f in self.find(filter, *args, session=session, **kwargs): + return f + + return None + + def find(self, *args: Any, **kwargs: Any) -> GridOutCursor: + """Query GridFS for files. + + Returns a cursor that iterates across files matching + arbitrary queries on the files collection. Can be combined + with other modifiers for additional control. For example:: + + for grid_out in fs.find({"filename": "lisa.txt"}, + no_cursor_timeout=True): + data = grid_out.read() + + would iterate through all versions of "lisa.txt" stored in GridFS. + Note that setting no_cursor_timeout to True may be important to + prevent the cursor from timing out during long multi-file processing + work. + + As another example, the call:: + + most_recent_three = fs.find().sort("uploadDate", -1).limit(3) + + would return a cursor to the three most recently uploaded files + in GridFS. + + Follows a similar interface to + :meth:`~pymongo.collection.Collection.find` + in :class:`~pymongo.collection.Collection`. + + If a :class:`~pymongo.client_session.ClientSession` is passed to + :meth:`find`, all returned :class:`~gridfs.grid_file.GridOut` instances + are associated with that session. + + :param filter: A query document that selects which files + to include in the result set. Can be an empty document to include + all files. + :param skip: the number of files to omit (from + the start of the result set) when returning the results + :param limit: the maximum number of results to + return + :param no_cursor_timeout: if False (the default), any + returned cursor is closed by the server after 10 minutes of + inactivity. If set to True, the returned cursor will never + time out on the server. Care should be taken to ensure that + cursors with no_cursor_timeout turned on are properly closed. + :param sort: a list of (key, direction) pairs + specifying the sort order for this query. See + :meth:`~pymongo.cursor.Cursor.sort` for details. + + Raises :class:`TypeError` if any of the arguments are of + improper type. Returns an instance of + :class:`~gridfs.grid_file.GridOutCursor` + corresponding to this query. + + .. versionchanged:: 3.0 + Removed the read_preference, tag_sets, and + secondary_acceptable_latency_ms options. + .. versionadded:: 2.7 + .. seealso:: The MongoDB documentation on `find `_. + """ + return GridOutCursor(self._collection, *args, **kwargs) + + def exists( + self, + document_or_id: Optional[Any] = None, + session: Optional[ClientSession] = None, + **kwargs: Any, + ) -> bool: + """Check if a file exists in this instance of :class:`GridFS`. + + The file to check for can be specified by the value of its + ``_id`` key, or by passing in a query document. A query + document can be passed in as dictionary, or by using keyword + arguments. Thus, the following three calls are equivalent: + + >>> fs.exists(file_id) + >>> fs.exists({"_id": file_id}) + >>> fs.exists(_id=file_id) + + As are the following two calls: + + >>> fs.exists({"filename": "mike.txt"}) + >>> fs.exists(filename="mike.txt") + + And the following two: + + >>> fs.exists({"foo": {"$gt": 12}}) + >>> fs.exists(foo={"$gt": 12}) + + Returns ``True`` if a matching file exists, ``False`` + otherwise. Calls to :meth:`exists` will not automatically + create appropriate indexes; application developers should be + sure to create indexes if needed and as appropriate. + + :param document_or_id: query document, or _id of the + document to check for + :param session: a + :class:`~pymongo.client_session.ClientSession` + :param kwargs: keyword arguments are used as a + query document, if they're present. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + _disallow_transactions(session) + if kwargs: + f = self._files.find_one(kwargs, ["_id"], session=session) + else: + f = self._files.find_one(document_or_id, ["_id"], session=session) + + return f is not None + + +class GridFSBucket: + """An instance of GridFS on top of a single Database.""" + + def __init__( + self, + db: Database[Any], + bucket_name: str = "fs", + chunk_size_bytes: int = DEFAULT_CHUNK_SIZE, + write_concern: Optional[WriteConcern] = None, + read_preference: Optional[_ServerMode] = None, + ) -> None: + """Create a new instance of :class:`GridFSBucket`. + + Raises :exc:`TypeError` if `database` is not an instance of + :class:`~pymongo.database.Database`. + + Raises :exc:`~pymongo.errors.ConfigurationError` if `write_concern` + is not acknowledged. + + :param database: database to use. + :param bucket_name: The name of the bucket. Defaults to 'fs'. + :param chunk_size_bytes: The chunk size in bytes. Defaults + to 255KB. + :param write_concern: The + :class:`~pymongo.write_concern.WriteConcern` to use. If ``None`` + (the default) db.write_concern is used. + :param read_preference: The read preference to use. If + ``None`` (the default) db.read_preference is used. + + .. versionchanged:: 4.0 + Removed the `disable_md5` parameter. See + :ref:`removed-gridfs-checksum` for details. + + .. versionchanged:: 3.11 + Running a GridFSBucket operation in a transaction now always raises + an error. GridFSBucket does not support multi-document transactions. + + .. versionchanged:: 3.7 + Added the `disable_md5` parameter. + + .. versionadded:: 3.1 + + .. seealso:: The MongoDB documentation on `gridfs `_. + """ + if not isinstance(db, Database): + raise TypeError(f"database must be an instance of Database, not {type(db)}") + + db = _clear_entity_type_registry(db) + + wtc = write_concern if write_concern is not None else db.write_concern + if not wtc.acknowledged: + raise ConfigurationError("write concern must be acknowledged") + + self._bucket_name = bucket_name + self._collection = db[bucket_name] + self._chunks: Collection[Any] = self._collection.chunks.with_options( + write_concern=write_concern, read_preference=read_preference + ) + + self._files: Collection[Any] = self._collection.files.with_options( + write_concern=write_concern, read_preference=read_preference + ) + + self._chunk_size_bytes = chunk_size_bytes + self._timeout = db.client.options.timeout + + def open_upload_stream( + self, + filename: str, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[ClientSession] = None, + ) -> GridIn: + """Opens a Stream that the application can write the contents of the + file to. + + The user must specify the filename, and can choose to add any + additional information in the metadata field of the file document or + modify the chunk size. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + with fs.open_upload_stream( + "test_file", chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) as grid_in: + grid_in.write("data I want to store!") + # uploaded on close + + Returns an instance of :class:`~gridfs.grid_file.GridIn`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param filename: The name of the file to upload. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + opts = { + "filename": filename, + "chunk_size": ( + chunk_size_bytes if chunk_size_bytes is not None else self._chunk_size_bytes + ), + } + if metadata is not None: + opts["metadata"] = metadata + + return GridIn(self._collection, session=session, **opts) + + def open_upload_stream_with_id( + self, + file_id: Any, + filename: str, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[ClientSession] = None, + ) -> GridIn: + """Opens a Stream that the application can write the contents of the + file to. + + The user must specify the file id and filename, and can choose to add + any additional information in the metadata field of the file document + or modify the chunk size. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + with fs.open_upload_stream_with_id( + ObjectId(), + "test_file", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) as grid_in: + grid_in.write("data I want to store!") + # uploaded on close + + Returns an instance of :class:`~gridfs.grid_file.GridIn`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param file_id: The id to use for this file. The id must not have + already been used for another file. + :param filename: The name of the file to upload. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes in :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + + opts = { + "_id": file_id, + "filename": filename, + "chunk_size": ( + chunk_size_bytes if chunk_size_bytes is not None else self._chunk_size_bytes + ), + } + if metadata is not None: + opts["metadata"] = metadata + + return GridIn(self._collection, session=session, **opts) + + @_csot.apply + def upload_from_stream( + self, + filename: str, + source: Any, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[ClientSession] = None, + ) -> ObjectId: + """Uploads a user file to a GridFS bucket. + + Reads the contents of the user file from `source` and uploads + it to the file `filename`. Source can be a string or file-like object. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + file_id = fs.upload_from_stream( + "test_file", + "data I want to store!", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + + Returns the _id of the uploaded file. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param filename: The name of the file to upload. + :param source: The source stream of the content to be uploaded. Must be + a file-like object that implements :meth:`read` or a string. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_upload_stream(filename, chunk_size_bytes, metadata, session=session) as gin: + gin.write(source) + + return cast(ObjectId, gin._id) + + @_csot.apply + def upload_from_stream_with_id( + self, + file_id: Any, + filename: str, + source: Any, + chunk_size_bytes: Optional[int] = None, + metadata: Optional[Mapping[str, Any]] = None, + session: Optional[ClientSession] = None, + ) -> None: + """Uploads a user file to a GridFS bucket with a custom file id. + + Reads the contents of the user file from `source` and uploads + it to the file `filename`. Source can be a string or file-like object. + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + file_id = fs.upload_from_stream( + ObjectId(), + "test_file", + "data I want to store!", + chunk_size_bytes=4, + metadata={"contentType": "text/plain"}) + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + Raises :exc:`~ValueError` if `filename` is not a string. + + :param file_id: The id to use for this file. The id must not have + already been used for another file. + :param filename: The name of the file to upload. + :param source: The source stream of the content to be uploaded. Must be + a file-like object that implements :meth:`read` or a string. + :param chunk_size_bytes` (options): The number of bytes per chunk of this + file. Defaults to the chunk_size_bytes of :class:`GridFSBucket`. + :param metadata: User data for the 'metadata' field of the + files collection document. If not provided the metadata field will + be omitted from the files collection document. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_upload_stream_with_id( + file_id, filename, chunk_size_bytes, metadata, session=session + ) as gin: + gin.write(source) + + def open_download_stream( + self, file_id: Any, session: Optional[ClientSession] = None + ) -> GridOut: + """Opens a Stream from which the application can read the contents of + the stored file specified by file_id. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # get _id of file to read. + file_id = fs.upload_from_stream("test_file", "data I want to store!") + grid_out = fs.open_download_stream(file_id) + contents = grid_out.read() + + Returns an instance of :class:`~gridfs.grid_file.GridOut`. + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be downloaded. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + gout = GridOut(self._collection, file_id, session=session) + + # Raise NoFile now, instead of on first attribute access. + gout.open() + return gout + + @_csot.apply + def download_to_stream( + self, file_id: Any, destination: Any, session: Optional[ClientSession] = None + ) -> None: + """Downloads the contents of the stored file specified by file_id and + writes the contents to `destination`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to read + file_id = fs.upload_from_stream("test_file", "data I want to store!") + # Get file to write to + file = open('myfile','wb+') + fs.download_to_stream(file_id, file) + file.seek(0) + contents = file.read() + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be downloaded. + :param destination: a file-like object implementing :meth:`write`. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_download_stream(file_id, session=session) as gout: + while True: + chunk = gout.readchunk() + if not len(chunk): + break + destination.write(chunk) + + @_csot.apply + def delete(self, file_id: Any, session: Optional[ClientSession] = None) -> None: + """Given an file_id, delete this stored file's files collection document + and associated chunks from a GridFS bucket. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to delete + file_id = fs.upload_from_stream("test_file", "data I want to store!") + fs.delete(file_id) + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be deleted. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + _disallow_transactions(session) + res = self._files.delete_one({"_id": file_id}, session=session) + self._chunks.delete_many({"files_id": file_id}, session=session) + if not res.deleted_count: + raise NoFile("no file could be deleted because none matched %s" % file_id) + + @_csot.apply + def delete_by_name(self, filename: str, session: Optional[ClientSession] = None) -> None: + """Given a filename, delete this stored file's files collection document(s) + and associated chunks from a GridFS bucket. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + fs.upload_from_stream("test_file", "data I want to store!") + fs.delete_by_name("test_file") + + Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists. + + :param filename: The name of the file to be deleted. + :param session: a :class:`~pymongo.client_session.ClientSession` + + .. versionadded:: 4.12 + """ + _disallow_transactions(session) + files = self._files.find({"filename": filename}, {"_id": 1}, session=session) + file_ids = [file["_id"] for file in files] + res = self._files.delete_many({"_id": {"$in": file_ids}}, session=session) + self._chunks.delete_many({"files_id": {"$in": file_ids}}, session=session) + if not res.deleted_count: + raise NoFile(f"no file could be deleted because none matched filename {filename!r}") + + def find(self, *args: Any, **kwargs: Any) -> GridOutCursor: + """Find and return the files collection documents that match ``filter`` + + Returns a cursor that iterates across files matching + arbitrary queries on the files collection. Can be combined + with other modifiers for additional control. + + For example:: + + for grid_data in fs.find({"filename": "lisa.txt"}, + no_cursor_timeout=True): + data = grid_data.read() + + would iterate through all versions of "lisa.txt" stored in GridFS. + Note that setting no_cursor_timeout to True may be important to + prevent the cursor from timing out during long multi-file processing + work. + + As another example, the call:: + + most_recent_three = fs.find().sort("uploadDate", -1).limit(3) + + would return a cursor to the three most recently uploaded files + in GridFS. + + Follows a similar interface to + :meth:`~pymongo.collection.Collection.find` + in :class:`~pymongo.collection.Collection`. + + If a :class:`~pymongo.client_session.ClientSession` is passed to + :meth:`find`, all returned :class:`~gridfs.grid_file.GridOut` instances + are associated with that session. + + :param filter: Search query. + :param batch_size: The number of documents to return per + batch. + :param limit: The maximum number of documents to return. + :param no_cursor_timeout: The server normally times out idle + cursors after an inactivity period (10 minutes) to prevent excess + memory use. Set this option to True prevent that. + :param skip: The number of documents to skip before + returning. + :param sort: The order by which to sort results. Defaults to + None. + """ + return GridOutCursor(self._collection, *args, **kwargs) + + def open_download_stream_by_name( + self, filename: str, revision: int = -1, session: Optional[ClientSession] = None + ) -> GridOut: + """Opens a Stream from which the application can read the contents of + `filename` and optional `revision`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + grid_out = fs.open_download_stream_by_name("test_file") + contents = grid_out.read() + + Returns an instance of :class:`~gridfs.grid_file.GridOut`. + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + + Raises :exc:`~ValueError` filename is not a string. + + :param filename: The name of the file to read from. + :param revision: Which revision (documents with the same + filename and different uploadDate) of the file to retrieve. + Defaults to -1 (the most recent revision). + :param session: a + :class:`~pymongo.client_session.ClientSession` + + :Note: Revision numbers are defined as follows: + + - 0 = the original stored file + - 1 = the first revision + - 2 = the second revision + - etc... + - -2 = the second most recent revision + - -1 = the most recent revision + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + validate_string("filename", filename) + query = {"filename": filename} + _disallow_transactions(session) + cursor = self._files.find(query, session=session) + if revision < 0: + skip = abs(revision) - 1 + cursor.limit(-1).skip(skip).sort("uploadDate", DESCENDING) + else: + cursor.limit(-1).skip(revision).sort("uploadDate", ASCENDING) + try: + grid_file = next(cursor) + return GridOut(self._collection, file_document=grid_file, session=session) + except StopIteration: + raise NoFile("no version %d for filename %r" % (revision, filename)) from None + + @_csot.apply + def download_to_stream_by_name( + self, + filename: str, + destination: Any, + revision: int = -1, + session: Optional[ClientSession] = None, + ) -> None: + """Write the contents of `filename` (with optional `revision`) to + `destination`. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get file to write to + file = open('myfile','wb') + fs.download_to_stream_by_name("test_file", file) + + Raises :exc:`~gridfs.errors.NoFile` if no such version of + that file exists. + + Raises :exc:`~ValueError` if `filename` is not a string. + + :param filename: The name of the file to read from. + :param destination: A file-like object that implements :meth:`write`. + :param revision: Which revision (documents with the same + filename and different uploadDate) of the file to retrieve. + Defaults to -1 (the most recent revision). + :param session: a + :class:`~pymongo.client_session.ClientSession` + + :Note: Revision numbers are defined as follows: + + - 0 = the original stored file + - 1 = the first revision + - 2 = the second revision + - etc... + - -2 = the second most recent revision + - -1 = the most recent revision + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + with self.open_download_stream_by_name(filename, revision, session=session) as gout: + while True: + chunk = gout.readchunk() + if not len(chunk): + break + destination.write(chunk) + + def rename( + self, file_id: Any, new_filename: str, session: Optional[ClientSession] = None + ) -> None: + """Renames the stored file with the specified file_id. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + # Get _id of file to rename + file_id = fs.upload_from_stream("test_file", "data I want to store!") + fs.rename(file_id, "new_test_name") + + Raises :exc:`~gridfs.errors.NoFile` if no file with file_id exists. + + :param file_id: The _id of the file to be renamed. + :param new_filename: The new name of the file. + :param session: a + :class:`~pymongo.client_session.ClientSession` + + .. versionchanged:: 3.6 + Added ``session`` parameter. + """ + _disallow_transactions(session) + result = self._files.update_one( + {"_id": file_id}, {"$set": {"filename": new_filename}}, session=session + ) + if not result.matched_count: + raise NoFile( + "no files could be renamed %r because none " + "matched file_id %i" % (new_filename, file_id) + ) + + def rename_by_name( + self, filename: str, new_filename: str, session: Optional[ClientSession] = None + ) -> None: + """Renames the stored file with the specified filename. + + For example:: + + my_db = MongoClient().test + fs = GridFSBucket(my_db) + fs.upload_from_stream("test_file", "data I want to store!") + fs.rename_by_name("test_file", "new_test_name") + + Raises :exc:`~gridfs.errors.NoFile` if no file with the given filename exists. + + :param filename: The filename of the file to be renamed. + :param new_filename: The new name of the file. + :param session: a :class:`~pymongo.client_session.ClientSession` + + .. versionadded:: 4.12 + """ + _disallow_transactions(session) + result = self._files.update_many( + {"filename": filename}, {"$set": {"filename": new_filename}}, session=session + ) + if not result.matched_count: + raise NoFile( + f"no files could be renamed {new_filename!r} because none matched filename {filename!r}" + ) + + +class GridIn: + """Class to write data to GridFS.""" + + def __init__( + self, + root_collection: Collection[Any], + session: Optional[ClientSession] = None, + **kwargs: Any, + ) -> None: + """Write a file to GridFS + + Application developers should generally not need to + instantiate this class directly - instead see the methods + provided by :class:`~gridfs.GridFS`. + + Raises :class:`TypeError` if `root_collection` is not an + instance of :class:`~pymongo.collection.Collection`. + + Any of the file level options specified in the `GridFS Spec + `_ may be passed as + keyword arguments. Any additional keyword arguments will be + set as additional fields on the file document. Valid keyword + arguments include: + + - ``"_id"``: unique ID for this file (default: + :class:`~bson.objectid.ObjectId`) - this ``"_id"`` must + not have already been used for another file + + - ``"filename"``: human name for the file + + - ``"contentType"`` or ``"content_type"``: valid mime-type + for the file + + - ``"chunkSize"`` or ``"chunk_size"``: size of each of the + chunks, in bytes (default: 255 kb) + + - ``"encoding"``: encoding used for this file. Any :class:`str` + that is written to the file will be converted to :class:`bytes`. + + :param root_collection: root collection to write to + :param session: a + :class:`~pymongo.client_session.ClientSession` to use for all + commands + :param kwargs: Any: file level options (see above) + + .. versionchanged:: 4.0 + Removed the `disable_md5` parameter. See + :ref:`removed-gridfs-checksum` for details. + + .. versionchanged:: 3.7 + Added the `disable_md5` parameter. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + `root_collection` must use an acknowledged + :attr:`~pymongo.collection.Collection.write_concern` + """ + if not isinstance(root_collection, Collection): + raise TypeError( + f"root_collection must be an instance of Collection, not {type(root_collection)}" + ) + + if not root_collection.write_concern.acknowledged: + raise ConfigurationError("root_collection must use acknowledged write_concern") + _disallow_transactions(session) + + # Handle alternative naming + if "content_type" in kwargs: + kwargs["contentType"] = kwargs.pop("content_type") + if "chunk_size" in kwargs: + kwargs["chunkSize"] = kwargs.pop("chunk_size") + + coll = _clear_entity_type_registry(root_collection, read_preference=ReadPreference.PRIMARY) + + # Defaults + kwargs["_id"] = kwargs.get("_id", ObjectId()) + kwargs["chunkSize"] = kwargs.get("chunkSize", DEFAULT_CHUNK_SIZE) + object.__setattr__(self, "_session", session) + object.__setattr__(self, "_coll", coll) + object.__setattr__(self, "_chunks", coll.chunks) + object.__setattr__(self, "_file", kwargs) + object.__setattr__(self, "_buffer", io.BytesIO()) + object.__setattr__(self, "_position", 0) + object.__setattr__(self, "_chunk_number", 0) + object.__setattr__(self, "_closed", False) + object.__setattr__(self, "_ensured_index", False) + object.__setattr__(self, "_buffered_docs", []) + object.__setattr__(self, "_buffered_docs_size", 0) + + def _create_index(self, collection: Collection[Any], index_key: Any, unique: bool) -> None: + doc = collection.find_one(projection={"_id": 1}, session=self._session) + if doc is None: + try: + index_keys = [ + index_spec["key"] + for index_spec in collection.list_indexes(session=self._session) + ] + except OperationFailure: + index_keys = [] + if index_key not in index_keys: + collection.create_index(index_key.items(), unique=unique, session=self._session) + + def _ensure_indexes(self) -> None: + if not object.__getattribute__(self, "_ensured_index"): + _disallow_transactions(self._session) + self._create_index(self._coll.files, _F_INDEX, False) + self._create_index(self._coll.chunks, _C_INDEX, True) + object.__setattr__(self, "_ensured_index", True) + + def abort(self) -> None: + """Remove all chunks/files that may have been uploaded and close.""" + self._coll.chunks.delete_many({"files_id": self._file["_id"]}, session=self._session) + self._coll.files.delete_one({"_id": self._file["_id"]}, session=self._session) + object.__setattr__(self, "_closed", True) + + @property + def closed(self) -> bool: + """Is this file closed?""" + return self._closed + + _id: Any = _grid_in_property("_id", "The ``'_id'`` value for this file.", read_only=True) + filename: Optional[str] = _grid_in_property("filename", "Name of this file.") + name: Optional[str] = _grid_in_property("filename", "Alias for `filename`.") + content_type: Optional[str] = _grid_in_property( + "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." + ) + length: int = _grid_in_property("length", "Length (in bytes) of this file.", closed_only=True) + chunk_size: int = _grid_in_property("chunkSize", "Chunk size for this file.", read_only=True) + upload_date: datetime.datetime = _grid_in_property( + "uploadDate", "Date that this file was uploaded.", closed_only=True + ) + md5: Optional[str] = _grid_in_property( + "md5", + "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", + closed_only=True, + ) + + _buffer: io.BytesIO + _closed: bool + _buffered_docs: list[dict[str, Any]] + _buffered_docs_size: int + + def __getattr__(self, name: str) -> Any: + if name == "_coll": + return object.__getattribute__(self, name) + elif name in self._file: + return self._file[name] + raise AttributeError("GridIn object has no attribute '%s'" % name) + + def __setattr__(self, name: str, value: Any) -> None: + # For properties of this instance like _buffer, or descriptors set on + # the class like filename, use regular __setattr__ + if name in self.__dict__ or name in self.__class__.__dict__: + object.__setattr__(self, name, value) + else: + # All other attributes are part of the document in db.fs.files. + # Store them to be sent to server on close() or if closed, send + # them now. + self._file[name] = value + if self._closed: + if _IS_SYNC: + self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {name: value}}) + else: + raise AttributeError( + "GridIn does not support __setattr__ after being closed(). Set the attribute before closing the file or use GridIn.set() instead" + ) + + def set(self, name: str, value: Any) -> None: + self._file[name] = value + if self._closed: + self._coll.files.update_one({"_id": self._file["_id"]}, {"$set": {name: value}}) + + def _flush_data(self, data: Any, force: bool = False) -> None: + """Flush `data` to a chunk.""" + self._ensure_indexes() + assert len(data) <= self.chunk_size + if data: + self._buffered_docs.append( + {"files_id": self._file["_id"], "n": self._chunk_number, "data": data} + ) + self._buffered_docs_size += len(data) + _CHUNK_OVERHEAD + if not self._buffered_docs: + return + # Limit to 100,000 chunks or 32MB (+1 chunk) of data. + if ( + force + or self._buffered_docs_size >= _UPLOAD_BUFFER_SIZE + or len(self._buffered_docs) >= _UPLOAD_BUFFER_CHUNKS + ): + try: + self._chunks.insert_many(self._buffered_docs, session=self._session) + except BulkWriteError as exc: + # For backwards compatibility, raise an insert_one style exception. + write_errors = exc.details["writeErrors"] + for err in write_errors: + if err.get("code") in (11000, 11001, 12582): # Duplicate key errors + self._raise_file_exists(self._file["_id"]) + result = {"writeErrors": write_errors} + wces = exc.details["writeConcernErrors"] + if wces: + result["writeConcernError"] = wces[-1] + _check_write_command_response(result) + raise + self._buffered_docs = [] + self._buffered_docs_size = 0 + self._chunk_number += 1 + self._position += len(data) + + def _flush_buffer(self, force: bool = False) -> None: + """Flush the buffer contents out to a chunk.""" + self._flush_data(self._buffer.getvalue(), force=force) + self._buffer.close() + self._buffer = io.BytesIO() + + def _flush(self) -> Any: + """Flush the file to the database.""" + try: + self._flush_buffer(force=True) + # The GridFS spec says length SHOULD be an Int64. + self._file["length"] = Int64(self._position) + self._file["uploadDate"] = datetime.datetime.now(tz=datetime.timezone.utc) + + return self._coll.files.insert_one(self._file, session=self._session) + except DuplicateKeyError: + self._raise_file_exists(self._id) + + def _raise_file_exists(self, file_id: Any) -> NoReturn: + """Raise a FileExists exception for the given file_id.""" + raise FileExists("file with _id %r already exists" % file_id) + + def close(self) -> None: + """Flush the file and close it. + + A closed file cannot be written any more. Calling + :meth:`close` more than once is allowed. + """ + if not self._closed: + self._flush() + object.__setattr__(self, "_closed", True) + + def read(self, size: int = -1) -> NoReturn: + raise io.UnsupportedOperation("read") + + def readable(self) -> bool: + return False + + def seekable(self) -> bool: + return False + + def write(self, data: Any) -> None: + """Write data to the file. There is no return value. + + `data` can be either a string of bytes or a file-like object + (implementing :meth:`read`). If the file has an + :attr:`encoding` attribute, `data` can also be a + :class:`str` instance, which will be encoded as + :attr:`encoding` before being written. + + Due to buffering, the data may not actually be written to the + database until the :meth:`close` method is called. Raises + :class:`ValueError` if this file is already closed. Raises + :class:`TypeError` if `data` is not an instance of + :class:`bytes`, a file-like object, or an instance of :class:`str`. + Unicode data is only allowed if the file has an :attr:`encoding` + attribute. + + :param data: string of bytes or file-like object to be written + to the file + """ + if self._closed: + raise ValueError("cannot write to a closed file") + + try: + # file-like + read = data.read + except AttributeError: + # string + if not isinstance(data, (str, bytes)): + raise TypeError("can only write strings or file-like objects") from None + if isinstance(data, str): + try: + data = data.encode(self.encoding) + except AttributeError: + raise TypeError( + "must specify an encoding for file in order to write str" + ) from None + read = io.BytesIO(data).read + + if inspect.iscoroutinefunction(read): + self._write_async(read) + else: + if self._buffer.tell() > 0: + # Make sure to flush only when _buffer is complete + space = self.chunk_size - self._buffer.tell() + if space: + try: + to_write = read(space) + except BaseException: + self.abort() + raise + self._buffer.write(to_write) + if len(to_write) < space: + return # EOF or incomplete + self._flush_buffer() + to_write = read(self.chunk_size) + while to_write and len(to_write) == self.chunk_size: + self._flush_data(to_write) + to_write = read(self.chunk_size) + self._buffer.write(to_write) + + def _write_async(self, read: Any) -> None: + if self._buffer.tell() > 0: + # Make sure to flush only when _buffer is complete + space = self.chunk_size - self._buffer.tell() + if space: + try: + to_write = read(space) + except BaseException: + self.abort() + raise + self._buffer.write(to_write) + if len(to_write) < space: + return # EOF or incomplete + self._flush_buffer() + to_write = read(self.chunk_size) + while to_write and len(to_write) == self.chunk_size: + self._flush_data(to_write) + to_write = read(self.chunk_size) + self._buffer.write(to_write) + + def writelines(self, sequence: Iterable[Any]) -> None: + """Write a sequence of strings to the file. + + Does not add separators. + """ + for line in sequence: + self.write(line) + + def writeable(self) -> bool: + return True + + def __enter__(self) -> GridIn: + """Support for the context manager protocol.""" + return self + + def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Any: + """Support for the context manager protocol. + + Close the file if no exceptions occur and allow exceptions to propagate. + """ + if exc_type is None: + # No exceptions happened. + self.close() + else: + # Something happened, at minimum mark as closed. + object.__setattr__(self, "_closed", True) + + # propagate exceptions + return False + + +GRIDOUT_BASE_CLASS = io.IOBase if _IS_SYNC else object # type: Any + + +class GridOut(GRIDOUT_BASE_CLASS): # type: ignore + + """Class to read data out of GridFS.""" + + def __init__( + self, + root_collection: Collection[Any], + file_id: Optional[int] = None, + file_document: Optional[Any] = None, + session: Optional[ClientSession] = None, + ) -> None: + """Read a file from GridFS + + Application developers should generally not need to + instantiate this class directly - instead see the methods + provided by :class:`~gridfs.GridFS`. + + Either `file_id` or `file_document` must be specified, + `file_document` will be given priority if present. Raises + :class:`TypeError` if `root_collection` is not an instance of + :class:`~pymongo.collection.Collection`. + + :param root_collection: root collection to read from + :param file_id: value of ``"_id"`` for the file to read + :param file_document: file document from + `root_collection.files` + :param session: a + :class:`~pymongo.client_session.ClientSession` to use for all + commands + + .. versionchanged:: 3.8 + For better performance and to better follow the GridFS spec, + :class:`GridOut` now uses a single cursor to read all the chunks in + the file. + + .. versionchanged:: 3.6 + Added ``session`` parameter. + + .. versionchanged:: 3.0 + Creating a GridOut does not immediately retrieve the file metadata + from the server. Metadata is fetched when first needed. + """ + if not isinstance(root_collection, Collection): + raise TypeError( + f"root_collection must be an instance of Collection, not {type(root_collection)}" + ) + _disallow_transactions(session) + + root_collection = _clear_entity_type_registry(root_collection) + + super().__init__() + + self._chunks = root_collection.chunks + self._files = root_collection.files + self._file_id = file_id + self._buffer = EMPTY + # Start position within the current buffered chunk. + self._buffer_pos = 0 + self._chunk_iter = None + # Position within the total file. + self._position = 0 + self._file = file_document + self._session = session + if not _IS_SYNC: + self.closed = False + + _id: Any = _grid_out_property("_id", "The ``'_id'`` value for this file.") + filename: str = _grid_out_property("filename", "Name of this file.") + name: str = _grid_out_property("filename", "Alias for `filename`.") + content_type: Optional[str] = _grid_out_property( + "contentType", "DEPRECATED, will be removed in PyMongo 5.0. Mime-type for this file." + ) + length: int = _grid_out_property("length", "Length (in bytes) of this file.") + chunk_size: int = _grid_out_property("chunkSize", "Chunk size for this file.") + upload_date: datetime.datetime = _grid_out_property( + "uploadDate", "Date that this file was first uploaded." + ) + aliases: Optional[list[str]] = _grid_out_property( + "aliases", "DEPRECATED, will be removed in PyMongo 5.0. List of aliases for this file." + ) + metadata: Optional[Mapping[str, Any]] = _grid_out_property( + "metadata", "Metadata attached to this file." + ) + md5: Optional[str] = _grid_out_property( + "md5", + "DEPRECATED, will be removed in PyMongo 5.0. MD5 of the contents of this file if an md5 sum was created.", + ) + + _file: Any + _chunk_iter: Any + + if not _IS_SYNC: + closed: bool + + def __next__(self) -> bytes: + line = self.readline() + if line: + return line + raise StopIteration() + + def to_list(self) -> list[bytes]: + return [x for x in self] # noqa: C416, RUF100 + + def readline(self, size: int = -1) -> bytes: + """Read one line or up to `size` bytes from the file. + + :param size: the maximum number of bytes to read + """ + return self._read_size_or_line(size=size, line=True) + + def readlines(self, size: int = -1) -> list[bytes]: + """Read one line or up to `size` bytes from the file. + + :param size: the maximum number of bytes to read + """ + self.open() + lines = [] + remainder = int(self.length) - self._position + bytes_read = 0 + while remainder > 0: + line = self._read_size_or_line(line=True) + bytes_read += len(line) + lines.append(line) + remainder = int(self.length) - self._position + if 0 < size < bytes_read: + break + + return lines + + def open(self) -> None: + if not self._file: + _disallow_transactions(self._session) + self._file = self._files.find_one({"_id": self._file_id}, session=self._session) + if not self._file: + raise NoFile( + f"no file in gridfs collection {self._files!r} with _id {self._file_id!r}" + ) + + def __getattr__(self, name: str) -> Any: + if _IS_SYNC: + self.open() # type: ignore[unused-coroutine] + elif not self._file: + raise InvalidOperation( + "You must call GridOut.open() before accessing the %s property" % name + ) + if name in self._file: + return self._file[name] + raise AttributeError("GridOut object has no attribute '%s'" % name) + + def readable(self) -> bool: + return True + + def readchunk(self) -> bytes: + """Reads a chunk at a time. If the current position is within a + chunk the remainder of the chunk is returned. + """ + self.open() + received = len(self._buffer) - self._buffer_pos + chunk_data = EMPTY + chunk_size = int(self.chunk_size) + + if received > 0: + chunk_data = self._buffer[self._buffer_pos :] + elif self._position < int(self.length): + chunk_number = int((received + self._position) / chunk_size) + if self._chunk_iter is None: + self._chunk_iter = GridOutChunkIterator( + self, self._chunks, self._session, chunk_number + ) + + chunk = self._chunk_iter.next() + chunk_data = chunk["data"][self._position % chunk_size :] + + if not chunk_data: + raise CorruptGridFile("truncated chunk") + + self._position += len(chunk_data) + self._buffer = EMPTY + self._buffer_pos = 0 + return chunk_data + + def _read_size_or_line(self, size: int = -1, line: bool = False) -> bytes: + """Internal read() and readline() helper.""" + self.open() + remainder = int(self.length) - self._position + if size < 0 or size > remainder: + size = remainder + + if size == 0: + return EMPTY + + received = 0 + data = [] + while received < size: + needed = size - received + if self._buffer: + # Optimization: Read the buffer with zero byte copies. + buf = self._buffer + chunk_start = self._buffer_pos + chunk_data = memoryview(buf)[self._buffer_pos :] + self._buffer = EMPTY + self._buffer_pos = 0 + self._position += len(chunk_data) + else: + buf = self.readchunk() + chunk_start = 0 + chunk_data = memoryview(buf) + if line: + pos = buf.find(NEWLN, chunk_start, chunk_start + needed) - chunk_start + if pos >= 0: + # Decrease size to exit the loop. + size = received + pos + 1 + needed = pos + 1 + if len(chunk_data) > needed: + data.append(chunk_data[:needed]) + # Optimization: Save the buffer with zero byte copies. + self._buffer = buf + self._buffer_pos = chunk_start + needed + self._position -= len(self._buffer) - self._buffer_pos + else: + data.append(chunk_data) + received += len(chunk_data) + + # Detect extra chunks after reading the entire file. + if size == remainder and self._chunk_iter: + try: + self._chunk_iter.next() + except StopIteration: + pass + + return b"".join(data) + + def read(self, size: int = -1) -> bytes: + """Read at most `size` bytes from the file (less if there + isn't enough data). + + The bytes are returned as an instance of :class:`bytes` + If `size` is negative or omitted all data is read. + + :param size: the number of bytes to read + + .. versionchanged:: 3.8 + This method now only checks for extra chunks after reading the + entire file. Previously, this method would check for extra chunks + on every call. + """ + return self._read_size_or_line(size=size) + + def tell(self) -> int: + """Return the current position of this file.""" + return self._position + + def seek(self, pos: int, whence: int = _SEEK_SET) -> int: + """Set the current position of this file. + + :param pos: the position (or offset if using relative + positioning) to seek to + :param whence: where to seek + from. :attr:`os.SEEK_SET` (``0``) for absolute file + positioning, :attr:`os.SEEK_CUR` (``1``) to seek relative + to the current position, :attr:`os.SEEK_END` (``2``) to + seek relative to the file's end. + + .. versionchanged:: 4.1 + The method now returns the new position in the file, to + conform to the behavior of :meth:`io.IOBase.seek`. + """ + if whence == _SEEK_SET: + new_pos = pos + elif whence == _SEEK_CUR: + new_pos = self._position + pos + elif whence == _SEEK_END: + new_pos = int(self.length) + pos + else: + raise OSError(22, "Invalid value for `whence`") + + if new_pos < 0: + raise OSError(22, "Invalid value for `pos` - must be positive") + + # Optimization, continue using the same buffer and chunk iterator. + if new_pos == self._position: + return new_pos + + self._position = new_pos + self._buffer = EMPTY + self._buffer_pos = 0 + if self._chunk_iter: + self._chunk_iter.close() + self._chunk_iter = None + return new_pos + + def seekable(self) -> bool: + return True + + def __iter__(self) -> GridOut: + """Return an iterator over all of this file's data. + + The iterator will return lines (delimited by ``b'\\n'``) of + :class:`bytes`. This can be useful when serving files + using a webserver that handles such an iterator efficiently. + + .. versionchanged:: 3.8 + The iterator now raises :class:`CorruptGridFile` when encountering + any truncated, missing, or extra chunk in a file. The previous + behavior was to only raise :class:`CorruptGridFile` on a missing + chunk. + + .. versionchanged:: 4.0 + The iterator now iterates over *lines* in the file, instead + of chunks, to conform to the base class :py:class:`io.IOBase`. + Use :meth:`GridOut.readchunk` to read chunk by chunk instead + of line by line. + """ + return self + + def close(self) -> None: + """Make GridOut more generically file-like.""" + if self._chunk_iter: + self._chunk_iter.close() + self._chunk_iter = None + if _IS_SYNC: + super().close() + else: + self.closed = True + + def write(self, value: Any) -> NoReturn: + raise io.UnsupportedOperation("write") + + def writelines(self, lines: Any) -> NoReturn: + raise io.UnsupportedOperation("writelines") + + def writable(self) -> bool: + return False + + def __enter__(self) -> GridOut: + """Makes it possible to use :class:`GridOut` files + with the async context manager protocol. + """ + return self + + def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> Any: + """Makes it possible to use :class:`GridOut` files + with the async context manager protocol. + """ + self.close() + return False + + def fileno(self) -> NoReturn: + raise io.UnsupportedOperation("fileno") + + def flush(self) -> None: + # GridOut is read-only, so flush does nothing. + pass + + def isatty(self) -> bool: + return False + + def truncate(self, size: Optional[int] = None) -> NoReturn: + # See https://docs.python.org/3/library/io.html#io.IOBase.writable + # for why truncate has to raise. + raise io.UnsupportedOperation("truncate") + + # Override IOBase.__del__ otherwise it will lead to __getattr__ on + # __IOBase_closed which calls _ensure_file and potentially performs I/O. + # We cannot do I/O in __del__ since it can lead to a deadlock. + def __del__(self) -> None: + pass + + +class GridOutChunkIterator: + """Iterates over a file's chunks using a single cursor. + + Raises CorruptGridFile when encountering any truncated, missing, or extra + chunk in a file. + """ + + def __init__( + self, + grid_out: GridOut, + chunks: Collection[Any], + session: Optional[ClientSession], + next_chunk: Any, + ) -> None: + self._id = grid_out._id + self._chunk_size = int(grid_out.chunk_size) + self._length = int(grid_out.length) + self._chunks = chunks + self._session = session + self._next_chunk = next_chunk + self._num_chunks = math.ceil(float(self._length) / self._chunk_size) + self._cursor = None + + _cursor: Optional[Cursor[Any]] + + def expected_chunk_length(self, chunk_n: int) -> int: + if chunk_n < self._num_chunks - 1: + return self._chunk_size + return self._length - (self._chunk_size * (self._num_chunks - 1)) + + def __iter__(self) -> GridOutChunkIterator: + return self + + def _create_cursor(self) -> None: + filter = {"files_id": self._id} + if self._next_chunk > 0: + filter["n"] = {"$gte": self._next_chunk} + _disallow_transactions(self._session) + self._cursor = self._chunks.find(filter, sort=[("n", 1)], session=self._session) + + def _next_with_retry(self) -> Mapping[str, Any]: + """Return the next chunk and retry once on CursorNotFound. + + We retry on CursorNotFound to maintain backwards compatibility in + cases where two calls to read occur more than 10 minutes apart (the + server's default cursor timeout). + """ + if self._cursor is None: + self._create_cursor() + assert self._cursor is not None + try: + return self._cursor.next() + except CursorNotFound: + self._cursor.close() + self._create_cursor() + return self._cursor.next() + + def next(self) -> Mapping[str, Any]: + try: + chunk = self._next_with_retry() + except StopIteration: + if self._next_chunk >= self._num_chunks: + raise + raise CorruptGridFile("no chunk #%d" % self._next_chunk) from None + + if chunk["n"] != self._next_chunk: + self.close() + raise CorruptGridFile( + "Missing chunk: expected chunk #%d but found " + "chunk with n=%d" % (self._next_chunk, chunk["n"]) + ) + + if chunk["n"] >= self._num_chunks: + # According to spec, ignore extra chunks if they are empty. + if len(chunk["data"]): + self.close() + raise CorruptGridFile( + "Extra chunk found: expected %d chunks but found " + "chunk with n=%d" % (self._num_chunks, chunk["n"]) + ) + + expected_length = self.expected_chunk_length(chunk["n"]) + if len(chunk["data"]) != expected_length: + self.close() + raise CorruptGridFile( + "truncated chunk #%d: expected chunk length to be %d but " + "found chunk with length %d" % (chunk["n"], expected_length, len(chunk["data"])) + ) + + self._next_chunk += 1 + return chunk + + __next__ = next + + def close(self) -> None: + if self._cursor: + self._cursor.close() + self._cursor = None + + +class GridOutIterator: + def __init__(self, grid_out: GridOut, chunks: Collection[Any], session: ClientSession): + self._chunk_iter = GridOutChunkIterator(grid_out, chunks, session, 0) + + def __iter__(self) -> GridOutIterator: + return self + + def next(self) -> bytes: + chunk = self._chunk_iter.next() + return bytes(chunk["data"]) + + __next__ = next + + +class GridOutCursor(Cursor): # type: ignore[type-arg] + """A cursor / iterator for returning GridOut objects as the result + of an arbitrary query against the GridFS files collection. + """ + + def __init__( + self, + collection: Collection[Any], + filter: Optional[Mapping[str, Any]] = None, + skip: int = 0, + limit: int = 0, + no_cursor_timeout: bool = False, + sort: Optional[Any] = None, + batch_size: int = 0, + session: Optional[ClientSession] = None, + ) -> None: + """Create a new cursor, similar to the normal + :class:`~pymongo.cursor.Cursor`. + + Should not be called directly by application developers - see + the :class:`~gridfs.GridFS` method :meth:`~gridfs.GridFS.find` instead. + + .. versionadded 2.7 + + .. seealso:: The MongoDB documentation on `cursors `_. + """ + _disallow_transactions(session) + collection = _clear_entity_type_registry(collection) + + # Hold on to the base "fs" collection to create GridOut objects later. + self._root_collection = collection + + super().__init__( + collection.files, + filter, + skip=skip, + limit=limit, + no_cursor_timeout=no_cursor_timeout, + sort=sort, + batch_size=batch_size, + session=session, + ) + + def next(self) -> GridOut: + """Get next GridOut object from cursor.""" + _disallow_transactions(self.session) + next_file = super().next() + return GridOut(self._root_collection, file_document=next_file, session=self.session) + + def to_list(self, length: Optional[int] = None) -> list[GridOut]: + """Convert the cursor to a list.""" + if length is None: + return [x for x in self] # noqa: C416,RUF100 + if length < 1: + raise ValueError("to_list() length must be greater than 0") + ret = [] + for _ in range(length): + ret.append(self.next()) + return ret + + __next__ = next + + def add_option(self, *args: Any, **kwargs: Any) -> NoReturn: + raise NotImplementedError("Method does not exist for GridOutCursor") + + def remove_option(self, *args: Any, **kwargs: Any) -> NoReturn: + raise NotImplementedError("Method does not exist for GridOutCursor") + + def _clone_base(self, session: Optional[ClientSession]) -> GridOutCursor: + """Creates an empty GridOutCursor for information to be copied into.""" + return GridOutCursor(self._root_collection, session=session) diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt new file mode 100644 index 0000000..0e63548 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt @@ -0,0 +1,760 @@ +@Switch01 +A_Rog +Aakanksha Agrawal +Abhinav Sagar +ABHYUDAY PRATAP SINGH +abs51295 +AceGentile +Adam Chainz +Adam Tse +Adam Wentz +admin +Adrien Morison +ahayrapetyan +Ahilya +AinsworthK +Akash Srivastava +Alan Yee +Albert Tugushev +Albert-Guan +albertg +Alberto Sottile +Aleks Bunin +Ales Erjavec +Alethea Flowers +Alex Gaynor +Alex Grönholm +Alex Hedges +Alex Loosley +Alex Morega +Alex Stachowiak +Alexander Shtyrov +Alexandre Conrad +Alexey Popravka +Aleš Erjavec +Alli +Ami Fischman +Ananya Maiti +Anatoly Techtonik +Anders Kaseorg +Andre Aguiar +Andreas Lutro +Andrei Geacar +Andrew Gaul +Andrew Shymanel +Andrey Bienkowski +Andrey Bulgakov +Andrés Delfino +Andy Freeland +Andy Kluger +Ani Hayrapetyan +Aniruddha Basak +Anish Tambe +Anrs Hu +Anthony Sottile +Antoine Musso +Anton Ovchinnikov +Anton Patrushev +Antonio Alvarado Hernandez +Antony Lee +Antti Kaihola +Anubhav Patel +Anudit Nagar +Anuj Godase +AQNOUCH Mohammed +AraHaan +Arindam Choudhury +Armin Ronacher +Artem +Arun Babu Neelicattu +Ashley Manton +Ashwin Ramaswami +atse +Atsushi Odagiri +Avinash Karhana +Avner Cohen +Awit (Ah-Wit) Ghirmai +Baptiste Mispelon +Barney Gale +barneygale +Bartek Ogryczak +Bastian Venthur +Ben Bodenmiller +Ben Darnell +Ben Hoyt +Ben Mares +Ben Rosser +Bence Nagy +Benjamin Peterson +Benjamin VanEvery +Benoit Pierre +Berker Peksag +Bernard +Bernard Tyers +Bernardo B. Marques +Bernhard M. Wiedemann +Bertil Hatt +Bhavam Vidyarthi +Blazej Michalik +Bogdan Opanchuk +BorisZZZ +Brad Erickson +Bradley Ayers +Brandon L. Reiss +Brandt Bucher +Brett Randall +Brett Rosen +Brian Cristante +Brian Rosner +briantracy +BrownTruck +Bruno Oliveira +Bruno Renié +Bruno S +Bstrdsmkr +Buck Golemon +burrows +Bussonnier Matthias +bwoodsend +c22 +Caleb Martinez +Calvin Smith +Carl Meyer +Carlos Liam +Carol Willing +Carter Thayer +Cass +Chandrasekhar Atina +Chih-Hsuan Yen +Chris Brinker +Chris Hunt +Chris Jerdonek +Chris Kuehl +Chris McDonough +Chris Pawley +Chris Pryer +Chris Wolfe +Christian Clauss +Christian Heimes +Christian Oudard +Christoph Reiter +Christopher Hunt +Christopher Snyder +cjc7373 +Clark Boylan +Claudio Jolowicz +Clay McClure +Cody +Cody Soyland +Colin Watson +Collin Anderson +Connor Osborn +Cooper Lees +Cooper Ry Lees +Cory Benfield +Cory Wright +Craig Kerstiens +Cristian Sorinel +Cristina +Cristina Muñoz +Curtis Doty +cytolentino +Daan De Meyer +Dale +Damian +Damian Quiroga +Damian Shaw +Dan Black +Dan Savilonis +Dan Sully +Dane Hillard +daniel +Daniel Collins +Daniel Hahler +Daniel Holth +Daniel Jost +Daniel Katz +Daniel Shaulov +Daniele Esposti +Daniele Nicolodi +Daniele Procida +Daniil Konovalenko +Danny Hermes +Danny McClanahan +Darren Kavanagh +Dav Clark +Dave Abrahams +Dave Jones +David Aguilar +David Black +David Bordeynik +David Caro +David D Lowe +David Evans +David Hewitt +David Linke +David Poggi +David Pursehouse +David Runge +David Tucker +David Wales +Davidovich +ddelange +Deepak Sharma +Deepyaman Datta +Denise Yu +dependabot[bot] +derwolfe +Desetude +Devesh Kumar Singh +Diego Caraballo +Diego Ramirez +DiegoCaraballo +Dimitri Merejkowsky +Dimitri Papadopoulos +Dirk Stolle +Dmitry Gladkov +Dmitry Volodin +Domen Kožar +Dominic Davis-Foster +Donald Stufft +Dongweiming +doron zarhi +Dos Moonen +Douglas Thor +DrFeathers +Dustin Ingram +Dwayne Bailey +Ed Morley +Edgar Ramírez +Edgar Ramírez Mondragón +Ee Durbin +Efflam Lemaillet +efflamlemaillet +Eitan Adler +ekristina +elainechan +Eli Schwartz +Elisha Hollander +Ellen Marie Dash +Emil Burzo +Emil Styrke +Emmanuel Arias +Endoh Takanao +enoch +Erdinc Mutlu +Eric Cousineau +Eric Gillingham +Eric Hanchrow +Eric Hopper +Erik M. Bray +Erik Rose +Erwin Janssen +Eugene Vereshchagin +everdimension +Federico +Felipe Peter +Felix Yan +fiber-space +Filip Kokosiński +Filipe Laíns +Finn Womack +finnagin +Flavio Amurrio +Florian Briand +Florian Rathgeber +Francesco +Francesco Montesano +Frost Ming +Gabriel Curio +Gabriel de Perthuis +Garry Polley +gavin +gdanielson +Geoffrey Sneddon +George Song +Georgi Valkov +Georgy Pchelkin +ghost +Giftlin Rajaiah +gizmoguy1 +gkdoc +Godefroid Chapelle +Gopinath M +GOTO Hayato +gousaiyang +gpiks +Greg Roodt +Greg Ward +Guilherme Espada +Guillaume Seguin +gutsytechster +Guy Rozendorn +Guy Tuval +gzpan123 +Hanjun Kim +Hari Charan +Harsh Vardhan +harupy +Harutaka Kawamura +hauntsaninja +Henrich Hartzer +Henry Schreiner +Herbert Pfennig +Holly Stotelmyer +Honnix +Hsiaoming Yang +Hugo Lopes Tavares +Hugo van Kemenade +Hugues Bruant +Hynek Schlawack +Ian Bicking +Ian Cordasco +Ian Lee +Ian Stapleton Cordasco +Ian Wienand +Igor Kuzmitshov +Igor Sobreira +Ilan Schnell +Illia Volochii +Ilya Baryshev +Inada Naoki +Ionel Cristian Mărieș +Ionel Maries Cristian +Itamar Turner-Trauring +Ivan Pozdeev +J. Nick Koston +Jacob Kim +Jacob Walls +Jaime Sanz +jakirkham +Jakub Kuczys +Jakub Stasiak +Jakub Vysoky +Jakub Wilk +James Cleveland +James Curtin +James Firth +James Gerity +James Polley +Jan Pokorný +Jannis Leidel +Jarek Potiuk +jarondl +Jason Curtis +Jason R. Coombs +JasonMo +JasonMo1 +Jay Graves +Jean Abou Samra +Jean-Christophe Fillion-Robin +Jeff Barber +Jeff Dairiki +Jeff Widman +Jelmer Vernooij +jenix21 +Jeremy Stanley +Jeremy Zafran +Jesse Rittner +Jiashuo Li +Jim Fisher +Jim Garrison +Jiun Bae +Jivan Amara +Joe Bylund +Joe Michelini +John Paton +John T. Wodder II +John-Scott Atlakson +johnthagen +Jon Banafato +Jon Dufresne +Jon Parise +Jonas Nockert +Jonathan Herbert +Joonatan Partanen +Joost Molenaar +Jorge Niedbalski +Joseph Bylund +Joseph Long +Josh Bronson +Josh Hansen +Josh Schneier +Joshua +Juan Luis Cano Rodríguez +Juanjo Bazán +Judah Rand +Julian Berman +Julian Gethmann +Julien Demoor +Jussi Kukkonen +jwg4 +Jyrki Pulliainen +Kai Chen +Kai Mueller +Kamal Bin Mustafa +kasium +kaustav haldar +keanemind +Keith Maxwell +Kelsey Hightower +Kenneth Belitzky +Kenneth Reitz +Kevin Burke +Kevin Carter +Kevin Frommelt +Kevin R Patterson +Kexuan Sun +Kit Randel +Klaas van Schelven +KOLANICH +kpinc +Krishna Oza +Kumar McMillan +Kurt McKee +Kyle Persohn +lakshmanaram +Laszlo Kiss-Kollar +Laurent Bristiel +Laurent LAPORTE +Laurie O +Laurie Opperman +layday +Leon Sasson +Lev Givon +Lincoln de Sousa +Lipis +lorddavidiii +Loren Carvalho +Lucas Cimon +Ludovic Gasc +Lukas Geiger +Lukas Juhrich +Luke Macken +Luo Jiebin +luojiebin +luz.paz +László Kiss Kollár +M00nL1ght +Marc Abramowitz +Marc Tamlyn +Marcus Smith +Mariatta +Mark Kohler +Mark Williams +Markus Hametner +Martey Dodoo +Martin Fischer +Martin Häcker +Martin Pavlasek +Masaki +Masklinn +Matej Stuchlik +Mathew Jennings +Mathieu Bridon +Mathieu Kniewallner +Matt Bacchi +Matt Good +Matt Maker +Matt Robenolt +matthew +Matthew Einhorn +Matthew Feickert +Matthew Gilliard +Matthew Iversen +Matthew Treinish +Matthew Trumbell +Matthew Willson +Matthias Bussonnier +mattip +Maurits van Rees +Max W Chase +Maxim Kurnikov +Maxime Rouyrre +mayeut +mbaluna +mdebi +memoselyk +meowmeowcat +Michael +Michael Aquilina +Michael E. Karpeles +Michael Klich +Michael Mintz +Michael Williamson +michaelpacer +Michał Górny +Mickaël Schoentgen +Miguel Araujo Perez +Mihir Singh +Mike +Mike Hendricks +Min RK +MinRK +Miro Hrončok +Monica Baluna +montefra +Monty Taylor +Muha Ajjan‮ +Nadav Wexler +Nahuel Ambrosini +Nate Coraor +Nate Prewitt +Nathan Houghton +Nathaniel J. Smith +Nehal J Wani +Neil Botelho +Nguyễn Gia Phong +Nicholas Serra +Nick Coghlan +Nick Stenning +Nick Timkovich +Nicolas Bock +Nicole Harris +Nikhil Benesch +Nikhil Ladha +Nikita Chepanov +Nikolay Korolev +Nipunn Koorapati +Nitesh Sharma +Niyas Sait +Noah +Noah Gorny +Nowell Strite +NtaleGrey +nvdv +OBITORASU +Ofek Lev +ofrinevo +Oliver Freund +Oliver Jeeves +Oliver Mannion +Oliver Tonnhofer +Olivier Girardot +Olivier Grisel +Ollie Rutherfurd +OMOTO Kenji +Omry Yadan +onlinejudge95 +Oren Held +Oscar Benjamin +Oz N Tiram +Pachwenko +Patrick Dubroy +Patrick Jenkins +Patrick Lawson +patricktokeeffe +Patrik Kopkan +Paul Ganssle +Paul Kehrer +Paul Moore +Paul Nasrat +Paul Oswald +Paul van der Linden +Paulus Schoutsen +Pavel Safronov +Pavithra Eswaramoorthy +Pawel Jasinski +Paweł Szramowski +Pekka Klärck +Peter Gessler +Peter Lisák +Peter Waller +petr-tik +Phaneendra Chiruvella +Phil Elson +Phil Freo +Phil Pennock +Phil Whelan +Philip Jägenstedt +Philip Molloy +Philippe Ombredanne +Pi Delport +Pierre-Yves Rofes +Pieter Degroote +pip +Prabakaran Kumaresshan +Prabhjyotsing Surjit Singh Sodhi +Prabhu Marappan +Pradyun Gedam +Prashant Sharma +Pratik Mallya +pre-commit-ci[bot] +Preet Thakkar +Preston Holmes +Przemek Wrzos +Pulkit Goyal +q0w +Qiangning Hong +Qiming Xu +Quentin Lee +Quentin Pradet +R. David Murray +Rafael Caricio +Ralf Schmitt +Razzi Abuissa +rdb +Reece Dunham +Remi Rampin +Rene Dudfield +Riccardo Magliocchetti +Riccardo Schirone +Richard Jones +Richard Si +Ricky Ng-Adam +Rishi +RobberPhex +Robert Collins +Robert McGibbon +Robert Pollak +Robert T. McGibbon +robin elisha robinson +Roey Berman +Rohan Jain +Roman Bogorodskiy +Roman Donchenko +Romuald Brunet +ronaudinho +Ronny Pfannschmidt +Rory McCann +Ross Brattain +Roy Wellington Ⅳ +Ruairidh MacLeod +Russell Keith-Magee +Ryan Shepherd +Ryan Wooden +ryneeverett +Sachi King +Salvatore Rinchiera +sandeepkiran-js +Sander Van Balen +Savio Jomton +schlamar +Scott Kitterman +Sean +seanj +Sebastian Jordan +Sebastian Schaetz +Segev Finer +SeongSoo Cho +Sergey Vasilyev +Seth Michael Larson +Seth Woodworth +Shahar Epstein +Shantanu +shireenrao +Shivansh-007 +Shlomi Fish +Shovan Maity +Simeon Visser +Simon Cross +Simon Pichugin +sinoroc +sinscary +snook92 +socketubs +Sorin Sbarnea +Srinivas Nyayapati +Stavros Korokithakis +Stefan Scherfke +Stefano Rivera +Stephan Erb +Stephen Rosen +stepshal +Steve (Gadget) Barnes +Steve Barnes +Steve Dower +Steve Kowalik +Steven Myint +Steven Silvester +stonebig +studioj +Stéphane Bidoul +Stéphane Bidoul (ACSONE) +Stéphane Klein +Sumana Harihareswara +Surbhi Sharma +Sviatoslav Sydorenko +Swat009 +Sylvain +Takayuki SHIMIZUKAWA +Taneli Hukkinen +tbeswick +Thiago +Thijs Triemstra +Thomas Fenzl +Thomas Grainger +Thomas Guettler +Thomas Johansson +Thomas Kluyver +Thomas Smith +Thomas VINCENT +Tim D. Smith +Tim Gates +Tim Harder +Tim Heap +tim smith +tinruufu +Tobias Hermann +Tom Forbes +Tom Freudenheim +Tom V +Tomas Hrnciar +Tomas Orsava +Tomer Chachamu +Tommi Enenkel | AnB +Tomáš Hrnčiar +Tony Beswick +Tony Narlock +Tony Zhaocheng Tan +TonyBeswick +toonarmycaptain +Toshio Kuratomi +toxinu +Travis Swicegood +Tushar Sadhwani +Tzu-ping Chung +Valentin Haenel +Victor Stinner +victorvpaulo +Vikram - Google +Viktor Szépe +Ville Skyttä +Vinay Sajip +Vincent Philippon +Vinicyus Macedo +Vipul Kumar +Vitaly Babiy +Vladimir Fokow +Vladimir Rutsky +W. Trevor King +Wil Tan +Wilfred Hughes +William Edwards +William ML Leslie +William T Olson +William Woodruff +Wilson Mo +wim glenn +Winson Luk +Wolfgang Maier +Wu Zhenyu +XAMES3 +Xavier Fernandez +xoviat +xtreak +YAMAMOTO Takashi +Yen Chi Hsuan +Yeray Diaz Diaz +Yoval P +Yu Jian +Yuan Jing Vincent Yan +Yusuke Hayashi +Zearin +Zhiping Deng +ziebam +Zvezdan Petkovic +Łukasz Langa +Роман Донченко +Семён Марьясин +‮rekcäH nitraM‮ diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/LICENSE.txt b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/LICENSE.txt new file mode 100644 index 0000000..8e7b65e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-present The pip developers (see AUTHORS.txt file) + +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/pip-24.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/METADATA new file mode 100644 index 0000000..e5b45bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/METADATA @@ -0,0 +1,88 @@ +Metadata-Version: 2.1 +Name: pip +Version: 24.0 +Summary: The PyPA recommended tool for installing Python packages. +Author-email: The pip developers +License: MIT +Project-URL: Homepage, https://pip.pypa.io/ +Project-URL: Documentation, https://pip.pypa.io +Project-URL: Source, https://github.com/pypa/pip +Project-URL: Changelog, https://pip.pypa.io/en/stable/news/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +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 +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +License-File: AUTHORS.txt + +pip - The Python Package Installer +================================== + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.org/project/pip/ + :alt: PyPI + +.. image:: https://img.shields.io/pypi/pyversions/pip + :target: https://pypi.org/project/pip + :alt: PyPI - Python Version + +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest + :target: https://pip.pypa.io/en/latest + :alt: Documentation + +pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes. + +Please take a look at our documentation for how to install and use pip: + +* `Installation`_ +* `Usage`_ + +We release updates regularly, with a new version every 3 months. Find more details in our documentation: + +* `Release notes`_ +* `Release process`_ + +If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms: + +* `Issue tracking`_ +* `Discourse channel`_ +* `User IRC`_ + +If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms: + +* `GitHub page`_ +* `Development documentation`_ +* `Development IRC`_ + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _package installer: https://packaging.python.org/guides/tool-recommendations/ +.. _Python Package Index: https://pypi.org +.. _Installation: https://pip.pypa.io/en/stable/installation/ +.. _Usage: https://pip.pypa.io/en/stable/ +.. _Release notes: https://pip.pypa.io/en/stable/news.html +.. _Release process: https://pip.pypa.io/en/latest/development/release-process/ +.. _GitHub page: https://github.com/pypa/pip +.. _Development documentation: https://pip.pypa.io/en/latest/development +.. _Issue tracking: https://github.com/pypa/pip/issues +.. _Discourse channel: https://discuss.python.org/c/packaging +.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa +.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD new file mode 100644 index 0000000..513310c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD @@ -0,0 +1,1005 @@ +../../../bin/pip,sha256=NS7XtIkzvoN0KTr4LPni77mQnJcH43vAVKghPUDBmhk,272 +../../../bin/pip3,sha256=NS7XtIkzvoN0KTr4LPni77mQnJcH43vAVKghPUDBmhk,272 +../../../bin/pip3.12,sha256=NS7XtIkzvoN0KTr4LPni77mQnJcH43vAVKghPUDBmhk,272 +pip-24.0.dist-info/AUTHORS.txt,sha256=SwXm4nkwRkmtnO1ZY-dLy7EPeoQNXMNLby5CN3GlNhY,10388 +pip-24.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-24.0.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093 +pip-24.0.dist-info/METADATA,sha256=kNEfJ3_Vho2mee4lfJdlbd5RHIqsfQJSMUB-bOkIOeI,3581 +pip-24.0.dist-info/RECORD,, +pip-24.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip-24.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92 +pip-24.0.dist-info/entry_points.txt,sha256=Fa_c0b-xGFaYxagIruvpJD6qqXmNTA02vAVIkmMj-9o,125 +pip-24.0.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/__init__.py,sha256=oAk1nFpLmUVS5Ln7NxvNoGUn5Vkn6FGQjPaNDf8Q8pk,355 +pip/__main__.py,sha256=WzbhHXTbSE6gBY19mNN9m4s5o_365LOvTYSgqgbdBhE,854 +pip/__pip-runner__.py,sha256=EnrfKmKMzWAdqg_JicLCOP9Y95Ux7zHh4ObvqLtQcjo,1444 +pip/__pycache__/__init__.cpython-312.pyc,, +pip/__pycache__/__main__.cpython-312.pyc,, +pip/__pycache__/__pip-runner__.cpython-312.pyc,, +pip/_internal/__init__.py,sha256=iqZ5-YQsQV08tkUc7L806Reop6tguLFWf70ySF6be0Y,515 +pip/_internal/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/__pycache__/build_env.cpython-312.pyc,, +pip/_internal/__pycache__/cache.cpython-312.pyc,, +pip/_internal/__pycache__/configuration.cpython-312.pyc,, +pip/_internal/__pycache__/exceptions.cpython-312.pyc,, +pip/_internal/__pycache__/main.cpython-312.pyc,, +pip/_internal/__pycache__/pyproject.cpython-312.pyc,, +pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc,, +pip/_internal/__pycache__/wheel_builder.cpython-312.pyc,, +pip/_internal/build_env.py,sha256=1ESpqw0iupS_K7phZK5zshVE5Czy9BtGLFU4W6Enva8,10243 +pip/_internal/cache.py,sha256=uiYD-9F0Bv1C8ZyWE85lpzDmQf7hcUkgL99GmI8I41Q,10370 +pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 +pip/_internal/cli/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc,, +pip/_internal/cli/__pycache__/base_command.cpython-312.pyc,, +pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc,, +pip/_internal/cli/__pycache__/command_context.cpython-312.pyc,, +pip/_internal/cli/__pycache__/main.cpython-312.pyc,, +pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc,, +pip/_internal/cli/__pycache__/parser.cpython-312.pyc,, +pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc,, +pip/_internal/cli/__pycache__/req_command.cpython-312.pyc,, +pip/_internal/cli/__pycache__/spinners.cpython-312.pyc,, +pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc,, +pip/_internal/cli/autocompletion.py,sha256=_br_5NgSxSuvPjMF0MLHzS5s6BpSkQAQHKrLK89VauM,6690 +pip/_internal/cli/base_command.py,sha256=iuVWGa2oTq7gBReo0er3Z0tXJ2oqBIC6QjDHcnDhKXY,8733 +pip/_internal/cli/cmdoptions.py,sha256=V8ggG6AtHpHKkH_6tRU0mhJaZTeqtrFpu75ghvMXXJk,30063 +pip/_internal/cli/command_context.py,sha256=RHgIPwtObh5KhMrd3YZTkl8zbVG-6Okml7YbFX4Ehg0,774 +pip/_internal/cli/main.py,sha256=Uzxt_YD1hIvB1AW5mxt6IVcht5G712AtMqdo51UMhmQ,2816 +pip/_internal/cli/main_parser.py,sha256=laDpsuBDl6kyfywp9eMMA9s84jfH2TJJn-vmL0GG90w,4338 +pip/_internal/cli/parser.py,sha256=KW6C3-7-4ErTNB0TfLTKwOdHcd-qefCeGnrOoE2r0RQ,10781 +pip/_internal/cli/progress_bars.py,sha256=So4mPoSjXkXiSHiTzzquH3VVyVD_njXlHJSExYPXAow,1968 +pip/_internal/cli/req_command.py,sha256=c7_XHABnXmD3_qlK9-r37KqdKBAcgmVKvQ2WcTrNLfc,18369 +pip/_internal/cli/spinners.py,sha256=hIJ83GerdFgFCdobIA23Jggetegl_uC4Sp586nzFbPE,5118 +pip/_internal/cli/status_codes.py,sha256=sEFHUaUJbqv8iArL3HAtcztWZmGOFX01hTesSytDEh0,116 +pip/_internal/commands/__init__.py,sha256=5oRO9O3dM2vGuh0bFw4HOVletryrz5HHMmmPWwJrH9U,3882 +pip/_internal/commands/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/commands/__pycache__/cache.cpython-312.pyc,, +pip/_internal/commands/__pycache__/check.cpython-312.pyc,, +pip/_internal/commands/__pycache__/completion.cpython-312.pyc,, +pip/_internal/commands/__pycache__/configuration.cpython-312.pyc,, +pip/_internal/commands/__pycache__/debug.cpython-312.pyc,, +pip/_internal/commands/__pycache__/download.cpython-312.pyc,, +pip/_internal/commands/__pycache__/freeze.cpython-312.pyc,, +pip/_internal/commands/__pycache__/hash.cpython-312.pyc,, +pip/_internal/commands/__pycache__/help.cpython-312.pyc,, +pip/_internal/commands/__pycache__/index.cpython-312.pyc,, +pip/_internal/commands/__pycache__/inspect.cpython-312.pyc,, +pip/_internal/commands/__pycache__/install.cpython-312.pyc,, +pip/_internal/commands/__pycache__/list.cpython-312.pyc,, +pip/_internal/commands/__pycache__/search.cpython-312.pyc,, +pip/_internal/commands/__pycache__/show.cpython-312.pyc,, +pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc,, +pip/_internal/commands/__pycache__/wheel.cpython-312.pyc,, +pip/_internal/commands/cache.py,sha256=xg76_ZFEBC6zoQ3gXLRfMZJft4z2a0RwH4GEFZC6nnU,7944 +pip/_internal/commands/check.py,sha256=Rb13Q28yoLh0j1gpx5SU0jlResNct21eQCRsnaO9xKA,1782 +pip/_internal/commands/completion.py,sha256=HT4lD0bgsflHq2IDgYfiEdp7IGGtE7s6MgI3xn0VQEw,4287 +pip/_internal/commands/configuration.py,sha256=n98enwp6y0b5G6fiRQjaZo43FlJKYve_daMhN-4BRNc,9766 +pip/_internal/commands/debug.py,sha256=63972uUCeMIGOdMMVeIUGrOjTOqTVWplFC82a-hcKyA,6777 +pip/_internal/commands/download.py,sha256=e4hw088zGo26WmJaMIRvCniLlLmoOjqolGyfHjsCkCQ,5335 +pip/_internal/commands/freeze.py,sha256=qrIHS_-c6JPrQ92hMhAv9kkl0bHgFpRLwYJDdbcYr1o,3243 +pip/_internal/commands/hash.py,sha256=EVVOuvGtoPEdFi8SNnmdqlCQrhCxV-kJsdwtdcCnXGQ,1703 +pip/_internal/commands/help.py,sha256=gcc6QDkcgHMOuAn5UxaZwAStsRBrnGSn_yxjS57JIoM,1132 +pip/_internal/commands/index.py,sha256=CNXQer_PeZKSJooURcCFCBEKGfwyNoUWYP_MWczAcOM,4775 +pip/_internal/commands/inspect.py,sha256=2wSPt9yfr3r6g-s2S5L6PvRtaHNVyb4TuodMStJ39cw,3188 +pip/_internal/commands/install.py,sha256=VxDd-BD3a27ApeE2OK34rfBXS6Zo2wtemK9-HCwPqxM,28782 +pip/_internal/commands/list.py,sha256=-QbpPuGDiGN1SdThsk2ml8beBnepliefbGhMAN8tkzU,12547 +pip/_internal/commands/search.py,sha256=sbBZiARRc050QquOKcCvOr2K3XLsoYebLKZGRi__iUI,5697 +pip/_internal/commands/show.py,sha256=t5jia4zcYJRJZy4U_Von7zMl03hJmmcofj6oDNTnj7Y,6419 +pip/_internal/commands/uninstall.py,sha256=OIqO9tqadY8kM4HwhFf1Q62fUIp7v8KDrTRo8yWMz7Y,3886 +pip/_internal/commands/wheel.py,sha256=CSnX8Pmf1oPCnd7j7bn1_f58G9KHNiAblvVJ5zykN-A,6476 +pip/_internal/configuration.py,sha256=XkAiBS0hpzsM-LF0Qu5hvPWO_Bs67-oQKRYFBuMbESs,14006 +pip/_internal/distributions/__init__.py,sha256=Hq6kt6gXBgjNit5hTTWLAzeCNOKoB-N0pGYSqehrli8,858 +pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/distributions/__pycache__/base.cpython-312.pyc,, +pip/_internal/distributions/__pycache__/installed.cpython-312.pyc,, +pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc,, +pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc,, +pip/_internal/distributions/base.py,sha256=oRSEvnv2ZjBnargamnv2fcJa1n6gUDKaW0g6CWSEpWs,1743 +pip/_internal/distributions/installed.py,sha256=QinHFbWAQ8oE0pbD8MFZWkwlnfU1QYTccA1vnhrlYOU,842 +pip/_internal/distributions/sdist.py,sha256=4K3V0VNMllHbBzCJibjwd_tylUKpmIdu2AQyhplvCQo,6709 +pip/_internal/distributions/wheel.py,sha256=-ma3sOtUQj0AxXCEb6_Fhmjl3nh4k3A0HC2taAb2N-4,1277 +pip/_internal/exceptions.py,sha256=TmF1iNFEneSWaemwlg6a5bpPuq2cMHK7d1-SvjsQHb0,23634 +pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30 +pip/_internal/index/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/index/__pycache__/collector.cpython-312.pyc,, +pip/_internal/index/__pycache__/package_finder.cpython-312.pyc,, +pip/_internal/index/__pycache__/sources.cpython-312.pyc,, +pip/_internal/index/collector.py,sha256=sH0tL_cOoCk6pLLfCSGVjFM4rPEJtllF-VobvAvLSH4,16590 +pip/_internal/index/package_finder.py,sha256=S_nC8gzVIMY6ikWfKoSOzRtoesUqnfNhAPl_BwSOusA,37843 +pip/_internal/index/sources.py,sha256=dJegiR9f86kslaAHcv9-R5L_XBf5Rzm_FkyPteDuPxI,8688 +pip/_internal/locations/__init__.py,sha256=Dh8LJWG8LRlDK4JIj9sfRF96TREzE--N_AIlx7Tqoe4,15365 +pip/_internal/locations/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc,, +pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc,, +pip/_internal/locations/__pycache__/base.cpython-312.pyc,, +pip/_internal/locations/_distutils.py,sha256=H9ZHK_35rdDV1Qsmi4QeaBULjFT4Mbu6QuoVGkJ6QHI,6009 +pip/_internal/locations/_sysconfig.py,sha256=jyNVtUfMIf0mtyY-Xp1m9yQ8iwECozSVVFmjkN9a2yw,7680 +pip/_internal/locations/base.py,sha256=RQiPi1d4FVM2Bxk04dQhXZ2PqkeljEL2fZZ9SYqIQ78,2556 +pip/_internal/main.py,sha256=r-UnUe8HLo5XFJz8inTcOOTiu_sxNhgHb6VwlGUllOI,340 +pip/_internal/metadata/__init__.py,sha256=9pU3W3s-6HtjFuYhWcLTYVmSaziklPv7k2x8p7X1GmA,4339 +pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/metadata/__pycache__/_json.cpython-312.pyc,, +pip/_internal/metadata/__pycache__/base.cpython-312.pyc,, +pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc,, +pip/_internal/metadata/_json.py,sha256=Rz5M5ciSNvITwaTQR6NfN8TgKgM5WfTws4D6CFknovE,2627 +pip/_internal/metadata/base.py,sha256=l3Wgku4xlgr8s4p6fS-3qQ4QKOpPbWLRwi5d9omEFG4,25907 +pip/_internal/metadata/importlib/__init__.py,sha256=jUUidoxnHcfITHHaAWG1G2i5fdBYklv_uJcjo2x7VYE,135 +pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc,, +pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc,, +pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc,, +pip/_internal/metadata/importlib/_compat.py,sha256=GAe_prIfCE4iUylrnr_2dJRlkkBVRUbOidEoID7LPoE,1882 +pip/_internal/metadata/importlib/_dists.py,sha256=UPl1wUujFqiwiltRJ1tMF42WRINO1sSpNNlYQ2mX0mk,8297 +pip/_internal/metadata/importlib/_envs.py,sha256=XTaFIYERP2JF0QUZuPx2ETiugXbPEcZ8q8ZKeht6Lpc,7456 +pip/_internal/metadata/pkg_resources.py,sha256=opjw4IBSqHvie6sXJ_cbT42meygoPEUfNURJuWZY7sk,10035 +pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 +pip/_internal/models/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/models/__pycache__/candidate.cpython-312.pyc,, +pip/_internal/models/__pycache__/direct_url.cpython-312.pyc,, +pip/_internal/models/__pycache__/format_control.cpython-312.pyc,, +pip/_internal/models/__pycache__/index.cpython-312.pyc,, +pip/_internal/models/__pycache__/installation_report.cpython-312.pyc,, +pip/_internal/models/__pycache__/link.cpython-312.pyc,, +pip/_internal/models/__pycache__/scheme.cpython-312.pyc,, +pip/_internal/models/__pycache__/search_scope.cpython-312.pyc,, +pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc,, +pip/_internal/models/__pycache__/target_python.cpython-312.pyc,, +pip/_internal/models/__pycache__/wheel.cpython-312.pyc,, +pip/_internal/models/candidate.py,sha256=hEPu8VdGE5qVASv6vLz-R-Rgh5-7LMbai1jgthMCd8M,931 +pip/_internal/models/direct_url.py,sha256=FwouYBKcqckh7B-k2H3HVgRhhFTukFwqiS3kfvtFLSk,6889 +pip/_internal/models/format_control.py,sha256=wtsQqSK9HaUiNxQEuB-C62eVimw6G4_VQFxV9-_KDBE,2486 +pip/_internal/models/index.py,sha256=tYnL8oxGi4aSNWur0mG8DAP7rC6yuha_MwJO8xw0crI,1030 +pip/_internal/models/installation_report.py,sha256=zRVZoaz-2vsrezj_H3hLOhMZCK9c7TbzWgC-jOalD00,2818 +pip/_internal/models/link.py,sha256=XirOAGv1jgMu7vu87kuPbohGj7VHpwVrd2q3KUgVQNg,20777 +pip/_internal/models/scheme.py,sha256=3EFQp_ICu_shH1-TBqhl0QAusKCPDFOlgHFeN4XowWs,738 +pip/_internal/models/search_scope.py,sha256=ASVyyZxiJILw7bTIVVpJx8J293M3Hk5F33ilGn0e80c,4643 +pip/_internal/models/selection_prefs.py,sha256=KZdi66gsR-_RUXUr9uejssk3rmTHrQVJWeNA2sV-VSY,1907 +pip/_internal/models/target_python.py,sha256=34EkorrMuRvRp-bjqHKJ-bOO71m9xdjN2b8WWFEC2HU,4272 +pip/_internal/models/wheel.py,sha256=YqazoIZyma_Q1ejFa1C7NHKQRRWlvWkdK96VRKmDBeI,3600 +pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50 +pip/_internal/network/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/network/__pycache__/auth.cpython-312.pyc,, +pip/_internal/network/__pycache__/cache.cpython-312.pyc,, +pip/_internal/network/__pycache__/download.cpython-312.pyc,, +pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc,, +pip/_internal/network/__pycache__/session.cpython-312.pyc,, +pip/_internal/network/__pycache__/utils.cpython-312.pyc,, +pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc,, +pip/_internal/network/auth.py,sha256=TC-OcW2KU4W6R1hU4qPgQXvVH54adACpZz6sWq-R9NA,20541 +pip/_internal/network/cache.py,sha256=48A971qCzKNFvkb57uGEk7-0xaqPS0HWj2711QNTxkU,3935 +pip/_internal/network/download.py,sha256=i0Tn55CD5D7XYEFY3TxiYaCf0OaaTQ6SScNgCsSeV14,6086 +pip/_internal/network/lazy_wheel.py,sha256=2PXVduYZPCPZkkQFe1J1GbfHJWeCU--FXonGyIfw9eU,7638 +pip/_internal/network/session.py,sha256=9tqEDD8JiVaFdplOEXJxNo9cjRfBZ6RIa0yQQ_qBNiM,18698 +pip/_internal/network/utils.py,sha256=6A5SrUJEEUHxbGtbscwU2NpCyz-3ztiDlGWHpRRhsJ8,4073 +pip/_internal/network/xmlrpc.py,sha256=sAxzOacJ-N1NXGPvap9jC3zuYWSnnv3GXtgR2-E2APA,1838 +pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/operations/__pycache__/check.cpython-312.pyc,, +pip/_internal/operations/__pycache__/freeze.cpython-312.pyc,, +pip/_internal/operations/__pycache__/prepare.cpython-312.pyc,, +pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc,, +pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc,, +pip/_internal/operations/build/build_tracker.py,sha256=z-H5DOknZdBa3dh2Vq6VBMY5qLYIKmlj2p6CGZK5Lc8,4832 +pip/_internal/operations/build/metadata.py,sha256=9S0CUD8U3QqZeXp-Zyt8HxwU90lE4QrnYDgrqZDzBnc,1422 +pip/_internal/operations/build/metadata_editable.py,sha256=VLL7LvntKE8qxdhUdEJhcotFzUsOSI8NNS043xULKew,1474 +pip/_internal/operations/build/metadata_legacy.py,sha256=o-eU21As175hDC7dluM1fJJ_FqokTIShyWpjKaIpHZw,2198 +pip/_internal/operations/build/wheel.py,sha256=sT12FBLAxDC6wyrDorh8kvcZ1jG5qInCRWzzP-UkJiQ,1075 +pip/_internal/operations/build/wheel_editable.py,sha256=yOtoH6zpAkoKYEUtr8FhzrYnkNHQaQBjWQ2HYae1MQg,1417 +pip/_internal/operations/build/wheel_legacy.py,sha256=C9j6rukgQI1n_JeQLoZGuDdfUwzCXShyIdPTp6edbMQ,3064 +pip/_internal/operations/check.py,sha256=fsqA88iGaqftCr2tlP3sSU202CSkoODRtW0O-JU9M4Y,6806 +pip/_internal/operations/freeze.py,sha256=uqoeTAf6HOYVMR2UgAT8N85UZoGEVEoQdan_Ao6SOfk,9816 +pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51 +pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc,, +pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc,, +pip/_internal/operations/install/editable_legacy.py,sha256=YeR0KadWXw_ZheC1NtAG1qVIEkOgRGHc23x-YtGW7NU,1282 +pip/_internal/operations/install/wheel.py,sha256=9hGb1c4bRnPIb2FG7CtUSPfPxqprmHQBtwIAlWPNTtE,27311 +pip/_internal/operations/prepare.py,sha256=57Oq87HfunX3Rbqp47FdaJr9cHbAKUm_3gv7WhBAqbE,28128 +pip/_internal/pyproject.py,sha256=4Xszp11xgr126yzG6BbJA0oaQ9WXuhb0jyUb-y_6lPQ,7152 +pip/_internal/req/__init__.py,sha256=TELFgZOof3lhMmaICVWL9U7PlhXo9OufokbMAJ6J2GI,2738 +pip/_internal/req/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/req/__pycache__/constructors.cpython-312.pyc,, +pip/_internal/req/__pycache__/req_file.cpython-312.pyc,, +pip/_internal/req/__pycache__/req_install.cpython-312.pyc,, +pip/_internal/req/__pycache__/req_set.cpython-312.pyc,, +pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc,, +pip/_internal/req/constructors.py,sha256=8hlY56imEthLORRwmloyKz3YOyXymIaKsNB6P9ewvNI,19018 +pip/_internal/req/req_file.py,sha256=M8ttOZL-PwAj7scPElhW3ZD2hiD9mm_6FJAGIbwAzEI,17790 +pip/_internal/req/req_install.py,sha256=wtOPxkyRSM8comTks8oL1Gp2oyGqbH7JwIDRci2QiPk,35460 +pip/_internal/req/req_set.py,sha256=iMYDUToSgkxFyrP_OrTtPSgw4dwjRyGRDpGooTqeA4Y,4704 +pip/_internal/req/req_uninstall.py,sha256=nmvTQaRCC0iu-5Tw0djlXJhSj6WmqHRvT3qkkEdC35E,24551 +pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/resolution/__pycache__/base.cpython-312.pyc,, +pip/_internal/resolution/base.py,sha256=qlmh325SBVfvG6Me9gc5Nsh5sdwHBwzHBq6aEXtKsLA,583 +pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc,, +pip/_internal/resolution/legacy/resolver.py,sha256=Xk24jQ62GvLr4Mc7IjN_qiO88qp0BImzVmPIFz9QLOE,24025 +pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc,, +pip/_internal/resolution/resolvelib/base.py,sha256=jg5COmHLhmBIKOR-4spdJD3jyULYa1BdsqiBu2YJnJ4,5173 +pip/_internal/resolution/resolvelib/candidates.py,sha256=19Ki91Po-MSxBknGIfOGkaWkFdOznN0W_nKv7jL28L0,21052 +pip/_internal/resolution/resolvelib/factory.py,sha256=vqqk-hjchdhShwWVdeW2_A-5ZblLhE_nC_v3Mhz4Svc,32292 +pip/_internal/resolution/resolvelib/found_candidates.py,sha256=hvL3Hoa9VaYo-qEOZkBi2Iqw251UDxPz-uMHVaWmLpE,5705 +pip/_internal/resolution/resolvelib/provider.py,sha256=4t23ivjruqM6hKBX1KpGiTt-M4HGhRcZnGLV0c01K7U,9824 +pip/_internal/resolution/resolvelib/reporter.py,sha256=YFm9hQvz4DFCbjZeFTQ56hTz3Ac-mDBnHkeNRVvMHLY,3100 +pip/_internal/resolution/resolvelib/requirements.py,sha256=-kJONP0WjDfdTvBAs2vUXPgAnOyNIBEAXY4b72ogtPE,5696 +pip/_internal/resolution/resolvelib/resolver.py,sha256=nLJOsVMEVi2gQUVJoUFKMZAeu2f7GRMjGMvNSWyz0Bc,12592 +pip/_internal/self_outdated_check.py,sha256=saxQLB8UzIFtMScquytG10TOTsYVFJQ_mkW1NY-46wE,8378 +pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/utils/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc,, +pip/_internal/utils/__pycache__/_log.cpython-312.pyc,, +pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc,, +pip/_internal/utils/__pycache__/compat.cpython-312.pyc,, +pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc,, +pip/_internal/utils/__pycache__/datetime.cpython-312.pyc,, +pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc,, +pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc,, +pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc,, +pip/_internal/utils/__pycache__/encoding.cpython-312.pyc,, +pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc,, +pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc,, +pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc,, +pip/_internal/utils/__pycache__/glibc.cpython-312.pyc,, +pip/_internal/utils/__pycache__/hashes.cpython-312.pyc,, +pip/_internal/utils/__pycache__/logging.cpython-312.pyc,, +pip/_internal/utils/__pycache__/misc.cpython-312.pyc,, +pip/_internal/utils/__pycache__/models.cpython-312.pyc,, +pip/_internal/utils/__pycache__/packaging.cpython-312.pyc,, +pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc,, +pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc,, +pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc,, +pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc,, +pip/_internal/utils/__pycache__/urls.cpython-312.pyc,, +pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc,, +pip/_internal/utils/__pycache__/wheel.cpython-312.pyc,, +pip/_internal/utils/_jaraco_text.py,sha256=yvDGelTVugRayPaOF2k4ab0Ky4d3uOkAfuOQjASjImY,3351 +pip/_internal/utils/_log.py,sha256=-jHLOE_THaZz5BFcCnoSL9EYAtJ0nXem49s9of4jvKw,1015 +pip/_internal/utils/appdirs.py,sha256=swgcTKOm3daLeXTW6v5BUS2Ti2RvEnGRQYH_yDXklAo,1665 +pip/_internal/utils/compat.py,sha256=ACyBfLgj3_XG-iA5omEDrXqDM0cQKzi8h8HRBInzG6Q,1884 +pip/_internal/utils/compatibility_tags.py,sha256=ydin8QG8BHqYRsPY4OL6cmb44CbqXl1T0xxS97VhHkk,5377 +pip/_internal/utils/datetime.py,sha256=m21Y3wAtQc-ji6Veb6k_M5g6A0ZyFI4egchTdnwh-pQ,242 +pip/_internal/utils/deprecation.py,sha256=NKo8VqLioJ4nnXXGmW4KdasxF90EFHkZaHeX1fT08C8,3627 +pip/_internal/utils/direct_url_helpers.py,sha256=6F1tc2rcKaCZmgfVwsE6ObIe_Pux23mUVYA-2D9wCFc,3206 +pip/_internal/utils/egg_link.py,sha256=0FePZoUYKv4RGQ2t6x7w5Z427wbA_Uo3WZnAkrgsuqo,2463 +pip/_internal/utils/encoding.py,sha256=qqsXDtiwMIjXMEiIVSaOjwH5YmirCaK-dIzb6-XJsL0,1169 +pip/_internal/utils/entrypoints.py,sha256=YlhLTRl2oHBAuqhc-zmL7USS67TPWVHImjeAQHreZTQ,3064 +pip/_internal/utils/filesystem.py,sha256=RhMIXUaNVMGjc3rhsDahWQ4MavvEQDdqXqgq-F6fpw8,5122 +pip/_internal/utils/filetypes.py,sha256=i8XAQ0eFCog26Fw9yV0Yb1ygAqKYB1w9Cz9n0fj8gZU,716 +pip/_internal/utils/glibc.py,sha256=Mesxxgg3BLxheLZx-dSf30b6gKpOgdVXw6W--uHSszQ,3113 +pip/_internal/utils/hashes.py,sha256=MjOigC75z6qoRMkgHiHqot7eqxfwDZSrEflJMPm-bHE,5118 +pip/_internal/utils/logging.py,sha256=fdtuZJ-AKkqwDTANDvGcBEpssL8el7T1jnwk1CnZl3Y,11603 +pip/_internal/utils/misc.py,sha256=fNXwaeeikvnUt4CPMFIL4-IQbZDxxjj4jDpzCi4ZsOw,23623 +pip/_internal/utils/models.py,sha256=5GoYU586SrxURMvDn_jBMJInitviJg4O5-iOU-6I0WY,1193 +pip/_internal/utils/packaging.py,sha256=5Wm6_x7lKrlqVjPI5MBN_RurcRHwVYoQ7Ksrs84de7s,2108 +pip/_internal/utils/setuptools_build.py,sha256=ouXpud-jeS8xPyTPsXJ-m34NPvK5os45otAzdSV_IJE,4435 +pip/_internal/utils/subprocess.py,sha256=zzdimb75jVLE1GU4WlTZ055gczhD7n1y1xTcNc7vNZQ,9207 +pip/_internal/utils/temp_dir.py,sha256=DUAw22uFruQdK43i2L2K53C-CDjRCPeAsBKJpu-rHQ4,9312 +pip/_internal/utils/unpacking.py,sha256=SBb2iV1crb89MDRTEKY86R4A_UOWApTQn9VQVcMDOlE,8821 +pip/_internal/utils/urls.py,sha256=AhaesUGl-9it6uvG6fsFPOr9ynFpGaTMk4t5XTX7Z_Q,1759 +pip/_internal/utils/virtualenv.py,sha256=S6f7csYorRpiD6cvn3jISZYc3I8PJC43H5iMFpRAEDU,3456 +pip/_internal/utils/wheel.py,sha256=i4BwUNHattzN0ixy3HBAF04tZPRh2CcxaT6t86viwkE,4499 +pip/_internal/vcs/__init__.py,sha256=UAqvzpbi0VbZo3Ub6skEeZAw-ooIZR-zX_WpCbxyCoU,596 +pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc,, +pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc,, +pip/_internal/vcs/__pycache__/git.cpython-312.pyc,, +pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc,, +pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc,, +pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc,, +pip/_internal/vcs/bazaar.py,sha256=j0oin0fpGRHcCFCxEcpPCQoFEvA-DMLULKdGP8Nv76o,3519 +pip/_internal/vcs/git.py,sha256=CeKBGJnl6uskvvjkAUXrJVxbHJrpS_B_pyfFdjL3CRc,18121 +pip/_internal/vcs/mercurial.py,sha256=oULOhzJ2Uie-06d1omkL-_Gc6meGaUkyogvqG9ZCyPs,5249 +pip/_internal/vcs/subversion.py,sha256=vhZs8L-TNggXqM1bbhl-FpbxE3TrIB6Tgnx8fh3S2HE,11729 +pip/_internal/vcs/versioncontrol.py,sha256=3eIjtOMYvOY5qP6BMYIYDZ375CSuec6kSEB0bOo1cSs,22787 +pip/_internal/wheel_builder.py,sha256=qTTzQV8F6b1jNsFCda1TRQC8J7gK-m7iuRNgKo7Dj68,11801 +pip/_vendor/__init__.py,sha256=U51NPwXdA-wXOiANIQncYjcMp6txgeOL5nHxksJeyas,4993 +pip/_vendor/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/__pycache__/six.cpython-312.pyc,, +pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc,, +pip/_vendor/cachecontrol/__init__.py,sha256=ctHagMhQXuvQDdm4TirZrwDOT5H8oBNAJqzdKI6sovk,676 +pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc,, +pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc,, +pip/_vendor/cachecontrol/_cmd.py,sha256=iist2EpzJvDVIhMAxXq8iFnTBsiZAd6iplxfmNboNyk,1737 +pip/_vendor/cachecontrol/adapter.py,sha256=_CcWvUP9048qAZjsNqViaHbdcLs9mmFNixVfpO7oebE,6392 +pip/_vendor/cachecontrol/cache.py,sha256=OTQj72tUf8C1uEgczdl3Gc8vkldSzsTITKtDGKMx4z8,1952 +pip/_vendor/cachecontrol/caches/__init__.py,sha256=dtrrroK5BnADR1GWjCZ19aZ0tFsMfvFBtLQQU1sp_ag,303 +pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc,, +pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc,, +pip/_vendor/cachecontrol/caches/file_cache.py,sha256=3z8AWKD-vfKeiJqIzLmJyIYtR2yd6Tsh3u1TyLRQoIQ,5352 +pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=9rmqwtYu_ljVkW6_oLqbC7EaX_a8YT_yLuna-eS0dgo,1386 +pip/_vendor/cachecontrol/controller.py,sha256=keCFA3ZaNVaWTwHd6F1zqWhb4vyvNx_UvZuo5iIYMfo,18384 +pip/_vendor/cachecontrol/filewrapper.py,sha256=STttGmIPBvZzt2b51dUOwoWX5crcMCpKZOisM3f5BNc,4292 +pip/_vendor/cachecontrol/heuristics.py,sha256=fdFbk9W8IeLrjteIz_fK4mj2HD_Y7COXF2Uc8TgjT1c,4828 +pip/_vendor/cachecontrol/serialize.py,sha256=0dHeMaDwysVAAnGVlhMOP4tDliohgNK0Jxk_zsOiWxw,7173 +pip/_vendor/cachecontrol/wrapper.py,sha256=hsGc7g8QGQTT-4f8tgz3AM5qwScg6FO0BSdLSRdEvpU,1417 +pip/_vendor/certifi/__init__.py,sha256=L_j-d0kYuA_MzA2_2hraF1ovf6KT6DTquRdV3paQwOk,94 +pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255 +pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc,, +pip/_vendor/certifi/__pycache__/core.cpython-312.pyc,, +pip/_vendor/certifi/cacert.pem,sha256=eU0Dn_3yd8BH4m8sfVj4Glhl2KDrcCSg-sEWT-pNJ88,281617 +pip/_vendor/certifi/core.py,sha256=DNTl8b_B6C4vO3Vc9_q2uvwHpNnBQoy5onDC4McImxc,4531 +pip/_vendor/chardet/__init__.py,sha256=57R-HSxj0PWmILMN0GFmUNqEMfrEVSamXyjD-W6_fbs,4797 +pip/_vendor/chardet/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/big5freq.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/big5prober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/chardistribution.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/charsetprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/cp949prober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/enums.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/escprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/escsm.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/eucjpprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/euckrfreq.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/euckrprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/euctwfreq.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/euctwprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/gb2312freq.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/gb2312prober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/hebrewprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/jisfreq.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/johabfreq.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/johabprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/jpcntx.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langthaimodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/latin1prober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/macromanprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/mbcssm.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/resultdict.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/sjisprober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/universaldetector.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/utf1632prober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/utf8prober.cpython-312.pyc,, +pip/_vendor/chardet/__pycache__/version.cpython-312.pyc,, +pip/_vendor/chardet/big5freq.py,sha256=ltcfP-3PjlNHCoo5e4a7C4z-2DhBTXRfY6jbMbB7P30,31274 +pip/_vendor/chardet/big5prober.py,sha256=lPMfwCX6v2AaPgvFh_cSWZcgLDbWiFCHLZ_p9RQ9uxE,1763 +pip/_vendor/chardet/chardistribution.py,sha256=13B8XUG4oXDuLdXvfbIWwLFeR-ZU21AqTS1zcdON8bU,10032 +pip/_vendor/chardet/charsetgroupprober.py,sha256=UKK3SaIZB2PCdKSIS0gnvMtLR9JJX62M-fZJu3OlWyg,3915 +pip/_vendor/chardet/charsetprober.py,sha256=L3t8_wIOov8em-vZWOcbkdsrwe43N6_gqNh5pH7WPd4,5420 +pip/_vendor/chardet/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/chardet/cli/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-312.pyc,, +pip/_vendor/chardet/cli/chardetect.py,sha256=zibMVg5RpKb-ME9_7EYG4ZM2Sf07NHcQzZ12U-rYJho,3242 +pip/_vendor/chardet/codingstatemachine.py,sha256=K7k69sw3jY5DmTXoSJQVsUtFIQKYPQVOSJJhBuGv_yE,3732 +pip/_vendor/chardet/codingstatemachinedict.py,sha256=0GY3Hi2qIZvDrOOJ3AtqppM1RsYxr_66ER4EHjuMiMc,542 +pip/_vendor/chardet/cp949prober.py,sha256=0jKRV7fECuWI16rNnks0ZECKA1iZYCIEaP8A1ZvjUSI,1860 +pip/_vendor/chardet/enums.py,sha256=TzECiZoCKNMqgwU76cPCeKWFBqaWvAdLMev5_bCkhY8,1683 +pip/_vendor/chardet/escprober.py,sha256=Kho48X65xE0scFylIdeJjM2bcbvRvv0h0WUbMWrJD3A,4006 +pip/_vendor/chardet/escsm.py,sha256=AqyXpA2FQFD7k-buBty_7itGEYkhmVa8X09NLRul3QM,12176 +pip/_vendor/chardet/eucjpprober.py,sha256=5KYaM9fsxkRYzw1b5k0fL-j_-ezIw-ij9r97a9MHxLY,3934 +pip/_vendor/chardet/euckrfreq.py,sha256=3mHuRvXfsq_QcQysDQFb8qSudvTiol71C6Ic2w57tKM,13566 +pip/_vendor/chardet/euckrprober.py,sha256=hiFT6wM174GIwRvqDsIcuOc-dDsq2uPKMKbyV8-1Xnc,1753 +pip/_vendor/chardet/euctwfreq.py,sha256=2alILE1Lh5eqiFJZjzRkMQXolNJRHY5oBQd-vmZYFFM,36913 +pip/_vendor/chardet/euctwprober.py,sha256=NxbpNdBtU0VFI0bKfGfDkpP7S2_8_6FlO87dVH0ogws,1753 +pip/_vendor/chardet/gb2312freq.py,sha256=49OrdXzD-HXqwavkqjo8Z7gvs58hONNzDhAyMENNkvY,20735 +pip/_vendor/chardet/gb2312prober.py,sha256=KPEBueaSLSvBpFeINMu0D6TgHcR90e5PaQawifzF4o0,1759 +pip/_vendor/chardet/hebrewprober.py,sha256=96T_Lj_OmW-fK7JrSHojYjyG3fsGgbzkoTNleZ3kfYE,14537 +pip/_vendor/chardet/jisfreq.py,sha256=mm8tfrwqhpOd3wzZKS4NJqkYBQVcDfTM2JiQ5aW932E,25796 +pip/_vendor/chardet/johabfreq.py,sha256=dBpOYG34GRX6SL8k_LbS9rxZPMjLjoMlgZ03Pz5Hmqc,42498 +pip/_vendor/chardet/johabprober.py,sha256=O1Qw9nVzRnun7vZp4UZM7wvJSv9W941mEU9uDMnY3DU,1752 +pip/_vendor/chardet/jpcntx.py,sha256=uhHrYWkLxE_rF5OkHKInm0HUsrjgKHHVQvtt3UcvotA,27055 +pip/_vendor/chardet/langbulgarianmodel.py,sha256=vmbvYFP8SZkSxoBvLkFqKiH1sjma5ihk3PTpdy71Rr4,104562 +pip/_vendor/chardet/langgreekmodel.py,sha256=JfB7bupjjJH2w3X_mYnQr9cJA_7EuITC2cRW13fUjeI,98484 +pip/_vendor/chardet/langhebrewmodel.py,sha256=3HXHaLQPNAGcXnJjkIJfozNZLTvTJmf4W5Awi6zRRKc,98196 +pip/_vendor/chardet/langhungarianmodel.py,sha256=WxbeQIxkv8YtApiNqxQcvj-tMycsoI4Xy-fwkDHpP_Y,101363 +pip/_vendor/chardet/langrussianmodel.py,sha256=s395bTZ87ESTrZCOdgXbEjZ9P1iGPwCl_8xSsac_DLY,128035 +pip/_vendor/chardet/langthaimodel.py,sha256=7bJlQitRpTnVGABmbSznHnJwOHDy3InkTvtFUx13WQI,102774 +pip/_vendor/chardet/langturkishmodel.py,sha256=XY0eGdTIy4eQ9Xg1LVPZacb-UBhHBR-cq0IpPVHowKc,95372 +pip/_vendor/chardet/latin1prober.py,sha256=p15EEmFbmQUwbKLC7lOJVGHEZwcG45ubEZYTGu01J5g,5380 +pip/_vendor/chardet/macromanprober.py,sha256=9anfzmY6TBfUPDyBDOdY07kqmTHpZ1tK0jL-p1JWcOY,6077 +pip/_vendor/chardet/mbcharsetprober.py,sha256=Wr04WNI4F3X_VxEverNG-H25g7u-MDDKlNt-JGj-_uU,3715 +pip/_vendor/chardet/mbcsgroupprober.py,sha256=iRpaNBjV0DNwYPu_z6TiHgRpwYahiM7ztI_4kZ4Uz9A,2131 +pip/_vendor/chardet/mbcssm.py,sha256=hUtPvDYgWDaA2dWdgLsshbwRfm3Q5YRlRogdmeRUNQw,30391 +pip/_vendor/chardet/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/chardet/metadata/__pycache__/languages.cpython-312.pyc,, +pip/_vendor/chardet/metadata/languages.py,sha256=FhvBIdZFxRQ-dTwkb_0madRKgVBCaUMQz9I5xqjE5iQ,13560 +pip/_vendor/chardet/resultdict.py,sha256=ez4FRvN5KaSosJeJ2WzUyKdDdg35HDy_SSLPXKCdt5M,402 +pip/_vendor/chardet/sbcharsetprober.py,sha256=-nd3F90i7GpXLjehLVHqVBE0KlWzGvQUPETLBNn4o6U,6400 +pip/_vendor/chardet/sbcsgroupprober.py,sha256=gcgI0fOfgw_3YTClpbra_MNxwyEyJ3eUXraoLHYb59E,4137 +pip/_vendor/chardet/sjisprober.py,sha256=aqQufMzRw46ZpFlzmYaYeT2-nzmKb-hmcrApppJ862k,4007 +pip/_vendor/chardet/universaldetector.py,sha256=xYBrg4x0dd9WnT8qclfADVD9ondrUNkqPmvte1pa520,14848 +pip/_vendor/chardet/utf1632prober.py,sha256=pw1epGdMj1hDGiCu1AHqqzOEfjX8MVdiW7O1BlT8-eQ,8505 +pip/_vendor/chardet/utf8prober.py,sha256=8m08Ub5490H4jQ6LYXvFysGtgKoKsHUd2zH_i8_TnVw,2812 +pip/_vendor/chardet/version.py,sha256=lGtJcxGM44Qz4Cbk4rbbmrKxnNr1-97U25TameLehZw,244 +pip/_vendor/colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 +pip/_vendor/colorama/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/colorama/__pycache__/ansi.cpython-312.pyc,, +pip/_vendor/colorama/__pycache__/ansitowin32.cpython-312.pyc,, +pip/_vendor/colorama/__pycache__/initialise.cpython-312.pyc,, +pip/_vendor/colorama/__pycache__/win32.cpython-312.pyc,, +pip/_vendor/colorama/__pycache__/winterm.cpython-312.pyc,, +pip/_vendor/colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 +pip/_vendor/colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +pip/_vendor/colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +pip/_vendor/colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +pip/_vendor/colorama/tests/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-312.pyc,, +pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-312.pyc,, +pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-312.pyc,, +pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-312.pyc,, +pip/_vendor/colorama/tests/__pycache__/utils.cpython-312.pyc,, +pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-312.pyc,, +pip/_vendor/colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +pip/_vendor/colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +pip/_vendor/colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +pip/_vendor/colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +pip/_vendor/colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +pip/_vendor/colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +pip/_vendor/colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +pip/_vendor/colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 +pip/_vendor/distlib/__init__.py,sha256=hJKF7FHoqbmGckncDuEINWo_OYkDNiHODtYXSMcvjcc,625 +pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/database.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/index.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/util.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/version.cpython-312.pyc,, +pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc,, +pip/_vendor/distlib/compat.py,sha256=Un-uIBvy02w-D267OG4VEhuddqWgKj9nNkxVltAb75w,41487 +pip/_vendor/distlib/database.py,sha256=0V9Qvs0Vrxa2F_-hLWitIyVyRifJ0pCxyOI-kEOBwsA,51965 +pip/_vendor/distlib/index.py,sha256=lTbw268rRhj8dw1sib3VZ_0EhSGgoJO3FKJzSFMOaeA,20797 +pip/_vendor/distlib/locators.py,sha256=o1r_M86_bRLafSpetmyfX8KRtFu-_Q58abvQrnOSnbA,51767 +pip/_vendor/distlib/manifest.py,sha256=3qfmAmVwxRqU1o23AlfXrQGZzh6g_GGzTAP_Hb9C5zQ,14168 +pip/_vendor/distlib/markers.py,sha256=n3DfOh1yvZ_8EW7atMyoYeZFXjYla0Nz0itQlojCd0A,5268 +pip/_vendor/distlib/metadata.py,sha256=pB9WZ9mBfmQxc9OVIldLS5CjOoQRvKAvUwwQyKwKQtQ,39693 +pip/_vendor/distlib/resources.py,sha256=LwbPksc0A1JMbi6XnuPdMBUn83X7BPuFNWqPGEKI698,10820 +pip/_vendor/distlib/scripts.py,sha256=nQFXN6G7nOWNDUyxirUep-3WOlJhB7McvCs9zOnkGTI,18315 +pip/_vendor/distlib/util.py,sha256=XSznxEi_i3T20UJuaVc0qXHz5ksGUCW1khYlBprN_QE,67530 +pip/_vendor/distlib/version.py,sha256=9pXkduchve_aN7JG6iL9VTYV_kqNSGoc2Dwl8JuySnQ,23747 +pip/_vendor/distlib/wheel.py,sha256=FVQCve8u-L0QYk5-YTZc7s4WmNQdvjRWTK08KXzZVX4,43958 +pip/_vendor/distro/__init__.py,sha256=2fHjF-SfgPvjyNZ1iHh_wjqWdR_Yo5ODHwZC0jLBPhc,981 +pip/_vendor/distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96Vs,64 +pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc,, +pip/_vendor/distro/__pycache__/distro.cpython-312.pyc,, +pip/_vendor/distro/distro.py,sha256=UZO1LjIhtFCMdlbiz39gj3raV-Amf3SBwzGzfApiMHw,49330 +pip/_vendor/idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849 +pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/codec.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/compat.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/core.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc,, +pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc,, +pip/_vendor/idna/codec.py,sha256=6ly5odKfqrytKT9_7UrlGklHnf1DSK2r9C6cSM4sa28,3374 +pip/_vendor/idna/compat.py,sha256=0_sOEUMT4CVw9doD3vyRhX80X19PwqFoUBs7gWsFME4,321 +pip/_vendor/idna/core.py,sha256=kkCFNJOrE6I3WwBTXcGNuc24V_QZQ8AULE6EYe1iHlU,12813 +pip/_vendor/idna/idnadata.py,sha256=9NIhTqC2piUpeIMOGZ9Bu_7eAFQ-Ic8TkP_hOzUpnDc,78344 +pip/_vendor/idna/intranges.py,sha256=YBr4fRYuWH7kTKS2tXlFjM24ZF1Pdvcir-aywniInqg,1881 +pip/_vendor/idna/package_data.py,sha256=C_jHJzmX8PI4xq0jpzmcTMxpb5lDsq4o5VyxQzlVrZE,21 +pip/_vendor/idna/uts46data.py,sha256=zvjZU24s58_uAS850Mcd0NnD0X7_gCMAMjzWNIeUJdc,206539 +pip/_vendor/msgpack/__init__.py,sha256=hyGhlnmcJkxryJBKC3X5FnEph375kQoL_mG8LZUuXgY,1132 +pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc,, +pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc,, +pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc,, +pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081 +pip/_vendor/msgpack/ext.py,sha256=C5MK8JhVYGYFWPvxsORsqZAnvOXefYQ57m1Ym0luW5M,6079 +pip/_vendor/msgpack/fallback.py,sha256=tvNBHyxxFbuVlC8GZShETClJxjLiDMOja4XwwyvNm2g,34544 +pip/_vendor/packaging/__about__.py,sha256=ugASIO2w1oUyH8_COqQ2X_s0rDhjbhQC3yJocD03h2c,661 +pip/_vendor/packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497 +pip/_vendor/packaging/__pycache__/__about__.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc,, +pip/_vendor/packaging/__pycache__/version.cpython-312.pyc,, +pip/_vendor/packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488 +pip/_vendor/packaging/_musllinux.py,sha256=_KGgY_qc7vhMGpoqss25n2hiLCNKRtvz9mCrS7gkqyc,4378 +pip/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 +pip/_vendor/packaging/markers.py,sha256=AJBOcY8Oq0kYc570KuuPTkvuqjAlhufaE2c9sCUbm64,8487 +pip/_vendor/packaging/requirements.py,sha256=NtDlPBtojpn1IUC85iMjPNsUmufjpSlwnNA-Xb4m5NA,4676 +pip/_vendor/packaging/specifiers.py,sha256=LRQ0kFsHrl5qfcFNEEJrIFYsnIHQUJXY9fIsakTrrqE,30110 +pip/_vendor/packaging/tags.py,sha256=lmsnGNiJ8C4D_Pf9PbM0qgbZvD9kmB9lpZBQUZa3R_Y,15699 +pip/_vendor/packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200 +pip/_vendor/packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665 +pip/_vendor/pkg_resources/__init__.py,sha256=hTAeJCNYb7dJseIDVsYK3mPQep_gphj4tQh-bspX8bg,109364 +pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/platformdirs/__init__.py,sha256=SkhEYVyC_HUHC6KX7n4M_6coyRMtEB38QMyOYIAX6Yk,20155 +pip/_vendor/platformdirs/__main__.py,sha256=fVvSiTzr2-RM6IsjWjj4fkaOtDOgDhUWv6sA99do4CQ,1476 +pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc,, +pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc,, +pip/_vendor/platformdirs/android.py,sha256=y_EEMKwYl2-bzYBDovksSn8m76on0Lda8eyJksVQE9U,7211 +pip/_vendor/platformdirs/api.py,sha256=jWtX06jAJytYrkJDOqEls97mCkyHRSZkoqUlbMK5Qew,7132 +pip/_vendor/platformdirs/macos.py,sha256=LueVOoVgGWDBwQb8OFwXkVKfVn33CM1Lkwf1-A86tRQ,3678 +pip/_vendor/platformdirs/unix.py,sha256=22JhR8ZY0aLxSVCFnKrc6f1iz6Gv42K24Daj7aTjfSg,8809 +pip/_vendor/platformdirs/version.py,sha256=mavZTQIJIXfdewEaSTn7EWrNfPZWeRofb-74xqW5f2M,160 +pip/_vendor/platformdirs/windows.py,sha256=4TtbPGoWG2PRgI11uquDa7eRk8TcxvnUNuuMGZItnXc,9573 +pip/_vendor/pygments/__init__.py,sha256=6AuDljQtvf89DTNUyWM7k3oUlP_lq70NU-INKKteOBY,2983 +pip/_vendor/pygments/__main__.py,sha256=es8EKMvXj5yToIfQ-pf3Dv5TnIeeM6sME0LW-n4ecHo,353 +pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/cmdline.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/console.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/style.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/token.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc,, +pip/_vendor/pygments/__pycache__/util.cpython-312.pyc,, +pip/_vendor/pygments/cmdline.py,sha256=byxYJp9gnjVeyhRlZ3UTMgo_LhkXh1afvN8wJBtAcc8,23685 +pip/_vendor/pygments/console.py,sha256=2wZ5W-U6TudJD1_NLUwjclMpbomFM91lNv11_60sfGY,1697 +pip/_vendor/pygments/filter.py,sha256=j5aLM9a9wSx6eH1oy473oSkJ02hGWNptBlVo4s1g_30,1938 +pip/_vendor/pygments/filters/__init__.py,sha256=h_koYkUFo-FFUxjs564JHUAz7O3yJpVwI6fKN3MYzG0,40386 +pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pygments/formatter.py,sha256=J9OL9hXLJKZk7moUgKwpjW9HNf4WlJFg_o_-Z_S_tTY,4178 +pip/_vendor/pygments/formatters/__init__.py,sha256=_xgAcdFKr0QNYwh_i98AU9hvfP3X2wAkhElFcRRF3Uo,5424 +pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/groff.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc,, +pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc,, +pip/_vendor/pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176 +pip/_vendor/pygments/formatters/bbcode.py,sha256=r1b7wzWTJouADDLh-Z11iRi4iQxD0JKJ1qHl6mOYxsA,3314 +pip/_vendor/pygments/formatters/groff.py,sha256=xy8Zf3tXOo6MWrXh7yPGWx3lVEkg_DhY4CxmsDb0IVo,5094 +pip/_vendor/pygments/formatters/html.py,sha256=PIzAyilNqaTzSSP2slDG2VDLE3qNioWy2rgtSSoviuI,35610 +pip/_vendor/pygments/formatters/img.py,sha256=XKXmg2_XONrR4mtq2jfEU8XCsoln3VSGTw-UYiEokys,21938 +pip/_vendor/pygments/formatters/irc.py,sha256=Ep-m8jd3voFO6Fv57cUGFmz6JVA67IEgyiBOwv0N4a0,4981 +pip/_vendor/pygments/formatters/latex.py,sha256=FGzJ-YqSTE8z_voWPdzvLY5Tq8jE_ygjGjM6dXZJ8-k,19351 +pip/_vendor/pygments/formatters/other.py,sha256=gPxkk5BdAzWTCgbEHg1lpLi-1F6ZPh5A_aotgLXHnzg,5073 +pip/_vendor/pygments/formatters/pangomarkup.py,sha256=6LKnQc8yh49f802bF0sPvbzck4QivMYqqoXAPaYP8uU,2212 +pip/_vendor/pygments/formatters/rtf.py,sha256=aA0v_psW6KZI3N18TKDifxeL6mcF8EDXcPXDWI4vhVQ,5014 +pip/_vendor/pygments/formatters/svg.py,sha256=dQONWypbzfvzGCDtdp3M_NJawScJvM2DiHbx1k-ww7g,7335 +pip/_vendor/pygments/formatters/terminal.py,sha256=FG-rpjRpFmNpiGB4NzIucvxq6sQIXB3HOTo2meTKtrU,4674 +pip/_vendor/pygments/formatters/terminal256.py,sha256=13SJ3D5pFdqZ9zROE6HbWnBDwHvOGE8GlsmqGhprRp4,11753 +pip/_vendor/pygments/lexer.py,sha256=2BpqLlT2ExvOOi7vnjK5nB4Fp-m52ldiPaXMox5uwug,34618 +pip/_vendor/pygments/lexers/__init__.py,sha256=j5KEi5O_VQ5GS59H49l-10gzUOkWKxlwGeVMlGO2MMk,12130 +pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc,, +pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc,, +pip/_vendor/pygments/lexers/_mapping.py,sha256=Hts4r_ZQ8icftGM7gkBPeED5lyVSv4affFgXYE6Ap04,72281 +pip/_vendor/pygments/lexers/python.py,sha256=c7jnmKFU9DLxTJW0UbwXt6Z9FJqbBlVsWA1Qr9xSA_w,53424 +pip/_vendor/pygments/modeline.py,sha256=eF2vO4LpOGoPvIKKkbPfnyut8hT4UiebZPpb-BYGQdI,986 +pip/_vendor/pygments/plugin.py,sha256=j1Fh310RbV2DQ9nvkmkqvlj38gdyuYKllLnGxbc8sJM,2591 +pip/_vendor/pygments/regexopt.py,sha256=jg1ALogcYGU96TQS9isBl6dCrvw5y5--BP_K-uFk_8s,3072 +pip/_vendor/pygments/scanner.py,sha256=b_nu5_f3HCgSdp5S_aNRBQ1MSCm4ZjDwec2OmTRickw,3092 +pip/_vendor/pygments/sphinxext.py,sha256=wBFYm180qea9JKt__UzhRlNRNhczPDFDaqGD21sbuso,6882 +pip/_vendor/pygments/style.py,sha256=C4qyoJrUTkq-OV3iO-8Vz3UtWYpJwSTdh5_vlGCGdNQ,6257 +pip/_vendor/pygments/styles/__init__.py,sha256=he7HjQx7sC0d2kfTVLjUs0J15mtToJM6M1brwIm9--Q,3700 +pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pygments/token.py,sha256=seNsmcch9OEHXYirh8Ool7w8xDhfNTbLj5rHAC-gc_o,6184 +pip/_vendor/pygments/unistring.py,sha256=FaUfG14NBJEKLQoY9qj6JYeXrpYcLmKulghdxOGFaOc,63223 +pip/_vendor/pygments/util.py,sha256=AEVY0qonyyEMgv4Do2dINrrqUAwUk2XYSqHM650uzek,10230 +pip/_vendor/pyparsing/__init__.py,sha256=9m1JbE2JTLdBG0Mb6B0lEaZj181Wx5cuPXZpsbHEYgE,9116 +pip/_vendor/pyparsing/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/actions.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/common.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/core.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/exceptions.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/helpers.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/results.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/testing.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/unicode.cpython-312.pyc,, +pip/_vendor/pyparsing/__pycache__/util.cpython-312.pyc,, +pip/_vendor/pyparsing/actions.py,sha256=05uaIPOznJPQ7VgRdmGCmG4sDnUPtwgv5qOYIqbL2UY,6567 +pip/_vendor/pyparsing/common.py,sha256=p-3c83E5-DjlkF35G0O9-kjQRpoejP-2_z0hxZ-eol4,13387 +pip/_vendor/pyparsing/core.py,sha256=yvuRlLpXSF8mgk-QhiW3OVLqD9T0rsj9tbibhRH4Yaw,224445 +pip/_vendor/pyparsing/diagram/__init__.py,sha256=nxmDOoYF9NXuLaGYy01tKFjkNReWJlrGFuJNWEiTo84,24215 +pip/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pyparsing/exceptions.py,sha256=6Jc6W1eDZBzyFu1J0YrcdNFVBC-RINujZmveSnB8Rxw,9523 +pip/_vendor/pyparsing/helpers.py,sha256=BZJHCA8SS0pYio30KGQTc9w2qMOaK4YpZ7hcvHbnTgk,38646 +pip/_vendor/pyparsing/results.py,sha256=9dyqQ-w3MjfmxWbFt8KEPU6IfXeyRdoWp2Og802rUQY,26692 +pip/_vendor/pyparsing/testing.py,sha256=eJncg0p83zm1FTPvM9auNT6oavIvXaibmRFDf1qmwkY,13488 +pip/_vendor/pyparsing/unicode.py,sha256=fAPdsJiARFbkPAih6NkYry0dpj4jPqelGVMlE4wWFW8,10646 +pip/_vendor/pyparsing/util.py,sha256=vTMzTdwSDyV8d_dSgquUTdWgBFoA_W30nfxEJDsshRQ,8670 +pip/_vendor/pyproject_hooks/__init__.py,sha256=kCehmy0UaBa9oVMD7ZIZrnswfnP3LXZ5lvnNJAL5JBM,491 +pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc,, +pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc,, +pip/_vendor/pyproject_hooks/_compat.py,sha256=by6evrYnqkisiM-MQcvOKs5bgDMzlOSgZqRHNqf04zE,138 +pip/_vendor/pyproject_hooks/_impl.py,sha256=61GJxzQip0IInhuO69ZI5GbNQ82XEDUB_1Gg5_KtUoc,11920 +pip/_vendor/pyproject_hooks/_in_process/__init__.py,sha256=9gQATptbFkelkIy0OfWFEACzqxXJMQDWCH9rBOAZVwQ,546 +pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc,, +pip/_vendor/pyproject_hooks/_in_process/_in_process.py,sha256=m2b34c917IW5o-Q_6TYIHlsK9lSUlNiyrITTUH_zwew,10927 +pip/_vendor/requests/__init__.py,sha256=owujob4dk45Siy4EYtbCKR6wcFph7E04a_v_OuAacBA,5169 +pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/api.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/auth.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/certs.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/compat.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/help.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/models.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/packages.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/structures.cpython-312.pyc,, +pip/_vendor/requests/__pycache__/utils.cpython-312.pyc,, +pip/_vendor/requests/__version__.py,sha256=ssI3Ezt7PaxgkOW45GhtwPUclo_SO_ygtIm4A74IOfw,435 +pip/_vendor/requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495 +pip/_vendor/requests/adapters.py,sha256=idj6cZcId3L5xNNeJ7ieOLtw3awJk5A64xUfetHwq3M,19697 +pip/_vendor/requests/api.py,sha256=q61xcXq4tmiImrvcSVLTbFyCiD2F-L_-hWKGbz4y8vg,6449 +pip/_vendor/requests/auth.py,sha256=h-HLlVx9j8rKV5hfSAycP2ApOSglTz77R0tz7qCbbEE,10187 +pip/_vendor/requests/certs.py,sha256=PVPooB0jP5hkZEULSCwC074532UFbR2Ptgu0I5zwmCs,575 +pip/_vendor/requests/compat.py,sha256=IhK9quyX0RRuWTNcg6d2JGSAOUbM6mym2p_2XjLTwf4,1286 +pip/_vendor/requests/cookies.py,sha256=kD3kNEcCj-mxbtf5fJsSaT86eGoEYpD3X0CSgpzl7BM,18560 +pip/_vendor/requests/exceptions.py,sha256=FA-_kVwBZ2jhXauRctN_ewHVK25b-fj0Azyz1THQ0Kk,3823 +pip/_vendor/requests/help.py,sha256=FnAAklv8MGm_qb2UilDQgS6l0cUttiCFKUjx0zn2XNA,3879 +pip/_vendor/requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 +pip/_vendor/requests/models.py,sha256=dDZ-iThotky-Noq9yy97cUEJhr3wnY6mv-xR_ePg_lk,35288 +pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695 +pip/_vendor/requests/sessions.py,sha256=-LvTzrPtetSTrR3buxu4XhdgMrJFLB1q5D7P--L2Xhw,30373 +pip/_vendor/requests/status_codes.py,sha256=FvHmT5uH-_uimtRz5hH9VCbt7VV-Nei2J9upbej6j8g,4235 +pip/_vendor/requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912 +pip/_vendor/requests/utils.py,sha256=BvQDKkLuXCSaVfSW_1blUN0IzJSfNC8njNr8vhKj76Y,33189 +pip/_vendor/resolvelib/__init__.py,sha256=h509TdEcpb5-44JonaU3ex2TM15GVBLjM9CNCPwnTTs,537 +pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc,, +pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc,, +pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc,, +pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc,, +pip/_vendor/resolvelib/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-312.pyc,, +pip/_vendor/resolvelib/compat/collections_abc.py,sha256=uy8xUZ-NDEw916tugUXm8HgwCGiMO0f-RcdnpkfXfOs,156 +pip/_vendor/resolvelib/providers.py,sha256=fuuvVrCetu5gsxPB43ERyjfO8aReS3rFQHpDgiItbs4,5871 +pip/_vendor/resolvelib/reporters.py,sha256=TSbRmWzTc26w0ggsV1bxVpeWDB8QNIre6twYl7GIZBE,1601 +pip/_vendor/resolvelib/resolvers.py,sha256=G8rsLZSq64g5VmIq-lB7UcIJ1gjAxIQJmTF4REZleQ0,20511 +pip/_vendor/resolvelib/structs.py,sha256=0_1_XO8z_CLhegP3Vpf9VJ3zJcfLm0NOHRM-i0Ykz3o,4963 +pip/_vendor/rich/__init__.py,sha256=dRxjIL-SbFVY0q3IjSMrfgBTHrm1LZDgLOygVBwiYZc,6090 +pip/_vendor/rich/__main__.py,sha256=TT8sb9PTnsnKhhrGuHkLN0jdN0dtKhtPkEr9CidDbPM,8478 +pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/abc.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/align.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/bar.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/box.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/cells.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/color.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/columns.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/console.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/containers.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/control.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/errors.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/json.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/layout.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/live.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/logging.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/markup.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/measure.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/padding.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/pager.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/palette.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/panel.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/progress.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/region.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/repr.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/rule.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/scope.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/screen.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/segment.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/status.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/style.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/styled.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/table.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/text.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/theme.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/themes.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc,, +pip/_vendor/rich/__pycache__/tree.cpython-312.pyc,, +pip/_vendor/rich/_cell_widths.py,sha256=2n4EiJi3X9sqIq0O16kUZ_zy6UYMd3xFfChlKfnW1Hc,10096 +pip/_vendor/rich/_emoji_codes.py,sha256=hu1VL9nbVdppJrVoijVshRlcRRe_v3dju3Mmd2sKZdY,140235 +pip/_vendor/rich/_emoji_replace.py,sha256=n-kcetsEUx2ZUmhQrfeMNc-teeGhpuSQ5F8VPBsyvDo,1064 +pip/_vendor/rich/_export_format.py,sha256=qxgV3nKnXQu1hfbnRVswPYy-AwIg1X0LSC47cK5s8jk,2100 +pip/_vendor/rich/_extension.py,sha256=Xt47QacCKwYruzjDi-gOBq724JReDj9Cm9xUi5fr-34,265 +pip/_vendor/rich/_fileno.py,sha256=HWZxP5C2ajMbHryvAQZseflVfQoGzsKOHzKGsLD8ynQ,799 +pip/_vendor/rich/_inspect.py,sha256=oZJGw31e64dwXSCmrDnvZbwVb1ZKhWfU8wI3VWohjJk,9695 +pip/_vendor/rich/_log_render.py,sha256=1ByI0PA1ZpxZY3CGJOK54hjlq4X-Bz_boIjIqCd8Kns,3225 +pip/_vendor/rich/_loop.py,sha256=hV_6CLdoPm0va22Wpw4zKqM0RYsz3TZxXj0PoS-9eDQ,1236 +pip/_vendor/rich/_null_file.py,sha256=tGSXk_v-IZmbj1GAzHit8A3kYIQMiCpVsCFfsC-_KJ4,1387 +pip/_vendor/rich/_palettes.py,sha256=cdev1JQKZ0JvlguV9ipHgznTdnvlIzUFDBb0It2PzjI,7063 +pip/_vendor/rich/_pick.py,sha256=evDt8QN4lF5CiwrUIXlOJCntitBCOsI3ZLPEIAVRLJU,423 +pip/_vendor/rich/_ratio.py,sha256=2lLSliL025Y-YMfdfGbutkQDevhcyDqc-DtUYW9mU70,5472 +pip/_vendor/rich/_spinners.py,sha256=U2r1_g_1zSjsjiUdAESc2iAMc3i4ri_S8PYP6kQ5z1I,19919 +pip/_vendor/rich/_stack.py,sha256=-C8OK7rxn3sIUdVwxZBBpeHhIzX0eI-VM3MemYfaXm0,351 +pip/_vendor/rich/_timer.py,sha256=zelxbT6oPFZnNrwWPpc1ktUeAT-Vc4fuFcRZLQGLtMI,417 +pip/_vendor/rich/_win32_console.py,sha256=P0vxI2fcndym1UU1S37XAzQzQnkyY7YqAKmxm24_gug,22820 +pip/_vendor/rich/_windows.py,sha256=dvNl9TmfPzNVxiKk5WDFihErZ5796g2UC9-KGGyfXmk,1926 +pip/_vendor/rich/_windows_renderer.py,sha256=t74ZL3xuDCP3nmTp9pH1L5LiI2cakJuQRQleHCJerlk,2783 +pip/_vendor/rich/_wrap.py,sha256=xfV_9t0Sg6rzimmrDru8fCVmUlalYAcHLDfrJZnbbwQ,1840 +pip/_vendor/rich/abc.py,sha256=ON-E-ZqSSheZ88VrKX2M3PXpFbGEUUZPMa_Af0l-4f0,890 +pip/_vendor/rich/align.py,sha256=Ji-Yokfkhnfe_xMmr4ISjZB07TJXggBCOYoYa-HDAr8,10368 +pip/_vendor/rich/ansi.py,sha256=iD6532QYqnBm6hADulKjrV8l8kFJ-9fEVooHJHH3hMg,6906 +pip/_vendor/rich/bar.py,sha256=a7UD303BccRCrEhGjfMElpv5RFYIinaAhAuqYqhUvmw,3264 +pip/_vendor/rich/box.py,sha256=FJ6nI3jD7h2XNFU138bJUt2HYmWOlRbltoCEuIAZhew,9842 +pip/_vendor/rich/cells.py,sha256=627ztJs9zOL-38HJ7kXBerR-gT8KBfYC8UzEwMJDYYo,4509 +pip/_vendor/rich/color.py,sha256=9Gh958U3f75WVdLTeC0U9nkGTn2n0wnojKpJ6jQEkIE,18224 +pip/_vendor/rich/color_triplet.py,sha256=3lhQkdJbvWPoLDO-AnYImAWmJvV5dlgYNCVZ97ORaN4,1054 +pip/_vendor/rich/columns.py,sha256=HUX0KcMm9dsKNi11fTbiM_h2iDtl8ySCaVcxlalEzq8,7131 +pip/_vendor/rich/console.py,sha256=pDvkbLkvtZIMIwQx_jkZ-seyNl4zGBLviXoWXte9fwg,99218 +pip/_vendor/rich/constrain.py,sha256=1VIPuC8AgtKWrcncQrjBdYqA3JVWysu6jZo1rrh7c7Q,1288 +pip/_vendor/rich/containers.py,sha256=aKgm5UDHn5Nmui6IJaKdsZhbHClh_X7D-_Wg8Ehrr7s,5497 +pip/_vendor/rich/control.py,sha256=DSkHTUQLorfSERAKE_oTAEUFefZnZp4bQb4q8rHbKws,6630 +pip/_vendor/rich/default_styles.py,sha256=-Fe318kMVI_IwciK5POpThcO0-9DYJ67TZAN6DlmlmM,8082 +pip/_vendor/rich/diagnose.py,sha256=an6uouwhKPAlvQhYpNNpGq9EJysfMIOvvCbO3oSoR24,972 +pip/_vendor/rich/emoji.py,sha256=omTF9asaAnsM4yLY94eR_9dgRRSm1lHUszX20D1yYCQ,2501 +pip/_vendor/rich/errors.py,sha256=5pP3Kc5d4QJ_c0KFsxrfyhjiPVe7J1zOqSFbFAzcV-Y,642 +pip/_vendor/rich/file_proxy.py,sha256=Tl9THMDZ-Pk5Wm8sI1gGg_U5DhusmxD-FZ0fUbcU0W0,1683 +pip/_vendor/rich/filesize.py,sha256=9fTLAPCAwHmBXdRv7KZU194jSgNrRb6Wx7RIoBgqeKY,2508 +pip/_vendor/rich/highlighter.py,sha256=p3C1g4QYzezFKdR7NF9EhPbzQDvdPUhGRgSyGGEmPko,9584 +pip/_vendor/rich/json.py,sha256=EYp9ucj-nDjYDkHCV6Mk1ve8nUOpuFLaW76X50Mis2M,5032 +pip/_vendor/rich/jupyter.py,sha256=QyoKoE_8IdCbrtiSHp9TsTSNyTHY0FO5whE7jOTd9UE,3252 +pip/_vendor/rich/layout.py,sha256=RFYL6HdCFsHf9WRpcvi3w-fpj-8O5dMZ8W96VdKNdbI,14007 +pip/_vendor/rich/live.py,sha256=vZzYvu7fqwlv3Gthl2xiw1Dc_O80VlGcCV0DOHwCyDM,14273 +pip/_vendor/rich/live_render.py,sha256=zElm3PrfSIvjOce28zETHMIUf9pFYSUA5o0AflgUP64,3667 +pip/_vendor/rich/logging.py,sha256=uB-cB-3Q4bmXDLLpbOWkmFviw-Fde39zyMV6tKJ2WHQ,11903 +pip/_vendor/rich/markup.py,sha256=xzF4uAafiEeEYDJYt_vUnJOGoTU8RrH-PH7WcWYXjCg,8198 +pip/_vendor/rich/measure.py,sha256=HmrIJX8sWRTHbgh8MxEay_83VkqNW_70s8aKP5ZcYI8,5305 +pip/_vendor/rich/padding.py,sha256=kTFGsdGe0os7tXLnHKpwTI90CXEvrceeZGCshmJy5zw,4970 +pip/_vendor/rich/pager.py,sha256=SO_ETBFKbg3n_AgOzXm41Sv36YxXAyI3_R-KOY2_uSc,828 +pip/_vendor/rich/palette.py,sha256=lInvR1ODDT2f3UZMfL1grq7dY_pDdKHw4bdUgOGaM4Y,3396 +pip/_vendor/rich/panel.py,sha256=wGMe40J8KCGgQoM0LyjRErmGIkv2bsYA71RCXThD0xE,10574 +pip/_vendor/rich/pretty.py,sha256=eLEYN9xVaMNuA6EJVYm4li7HdOHxCqmVKvnOqJpyFt0,35852 +pip/_vendor/rich/progress.py,sha256=n4KF9vky8_5iYeXcyZPEvzyLplWlDvFLkM5JI0Bs08A,59706 +pip/_vendor/rich/progress_bar.py,sha256=cEoBfkc3lLwqba4XKsUpy4vSQKDh2QQ5J2J94-ACFoo,8165 +pip/_vendor/rich/prompt.py,sha256=x0mW-pIPodJM4ry6grgmmLrl8VZp99kqcmdnBe70YYA,11303 +pip/_vendor/rich/protocol.py,sha256=5hHHDDNHckdk8iWH5zEbi-zuIVSF5hbU2jIo47R7lTE,1391 +pip/_vendor/rich/region.py,sha256=rNT9xZrVZTYIXZC0NYn41CJQwYNbR-KecPOxTgQvB8Y,166 +pip/_vendor/rich/repr.py,sha256=9Z8otOmM-tyxnyTodvXlectP60lwahjGiDTrbrxPSTg,4431 +pip/_vendor/rich/rule.py,sha256=0fNaS_aERa3UMRc3T5WMpN_sumtDxfaor2y3of1ftBk,4602 +pip/_vendor/rich/scope.py,sha256=TMUU8qo17thyqQCPqjDLYpg_UU1k5qVd-WwiJvnJVas,2843 +pip/_vendor/rich/screen.py,sha256=YoeReESUhx74grqb0mSSb9lghhysWmFHYhsbMVQjXO8,1591 +pip/_vendor/rich/segment.py,sha256=XLnJEFvcV3bjaVzMNUJiem3n8lvvI9TJ5PTu-IG2uTg,24247 +pip/_vendor/rich/spinner.py,sha256=15koCmF0DQeD8-k28Lpt6X_zJQUlzEhgo_6A6uy47lc,4339 +pip/_vendor/rich/status.py,sha256=gJsIXIZeSo3urOyxRUjs6VrhX5CZrA0NxIQ-dxhCnwo,4425 +pip/_vendor/rich/style.py,sha256=3hiocH_4N8vwRm3-8yFWzM7tSwjjEven69XqWasSQwM,27073 +pip/_vendor/rich/styled.py,sha256=eZNnzGrI4ki_54pgY3Oj0T-x3lxdXTYh4_ryDB24wBU,1258 +pip/_vendor/rich/syntax.py,sha256=jgDiVCK6cpR0NmBOpZmIu-Ud4eaW7fHvjJZkDbjpcSA,35173 +pip/_vendor/rich/table.py,sha256=-WzesL-VJKsaiDU3uyczpJMHy6VCaSewBYJwx8RudI8,39684 +pip/_vendor/rich/terminal_theme.py,sha256=1j5-ufJfnvlAo5Qsi_ACZiXDmwMXzqgmFByObT9-yJY,3370 +pip/_vendor/rich/text.py,sha256=_8JBlSau0c2z8ENOZMi1hJ7M1ZGY408E4-hXjHyyg1A,45525 +pip/_vendor/rich/theme.py,sha256=belFJogzA0W0HysQabKaHOc3RWH2ko3fQAJhoN-AFdo,3777 +pip/_vendor/rich/themes.py,sha256=0xgTLozfabebYtcJtDdC5QkX5IVUEaviqDUJJh4YVFk,102 +pip/_vendor/rich/traceback.py,sha256=yCLVrCtyoFNENd9mkm2xeG3KmqkTwH9xpFOO7p2Bq0A,29604 +pip/_vendor/rich/tree.py,sha256=BMbUYNjS9uodNPfvtY_odmU09GA5QzcMbQ5cJZhllQI,9169 +pip/_vendor/six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 +pip/_vendor/tenacity/__init__.py,sha256=3kvAL6KClq8GFo2KFhmOzskRKSDQI-ubrlfZ8AQEEI0,20493 +pip/_vendor/tenacity/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/_asyncio.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/_utils.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/after.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/before.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/before_sleep.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/nap.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/retry.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/stop.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/tornadoweb.cpython-312.pyc,, +pip/_vendor/tenacity/__pycache__/wait.cpython-312.pyc,, +pip/_vendor/tenacity/_asyncio.py,sha256=Qi6wgQsGa9MQibYRy3OXqcDQswIZZ00dLOoSUGN-6o8,3551 +pip/_vendor/tenacity/_utils.py,sha256=ubs6a7sxj3JDNRKWCyCU2j5r1CB7rgyONgZzYZq6D_4,2179 +pip/_vendor/tenacity/after.py,sha256=S5NCISScPeIrKwIeXRwdJl3kV9Q4nqZfnNPDx6Hf__g,1682 +pip/_vendor/tenacity/before.py,sha256=dIZE9gmBTffisfwNkK0F1xFwGPV41u5GK70UY4Pi5Kc,1562 +pip/_vendor/tenacity/before_sleep.py,sha256=YmpgN9Y7HGlH97U24vvq_YWb5deaK4_DbiD8ZuFmy-E,2372 +pip/_vendor/tenacity/nap.py,sha256=fRWvnz1aIzbIq9Ap3gAkAZgDH6oo5zxMrU6ZOVByq0I,1383 +pip/_vendor/tenacity/retry.py,sha256=jrzD_mxA5mSTUEdiYB7SHpxltjhPSYZSnSRATb-ggRc,8746 +pip/_vendor/tenacity/stop.py,sha256=YMJs7ZgZfND65PRLqlGB_agpfGXlemx_5Hm4PKnBqpQ,3086 +pip/_vendor/tenacity/tornadoweb.py,sha256=po29_F1Mt8qZpsFjX7EVwAT0ydC_NbVia9gVi7R_wXA,2142 +pip/_vendor/tenacity/wait.py,sha256=3FcBJoCDgym12_dN6xfK8C1gROY0Hn4NSI2u8xv50uE,8024 +pip/_vendor/tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396 +pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc,, +pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc,, +pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc,, +pip/_vendor/tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633 +pip/_vendor/tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943 +pip/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254 +pip/_vendor/truststore/__init__.py,sha256=qzTLSH8PvAkY1fr6QQ2vV-KwE_M83wdXugtpJaP_AbM,403 +pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc,, +pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc,, +pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc,, +pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc,, +pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc,, +pip/_vendor/truststore/_api.py,sha256=xjuEu_rlH4hcdJTROImEyOEqdw-F8t5vO2H2BToY0Ro,9893 +pip/_vendor/truststore/_macos.py,sha256=BjvAKoAjXhdIPuxpY124HJIFswDb0pq8DjynzJOVwqc,17694 +pip/_vendor/truststore/_openssl.py,sha256=LLUZ7ZGaio-i5dpKKjKCSeSufmn6T8pi9lDcFnvSyq0,2324 +pip/_vendor/truststore/_ssl_constants.py,sha256=NUD4fVKdSD02ri7-db0tnO0VqLP9aHuzmStcW7tAl08,1130 +pip/_vendor/truststore/_windows.py,sha256=1x_EhROeJ9QK1sMAjfnZC7awYI8UnBJYL-TjACUYI4A,17468 +pip/_vendor/typing_extensions.py,sha256=EWpcpyQnVmc48E9fSyPGs-vXgHcAk9tQABQIxmMsCGk,111130 +pip/_vendor/urllib3/__init__.py,sha256=iXLcYiJySn0GNbWOOZDDApgBL1JgP44EZ8i1760S8Mc,3333 +pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc,, +pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc,, +pip/_vendor/urllib3/_collections.py,sha256=pyASJJhW7wdOpqJj9QJA8FyGRfr8E8uUUhqUvhF0728,11372 +pip/_vendor/urllib3/_version.py,sha256=azoM7M7BUADl2kBhMVR6PPf2GhBDI90me1fcnzTwdcw,64 +pip/_vendor/urllib3/connection.py,sha256=92k9td_y4PEiTIjNufCUa1NzMB3J3w0LEdyokYgXnW8,20300 +pip/_vendor/urllib3/connectionpool.py,sha256=Be6q65SR9laoikg-h_jmc_p8OWtEmwgq_Om_Xtig-2M,40285 +pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/_appengine_environ.py,sha256=bDbyOEhW2CKLJcQqAKAyrEHN-aklsyHFKq6vF8ZFsmk,957 +pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-312.pyc,, +pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=4Xk64qIkPBt09A5q-RIFUuDhNc9mXilVapm7WnYnzRw,17632 +pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=B2JBB2_NRP02xK6DCa1Pa9IuxrPwxzDzZbixQkb7U9M,13922 +pip/_vendor/urllib3/contrib/appengine.py,sha256=VR68eAVE137lxTgjBDwCna5UiBZTOKa01Aj_-5BaCz4,11036 +pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=NlfkW7WMdW8ziqudopjHoW299og1BTWi0IeIibquFwk,4528 +pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=hDJh4MhyY_p-oKlFcYcQaVQRDv6GMmBGuW9yjxyeejM,17081 +pip/_vendor/urllib3/contrib/securetransport.py,sha256=yhZdmVjY6PI6EeFbp7qYOp6-vp1Rkv2NMuOGaEj7pmc,34448 +pip/_vendor/urllib3/contrib/socks.py,sha256=aRi9eWXo9ZEb95XUxef4Z21CFlnnjbEiAo9HOseoMt4,7097 +pip/_vendor/urllib3/exceptions.py,sha256=0Mnno3KHTNfXRfY7638NufOPkUb6mXOm-Lqj-4x2w8A,8217 +pip/_vendor/urllib3/fields.py,sha256=kvLDCg_JmH1lLjUUEY_FLS8UhY7hBvDPuVETbY8mdrM,8579 +pip/_vendor/urllib3/filepost.py,sha256=5b_qqgRHVlL7uLtdAYBzBh-GHmU5AfJVt_2N0XS3PeY,2440 +pip/_vendor/urllib3/packages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc,, +pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc,, +pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc,, +pip/_vendor/urllib3/packages/backports/makefile.py,sha256=nbzt3i0agPVP07jqqgjhaYjMmuAi_W5E0EywZivVO8E,1417 +pip/_vendor/urllib3/packages/backports/weakref_finalize.py,sha256=tRCal5OAhNSRyb0DhHp-38AtIlCsRP8BxF3NX-6rqIA,5343 +pip/_vendor/urllib3/packages/six.py,sha256=b9LM0wBXv7E7SrbCjAm4wwN-hrH-iNxv18LgWNMMKPo,34665 +pip/_vendor/urllib3/poolmanager.py,sha256=mJmZWy_Mb4-dHbmNCKbDqv3fZS9UF_2bVDuiECHyPaI,20943 +pip/_vendor/urllib3/request.py,sha256=YTWFNr7QIwh7E1W9dde9LM77v2VWTJ5V78XuTTw7D1A,6691 +pip/_vendor/urllib3/response.py,sha256=fmDJAFkG71uFTn-sVSTh2Iw0WmcXQYqkbRjihvwBjU8,30641 +pip/_vendor/urllib3/util/__init__.py,sha256=JEmSmmqqLyaw8P51gUImZh8Gwg9i1zSe-DoqAitn2nc,1155 +pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/queue.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc,, +pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc,, +pip/_vendor/urllib3/util/connection.py,sha256=5Lx2B1PW29KxBn2T0xkN1CBgRBa3gGVJBKoQoRogEVk,4901 +pip/_vendor/urllib3/util/proxy.py,sha256=zUvPPCJrp6dOF0N4GAVbOcl6o-4uXKSrGiTkkr5vUS4,1605 +pip/_vendor/urllib3/util/queue.py,sha256=nRgX8_eX-_VkvxoX096QWoz8Ps0QHUAExILCY_7PncM,498 +pip/_vendor/urllib3/util/request.py,sha256=C0OUt2tcU6LRiQJ7YYNP9GvPrSvl7ziIBekQ-5nlBZk,3997 +pip/_vendor/urllib3/util/response.py,sha256=GJpg3Egi9qaJXRwBh5wv-MNuRWan5BIu40oReoxWP28,3510 +pip/_vendor/urllib3/util/retry.py,sha256=6ENvOZ8PBDzh8kgixpql9lIrb2dxH-k7ZmBanJF2Ng4,22050 +pip/_vendor/urllib3/util/ssl_.py,sha256=X4-AqW91aYPhPx6-xbf66yHFQKbqqfC_5Zt4WkLX1Hc,17177 +pip/_vendor/urllib3/util/ssl_match_hostname.py,sha256=Ir4cZVEjmAk8gUAIHWSi7wtOO83UCYABY2xFD1Ql_WA,5758 +pip/_vendor/urllib3/util/ssltransport.py,sha256=NA-u5rMTrDFDFC8QzRKUEKMG0561hOD4qBTr3Z4pv6E,6895 +pip/_vendor/urllib3/util/timeout.py,sha256=cwq4dMk87mJHSBktK1miYJ-85G-3T3RmT20v7SFCpno,10168 +pip/_vendor/urllib3/util/url.py,sha256=lCAE7M5myA8EDdW0sJuyyZhVB9K_j38ljWhHAnFaWoE,14296 +pip/_vendor/urllib3/util/wait.py,sha256=fOX0_faozG2P7iVojQoE1mbydweNyTcm-hXEfFrTtLI,5403 +pip/_vendor/vendor.txt,sha256=4NKk7fQhVsZw0U-0zmm9Q2LgGyaPXacFbnJAaS0Q6EY,493 +pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579 +pip/_vendor/webencodings/__pycache__/__init__.cpython-312.pyc,, +pip/_vendor/webencodings/__pycache__/labels.cpython-312.pyc,, +pip/_vendor/webencodings/__pycache__/mklabels.cpython-312.pyc,, +pip/_vendor/webencodings/__pycache__/tests.cpython-312.pyc,, +pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-312.pyc,, +pip/_vendor/webencodings/labels.py,sha256=4AO_KxTddqGtrL9ns7kAPjb0CcN6xsCIxbK37HY9r3E,8979 +pip/_vendor/webencodings/mklabels.py,sha256=GYIeywnpaLnP0GSic8LFWgd0UVvO_l1Nc6YoF-87R_4,1305 +pip/_vendor/webencodings/tests.py,sha256=OtGLyjhNY1fvkW1GvLJ_FV9ZoqC9Anyjr7q3kxTbzNs,6563 +pip/_vendor/webencodings/x_user_defined.py,sha256=yOqWSdmpytGfUgh_Z6JYgDNhoc-BAHyyeeT15Fr42tM,4307 +pip/py.typed,sha256=EBVvvPRTn_eIpz5e5QztSCdrMX7Qwd7VP93RSoIlZ2I,286 diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/REQUESTED new file mode 100644 index 0000000..e69de29 diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/WHEEL new file mode 100644 index 0000000..98c0d20 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.42.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/entry_points.txt new file mode 100644 index 0000000..26fa361 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +pip = pip._internal.cli.main:main +pip3 = pip._internal.cli.main:main +pip3.12 = pip._internal.cli.main:main diff --git a/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/top_level.txt new file mode 100644 index 0000000..a1b589e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip-24.0.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.12/site-packages/pip/__init__.py b/.venv/lib/python3.12/site-packages/pip/__init__.py new file mode 100644 index 0000000..be0e3ed --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/__init__.py @@ -0,0 +1,13 @@ +from typing import List, Optional + +__version__ = "24.0" + + +def main(args: Optional[List[str]] = None) -> int: + """This is an internal API only meant for use by pip's own console scripts. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/.venv/lib/python3.12/site-packages/pip/__main__.py b/.venv/lib/python3.12/site-packages/pip/__main__.py new file mode 100644 index 0000000..5991326 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/__main__.py @@ -0,0 +1,24 @@ +import os +import sys + +# Remove '' and current working directory from the first entry +# of sys.path, if present to avoid using current directory +# in pip commands check, freeze, install, list and show, +# when invoked as python -m pip +if sys.path[0] in ("", os.getcwd()): + sys.path.pop(0) + +# If we are running from a wheel, add the wheel to sys.path +# This allows the usage python pip-*.whl/pip install pip-*.whl +if __package__ == "": + # __file__ is pip-*.whl/pip/__main__.py + # first dirname call strips of '/__main__.py', second strips off '/pip' + # Resulting path is the name of the wheel itself + # Add that to sys.path so we can import pip + path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, path) + +if __name__ == "__main__": + from pip._internal.cli.main import main as _main + + sys.exit(_main()) diff --git a/.venv/lib/python3.12/site-packages/pip/__pip-runner__.py b/.venv/lib/python3.12/site-packages/pip/__pip-runner__.py new file mode 100644 index 0000000..49a148a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pip/__pip-runner__.py @@ -0,0 +1,50 @@ +"""Execute exactly this copy of pip, within a different environment. + +This file is named as it is, to ensure that this module can't be imported via +an import statement. +""" + +# /!\ This version compatibility check section must be Python 2 compatible. /!\ + +import sys + +# Copied from setup.py +PYTHON_REQUIRES = (3, 7) + + +def version_str(version): # type: ignore + return ".".join(str(v) for v in version) + + +if sys.version_info[:2] < PYTHON_REQUIRES: + raise SystemExit( + "This version of pip does not support python {} (requires >={}).".format( + version_str(sys.version_info[:2]), version_str(PYTHON_REQUIRES) + ) + ) + +# From here on, we can use Python 3 features, but the syntax must remain +# Python 2 compatible. + +import runpy # noqa: E402 +from importlib.machinery import PathFinder # noqa: E402 +from os.path import dirname # noqa: E402 + +PIP_SOURCES_ROOT = dirname(dirname(__file__)) + + +class PipImportRedirectingFinder: + @classmethod + def find_spec(self, fullname, path=None, target=None): # type: ignore + if fullname != "pip": + return None + + spec = PathFinder.find_spec(fullname, [PIP_SOURCES_ROOT], target) + assert spec, (PIP_SOURCES_ROOT, fullname) + return spec + + +sys.meta_path.insert(0, PipImportRedirectingFinder()) + +assert __name__ == "__main__", "Cannot run __pip-runner__.py as a non-main module" +runpy.run_module("pip", run_name="__main__", alter_sys=True) 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 new file mode 100644 index 0000000000000000000000000000000000000000..0d0792d3cf01e884e8cea30ce593b15b7d670106 GIT binary patch literal 706 zcmX|9J#Q2-5Vd#rF2@}q;-d)#7#egESwo0`AQUMmLPCNN5vpred%Mn^Vb``izJ!f} zl!5|@hA!bBfcPiSK)Ve=RCGasCKcl&VdNRlSa07vd-n6>WSgMveF|ho3HjlRV{05G z2RAXgb^a zSGOJ;C$&n8&1iNmzP4OPsU1Mww8D+7@(TW2h?)0;3mC9F%>&>4C8Z=FTXc(jJVLLM zZn(MgbSQ!0N)?mJ35z}0;>rW2RZ+20q{^{1ZP^F}>sQQ3a|M{*P%P65bRigIRvHI! zyUozxG85kL-9iIhBE7iygsvX2_wP^%m9^;r(mi5Zh-&(i^l z^|}+3BVCfjrmCYdmCAOoWfL7vW7sBt+N3f6y>s^M(wl`Z(ewcx;cX^wWechW(II`i)%L iJ9Tcm^Q$>Q&%b`L8||*XJMneu@;<@(efHaLW2(Pr{J=E; literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/__pycache__/__main__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42517942cab7c1ed9fdd10f14def6f14f594272c GIT binary patch literal 862 zcmb7C&ubGw6n?Y2X?|BKwa`{P*o!$di?Nb`Vu<$8Lp4QEA(v&FohGy0U+k>WRgeaR zHhAcYVC|__CI5+Dv{jHTLco)^KyN)c(_~4Z_24_q`{vCz@B8L4U$WU0u;_gj$YT-U zH{V1N8-k-(2w(>^(2xPOK}I!%kuF8lQPyCZZ8Sju4Eorp9-;0KtO+e}XV34UCnNSK zYtUsbcp$(rO<2DM;I1%@b3EwT0rT|wAIx~96y=)m51$jK6$V%XEOdndW=@luc;fU5 zo1SE(%ycb-fXxQ!GiUFzdM27rq>aEwKFY=ZaQcG~!i1ujDzOwLheC;?+;}M1G!&^% zL&;ISW;oWsUj3OCijM7sX+?3=`nuY{iW0^(;##VSd6zX}@X{EuD0V$w;U}yz{4Iq^Qh5qEX0$xYkDGF7RmTJnl(>$vVw`JugPd!$a~ z`?l4v7w^tjC?yTc#Fi)LTi9yJ23eII-)q{|jr{e3Oo@l*hi0kFT*%Q|^NxRx)`XWN;5R!+G>SsWTZ=d`lo$`mmUU9d$SKcjuEFH`)9Hehlk`{VWX5$`j+O>7fK6n?Y2{*PmW0|}wPA}Ga`V>dtrsvxw500A`^2M)DVtrqV%S!3^-oprz= zkx~??RI1b*s)pV;R27I;>aoY3+l!4+6IMc^Y7gZW@S!cIzS;Ex5^YD?H}m$*`{vD? z@4cVn@hISU^pl`}9RT=^EB=V|7G0YFtbhhZ(1<2fNs(w`QP89%D2kL+!oI_cSrfFt zFi_!=0AP&@Km%H^5`SU$S7`ZPgu8e$AMQTE*X|-JAHIH_xN(JwA)G_ia4k}d5FTr| z8m+~OF~a?0{G)xok3SdV0-OXbvIJVx$1V2bt+sn-yeum_U)H>}Mi96EZqMK&5TF~h z*rmMyEOZ*QKHq+w?+#UV{|ioBi)sDeChm%!jX+aKC+EHyze-CDo67X6TC%H6*`Cpv zTr%rT*(}R-y`GV;==jf&Raw)^Wol8wmZ>qTTc%ONbv7D3&t1xTmC869YK>~L%4FR} zH6z<5?q&^(BC=|G@HJCw;O>%YykW~1(XCcDEgRitbv3FQJzZwDYE$ow$2{oCwD6cm z|2TxA)O$H@00}>C>Fz^;Od^$+t@w`*O<00?I1YZU{1=*nv^e>UWM4ttk4`S)86b!{ z4<97}76@mnH|B|t@g-Ry1U}m!K@+|cuaGpEOcO_Bwv`qfA^Vm?W*H}yEI+H{X3QGR zF_qO+BbT?#3N6_zS1>P{wq9bn4@_g)JpTU3dyMJRZi;i+Sxk7Ys$b02oA!)p?9Yzw z%Q4-iBXzZONu8!FhcA^=6qF-Y!!W3&DA{`RS>*6EHR#p4H8!_>mRd|V4FzM$9M3Nmfp@Q<=XR&+qk3gLo}Rs+hWgbEt>Mcs67! zhSG_>bcThw0{*c<6pOGH>;L@Fr-u;k*2(*+OedCUNtrI1;4x3k+)GEy#N}($YW^jQ_ z!G3Y+Aa*9wgcoe7^ty`g#OHZpKgXFS^P*p!3(~yA;-}$-b)J+3KfLQ?UP9Iila7cl zo))c*2<1e|cy)!RpJpWks>Jpz7wriD0@^#d~|* zVAQg$9ei(;$KAWjF20+;g`3D{;ltklG3x;S7+(lko+?k zCIe3Z#rkF-(kJncxhJodC?V)|0p#%5f?MI=+@;moJ+gea)?I?6mhPJIiaxt(9 T$u=aHH{BZk-u?v+yEpy^ int: + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d5071a96108ec9c2af3e0aa4604b3d2c6d92348 GIT binary patch literal 808 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 WSQtKi-P+0a7CN5$l7W~G`u_or2)5+_ literal 0 HcmV?d00001 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 new file mode 100644 index 0000000000000000000000000000000000000000..a620ccf0f4db5d6ecd74a0803a31eb468ed31078 GIT binary patch literal 12686 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 C0g5XC literal 0 HcmV?d00001 diff --git a/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc b/.venv/lib/python3.12/site-packages/pip/_internal/__pycache__/configuration.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b26c7fa8f9fb69a2f4c3525173775ef2fa9b2c9a GIT binary patch literal 17687 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